mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-08 05:47:05 +09:00
DROID-449 App | Tech | Add debug tree menu (#2730)
DROID-449 App | Tech | Add debug tree menu
This commit is contained in:
parent
548f3b0039
commit
e2ac0e6076
41 changed files with 598 additions and 359 deletions
|
@ -16,7 +16,7 @@ import com.anytypeio.anytype.core_models.Event
|
|||
import com.anytypeio.anytype.core_models.Payload
|
||||
import com.anytypeio.anytype.core_models.Position
|
||||
import com.anytypeio.anytype.core_ui.widgets.text.TextInputWidget
|
||||
import com.anytypeio.anytype.domain.base.Either
|
||||
import com.anytypeio.anytype.domain.base.Resultat
|
||||
import com.anytypeio.anytype.domain.block.interactor.CreateBlock
|
||||
import com.anytypeio.anytype.domain.block.interactor.UpdateTextStyle
|
||||
import com.anytypeio.anytype.features.editor.base.EditorTestSetup
|
||||
|
@ -24,7 +24,6 @@ import com.anytypeio.anytype.features.editor.base.TestEditorFragment
|
|||
import com.anytypeio.anytype.presentation.MockBlockContentFactory.StubTextContent
|
||||
import com.anytypeio.anytype.presentation.MockBlockFactory
|
||||
import com.anytypeio.anytype.presentation.editor.EditorViewModel
|
||||
import com.anytypeio.anytype.test_utils.ValueClassAnswer
|
||||
import com.anytypeio.anytype.test_utils.utils.TestUtils
|
||||
import com.anytypeio.anytype.ui.editor.EditorFragment
|
||||
import com.anytypeio.anytype.utils.CoroutinesTestRule
|
||||
|
@ -33,7 +32,6 @@ import org.junit.Before
|
|||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.kotlin.doAnswer
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.stub
|
||||
import org.mockito.kotlin.times
|
||||
|
@ -183,7 +181,7 @@ class CreateBlockTesting : EditorTestSetup() {
|
|||
|
||||
// Check results
|
||||
|
||||
verifyBlocking(createBlock, times(1)) { invoke(params) }
|
||||
verifyBlocking(createBlock, times(1)) { asFlow(params) }
|
||||
|
||||
Espresso.onView(
|
||||
TestUtils.withRecyclerView(R.id.recycler).atPositionOnView(0, targetViewId)
|
||||
|
@ -326,7 +324,7 @@ class CreateBlockTesting : EditorTestSetup() {
|
|||
createBlock.stub {
|
||||
onBlocking {
|
||||
execute(params)
|
||||
} doAnswer ValueClassAnswer(
|
||||
} doReturn Resultat.success(
|
||||
Pair(new.id, Payload(context = root, events = events))
|
||||
)
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import com.anytypeio.anytype.core_models.SmartBlockType
|
|||
import com.anytypeio.anytype.core_models.ext.content
|
||||
import com.anytypeio.anytype.core_ui.widgets.text.TextInputWidget
|
||||
import com.anytypeio.anytype.domain.base.Either
|
||||
import com.anytypeio.anytype.domain.base.Resultat
|
||||
import com.anytypeio.anytype.domain.block.interactor.CreateBlock
|
||||
import com.anytypeio.anytype.domain.block.interactor.UpdateTextStyle
|
||||
import com.anytypeio.anytype.features.editor.base.EditorTestSetup
|
||||
|
@ -25,7 +26,6 @@ import com.anytypeio.anytype.features.editor.base.TestEditorFragment
|
|||
import com.anytypeio.anytype.presentation.MockBlockContentFactory.StubTextContent
|
||||
import com.anytypeio.anytype.presentation.MockBlockFactory
|
||||
import com.anytypeio.anytype.presentation.editor.EditorViewModel
|
||||
import com.anytypeio.anytype.test_utils.ValueClassAnswer
|
||||
import com.anytypeio.anytype.test_utils.utils.TestUtils
|
||||
import com.anytypeio.anytype.ui.editor.EditorFragment
|
||||
import com.bartoszlipinski.disableanimationsrule.DisableAnimationsRule
|
||||
|
@ -367,7 +367,7 @@ class ListBlockTesting : EditorTestSetup() {
|
|||
createBlock.stub {
|
||||
onBlocking {
|
||||
execute(params)
|
||||
} doAnswer ValueClassAnswer(
|
||||
} doReturn Resultat.success(
|
||||
Pair(new.id, Payload(context = root, events = events))
|
||||
)
|
||||
}
|
||||
|
|
|
@ -12,12 +12,12 @@ import com.anytypeio.anytype.core_models.Event
|
|||
import com.anytypeio.anytype.core_models.Payload
|
||||
import com.anytypeio.anytype.domain.base.Either
|
||||
import com.anytypeio.anytype.domain.base.Result
|
||||
import com.anytypeio.anytype.domain.base.Resultat
|
||||
import com.anytypeio.anytype.features.editor.base.EditorTestSetup
|
||||
import com.anytypeio.anytype.features.editor.base.TestEditorFragment
|
||||
import com.anytypeio.anytype.presentation.MockBlockContentFactory.StubTextContent
|
||||
import com.anytypeio.anytype.presentation.editor.EditorViewModel
|
||||
import com.anytypeio.anytype.test_utils.MockDataFactory
|
||||
import com.anytypeio.anytype.test_utils.ValueClassAnswer
|
||||
import com.anytypeio.anytype.test_utils.utils.checkHasText
|
||||
import com.anytypeio.anytype.test_utils.utils.onItemView
|
||||
import com.anytypeio.anytype.test_utils.utils.rVMatcher
|
||||
|
@ -114,7 +114,7 @@ class MentionUpdateTesting : EditorTestSetup() {
|
|||
stubInterceptThreadStatus()
|
||||
stubUpdateText()
|
||||
openPage.stub {
|
||||
onBlocking { execute(any()) } doAnswer ValueClassAnswer(
|
||||
onBlocking { execute(any()) } doReturn Resultat.success(
|
||||
Result.Success(
|
||||
Payload(
|
||||
context = root,
|
||||
|
|
|
@ -20,6 +20,7 @@ import com.anytypeio.anytype.domain.`object`.UpdateDetail
|
|||
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
||||
import com.anytypeio.anytype.domain.base.Either
|
||||
import com.anytypeio.anytype.domain.base.Result
|
||||
import com.anytypeio.anytype.domain.base.Resultat
|
||||
import com.anytypeio.anytype.domain.block.UpdateDivider
|
||||
import com.anytypeio.anytype.domain.block.interactor.ClearBlockContent
|
||||
import com.anytypeio.anytype.domain.block.interactor.ClearBlockStyle
|
||||
|
@ -103,9 +104,8 @@ import com.anytypeio.anytype.presentation.editor.toggle.ToggleStateHolder
|
|||
import com.anytypeio.anytype.presentation.search.ObjectSearchConstants
|
||||
import com.anytypeio.anytype.presentation.util.CopyFileToCacheDirectory
|
||||
import com.anytypeio.anytype.presentation.util.Dispatcher
|
||||
import com.anytypeio.anytype.presentation.util.downloader.MiddlewareShareDownloader
|
||||
import com.anytypeio.anytype.presentation.util.downloader.DocumentFileShareDownloader
|
||||
import com.anytypeio.anytype.test_utils.MockDataFactory
|
||||
import com.anytypeio.anytype.test_utils.ValueClassAnswer
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||
|
@ -140,24 +140,31 @@ open class EditorTestSetup {
|
|||
lateinit var copyFileToCacheDirectory: CopyFileToCacheDirectory
|
||||
|
||||
@Mock
|
||||
lateinit var middlewareShareDownloader: MiddlewareShareDownloader
|
||||
lateinit var documentFileShareDownloader: DocumentFileShareDownloader
|
||||
|
||||
@Mock
|
||||
lateinit var openPage: OpenPage
|
||||
|
||||
@Mock
|
||||
lateinit var closePage: CloseBlock
|
||||
|
||||
@Mock
|
||||
lateinit var updateText: UpdateText
|
||||
|
||||
@Mock
|
||||
lateinit var createBlock: CreateBlock
|
||||
|
||||
@Mock
|
||||
lateinit var interceptEvents: InterceptEvents
|
||||
|
||||
@Mock
|
||||
lateinit var updateCheckbox: UpdateCheckbox
|
||||
|
||||
@Mock
|
||||
lateinit var unlinkBlocks: UnlinkBlocks
|
||||
|
||||
lateinit var getSearchObjects: SearchObjects
|
||||
|
||||
@Mock
|
||||
lateinit var duplicateBlock: DuplicateBlock
|
||||
|
||||
|
@ -341,7 +348,7 @@ open class EditorTestSetup {
|
|||
applyTemplate = applyTemplate
|
||||
)
|
||||
|
||||
featureToggles = DefaultFeatureToggles()
|
||||
featureToggles = mock<DefaultFeatureToggles>()
|
||||
|
||||
|
||||
TestEditorFragment.testViewModelFactory = EditorViewModelFactory(
|
||||
|
@ -375,7 +382,7 @@ open class EditorTestSetup {
|
|||
duplicateBlock = duplicateBlock,
|
||||
updateAlignment = updateAlignment,
|
||||
downloadFile = downloadFile,
|
||||
middlewareShareDownloader = middlewareShareDownloader,
|
||||
documentFileShareDownloader = documentFileShareDownloader,
|
||||
mergeBlocks = mergeBlocks,
|
||||
updateTextColor = updateTextColor,
|
||||
replaceBlock = replaceBlock,
|
||||
|
@ -455,7 +462,7 @@ open class EditorTestSetup {
|
|||
relations: List<Relation> = emptyList()
|
||||
) {
|
||||
openPage.stub {
|
||||
onBlocking { execute(any()) } doAnswer ValueClassAnswer(
|
||||
onBlocking { execute(any()) } doReturn Resultat.success(
|
||||
Result.Success(
|
||||
Payload(
|
||||
context = root,
|
||||
|
@ -479,7 +486,7 @@ open class EditorTestSetup {
|
|||
events: List<Event.Command>
|
||||
) {
|
||||
createBlock.stub {
|
||||
onBlocking { execute(params) } doAnswer ValueClassAnswer(
|
||||
onBlocking { execute(params) } doReturn Resultat.success(
|
||||
Pair(
|
||||
MockDataFactory.randomUuid(),
|
||||
Payload(context = root, events = events)
|
||||
|
|
|
@ -123,7 +123,8 @@ import com.anytypeio.anytype.presentation.relations.providers.ObjectValueProvide
|
|||
import com.anytypeio.anytype.presentation.util.CopyFileToCacheDirectory
|
||||
import com.anytypeio.anytype.presentation.util.DefaultCopyFileToCacheDirectory
|
||||
import com.anytypeio.anytype.presentation.util.Dispatcher
|
||||
import com.anytypeio.anytype.presentation.util.downloader.MiddlewareShareDownloader
|
||||
import com.anytypeio.anytype.presentation.util.downloader.DebugTreeShareDownloader
|
||||
import com.anytypeio.anytype.presentation.util.downloader.DocumentFileShareDownloader
|
||||
import com.anytypeio.anytype.presentation.util.downloader.UriFileProvider
|
||||
import com.anytypeio.anytype.providers.DefaultCoverImageHashProvider
|
||||
import com.anytypeio.anytype.providers.DefaultUriFileProvider
|
||||
|
@ -409,7 +410,7 @@ object EditorSessionModule {
|
|||
setRelationKey: SetRelationKey,
|
||||
analytics: Analytics,
|
||||
updateBlocksMark: UpdateBlocksMark,
|
||||
middlewareShareDownloader: MiddlewareShareDownloader,
|
||||
documentFileShareDownloader: DocumentFileShareDownloader,
|
||||
clearBlockContent: ClearBlockContent,
|
||||
clearBlockStyle: ClearBlockStyle
|
||||
): Orchestrator = Orchestrator(
|
||||
|
@ -431,7 +432,7 @@ object EditorSessionModule {
|
|||
updateDivider = updateDivider,
|
||||
memory = memory,
|
||||
downloadFile = downloadFile,
|
||||
middlewareShareDownloader = middlewareShareDownloader,
|
||||
documentFileShareDownloader = documentFileShareDownloader,
|
||||
turnIntoDocument = turnIntoDocument,
|
||||
textInteractor = Interactor.TextInteractor(
|
||||
proxies = proxer,
|
||||
|
@ -1034,17 +1035,25 @@ object EditorUseCaseModule {
|
|||
@JvmStatic
|
||||
@Provides
|
||||
@PerScreen
|
||||
fun providesMiddlewareShareDownloader(
|
||||
fun providesDocumentFileShareDownloader(
|
||||
repo: BlockRepository,
|
||||
context: Context,
|
||||
fileProvider: UriFileProvider
|
||||
): MiddlewareShareDownloader = MiddlewareShareDownloader(
|
||||
): DocumentFileShareDownloader = DocumentFileShareDownloader(
|
||||
repo = repo,
|
||||
context = context.applicationContext,
|
||||
uriFileProvider = fileProvider
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerScreen
|
||||
fun providesDebugTreeShareDownloader(
|
||||
repo: BlockRepository,
|
||||
context: Context,
|
||||
fileProvider: UriFileProvider
|
||||
): DebugTreeShareDownloader = DebugTreeShareDownloader(
|
||||
repo = repo,
|
||||
dispatchers = AppCoroutineDispatchers(
|
||||
io = Dispatchers.IO,
|
||||
computation = Dispatchers.Default,
|
||||
main = Dispatchers.Main
|
||||
),
|
||||
context = context.applicationContext,
|
||||
uriFileProvider = fileProvider
|
||||
)
|
||||
|
|
|
@ -3,6 +3,7 @@ package com.anytypeio.anytype.di.feature
|
|||
import com.anytypeio.anytype.analytics.base.Analytics
|
||||
import com.anytypeio.anytype.core_models.Payload
|
||||
import com.anytypeio.anytype.core_utils.di.scope.PerDialog
|
||||
import com.anytypeio.anytype.core_utils.tools.FeatureToggles
|
||||
import com.anytypeio.anytype.domain.`object`.DuplicateObject
|
||||
import com.anytypeio.anytype.domain.auth.repo.AuthRepository
|
||||
import com.anytypeio.anytype.domain.block.interactor.CreateBlock
|
||||
|
@ -23,6 +24,7 @@ import com.anytypeio.anytype.presentation.objects.menu.ObjectMenuViewModel
|
|||
import com.anytypeio.anytype.presentation.objects.menu.ObjectSetMenuViewModel
|
||||
import com.anytypeio.anytype.presentation.sets.ObjectSet
|
||||
import com.anytypeio.anytype.presentation.util.Dispatcher
|
||||
import com.anytypeio.anytype.presentation.util.downloader.DebugTreeShareDownloader
|
||||
import com.anytypeio.anytype.ui.editor.sheets.ObjectMenuFragment
|
||||
import com.anytypeio.anytype.ui.sets.ObjectSetMenuFragment
|
||||
import dagger.Module
|
||||
|
@ -92,6 +94,7 @@ object ObjectMenuModule {
|
|||
fun provideViewModelFactory(
|
||||
setObjectIsArchived: SetObjectIsArchived,
|
||||
duplicateObject: DuplicateObject,
|
||||
debugTreeShareDownloader: DebugTreeShareDownloader,
|
||||
addToFavorite: AddToFavorite,
|
||||
removeFromFavorite: RemoveFromFavorite,
|
||||
addBackLinkToObject: AddBackLinkToObject,
|
||||
|
@ -100,10 +103,12 @@ object ObjectMenuModule {
|
|||
analytics: Analytics,
|
||||
dispatcher: Dispatcher<Payload>,
|
||||
updateFields: UpdateFields,
|
||||
featureToggles: FeatureToggles,
|
||||
delegator: Delegator<Action>
|
||||
): ObjectMenuViewModel.Factory = ObjectMenuViewModel.Factory(
|
||||
setObjectIsArchived = setObjectIsArchived,
|
||||
duplicateObject = duplicateObject,
|
||||
debugTreeShareDownloader = debugTreeShareDownloader,
|
||||
addToFavorite = addToFavorite,
|
||||
removeFromFavorite = removeFromFavorite,
|
||||
addBackLinkToObject = addBackLinkToObject,
|
||||
|
@ -113,14 +118,15 @@ object ObjectMenuModule {
|
|||
dispatcher = dispatcher,
|
||||
updateFields = updateFields,
|
||||
delegator = delegator,
|
||||
menuOptionsProvider = createMenuOptionsProvider(storage)
|
||||
menuOptionsProvider = createMenuOptionsProvider(storage, featureToggles)
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
private fun createMenuOptionsProvider(storage: Editor.Storage) =
|
||||
private fun createMenuOptionsProvider(storage: Editor.Storage, featureToggles: FeatureToggles) =
|
||||
ObjectMenuOptionsProviderImpl(
|
||||
details = storage.details.stream().map { it.details },
|
||||
restrictions = storage.objectRestrictions.stream()
|
||||
restrictions = storage.objectRestrictions.stream(),
|
||||
featureToggles = featureToggles
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -140,6 +146,7 @@ object ObjectSetMenuModule {
|
|||
urlBuilder: UrlBuilder,
|
||||
analytics: Analytics,
|
||||
state: StateFlow<ObjectSet>,
|
||||
featureToggles: FeatureToggles,
|
||||
dispatcher: Dispatcher<Payload>
|
||||
): ObjectSetMenuViewModel.Factory = ObjectSetMenuViewModel.Factory(
|
||||
setObjectIsArchived = setObjectIsArchived,
|
||||
|
@ -152,7 +159,7 @@ object ObjectSetMenuModule {
|
|||
analytics = analytics,
|
||||
state = state,
|
||||
dispatcher = dispatcher,
|
||||
menuOptionsProvider = createMenuOptionsProvider(state)
|
||||
menuOptionsProvider = createMenuOptionsProvider(state, featureToggles)
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
|
@ -173,9 +180,13 @@ object ObjectSetMenuModule {
|
|||
)
|
||||
|
||||
@JvmStatic
|
||||
private fun createMenuOptionsProvider(state: StateFlow<ObjectSet>) =
|
||||
private fun createMenuOptionsProvider(
|
||||
state: StateFlow<ObjectSet>,
|
||||
featureToggles: FeatureToggles
|
||||
) =
|
||||
ObjectMenuOptionsProviderImpl(
|
||||
details = state.map { it.details },
|
||||
restrictions = state.map { it.objectRestrictions }
|
||||
restrictions = state.map { it.objectRestrictions },
|
||||
featureToggles = featureToggles
|
||||
)
|
||||
}
|
|
@ -5,16 +5,16 @@ import android.view.LayoutInflater
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_ui.features.objects.ObjectActionAdapter
|
||||
import com.anytypeio.anytype.core_ui.layout.SpacingItemDecoration
|
||||
import com.anytypeio.anytype.core_ui.reactive.clicks
|
||||
import com.anytypeio.anytype.core_ui.reactive.click
|
||||
import com.anytypeio.anytype.core_ui.reactive.proceed
|
||||
import com.anytypeio.anytype.core_utils.ext.arg
|
||||
import com.anytypeio.anytype.core_utils.ext.subscribe
|
||||
import com.anytypeio.anytype.core_utils.ext.shareFile
|
||||
import com.anytypeio.anytype.core_utils.ext.toast
|
||||
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetFragment
|
||||
import com.anytypeio.anytype.core_utils.ui.showActionableSnackBar
|
||||
|
@ -29,10 +29,9 @@ import com.anytypeio.anytype.ui.editor.modals.IconPickerFragmentBase
|
|||
import com.anytypeio.anytype.ui.moving.MoveToFragment
|
||||
import com.anytypeio.anytype.ui.moving.OnMoveToAction
|
||||
import com.anytypeio.anytype.ui.relations.RelationListFragment
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
|
||||
abstract class ObjectMenuBaseFragment : BaseBottomSheetFragment<FragmentObjectMenuBinding>(),
|
||||
abstract class ObjectMenuBaseFragment :
|
||||
BaseBottomSheetFragment<FragmentObjectMenuBinding>(),
|
||||
OnMoveToAction {
|
||||
|
||||
protected val ctx get() = arg<Id>(CTX_KEY)
|
||||
|
@ -53,30 +52,12 @@ abstract class ObjectMenuBaseFragment : BaseBottomSheetFragment<FragmentObjectMe
|
|||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
binding.optionHistory
|
||||
.clicks()
|
||||
.onEach { vm.onHistoryClicked() }
|
||||
.launchIn(lifecycleScope)
|
||||
|
||||
binding.optionLayout
|
||||
.clicks()
|
||||
.onEach { vm.onLayoutClicked(ctx) }
|
||||
.launchIn(lifecycleScope)
|
||||
|
||||
binding.optionIcon
|
||||
.clicks()
|
||||
.onEach { vm.onIconClicked(ctx) }
|
||||
.launchIn(lifecycleScope)
|
||||
|
||||
binding.optionRelations
|
||||
.clicks()
|
||||
.onEach { vm.onRelationsClicked() }
|
||||
.launchIn(lifecycleScope)
|
||||
|
||||
binding.optionCover
|
||||
.clicks()
|
||||
.onEach { vm.onCoverClicked(ctx) }
|
||||
.launchIn(lifecycleScope)
|
||||
click(binding.objectDiagnostics) { vm.onDiagnosticsClicked(ctx) }
|
||||
click(binding.optionHistory) { vm.onHistoryClicked() }
|
||||
click(binding.optionLayout) { vm.onLayoutClicked(ctx) }
|
||||
click(binding.optionIcon) { vm.onIconClicked(ctx) }
|
||||
click(binding.optionRelations) { vm.onRelationsClicked() }
|
||||
click(binding.optionCover) { vm.onCoverClicked(ctx) }
|
||||
|
||||
binding.rvActions.apply {
|
||||
layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
|
||||
|
@ -91,13 +72,12 @@ abstract class ObjectMenuBaseFragment : BaseBottomSheetFragment<FragmentObjectMe
|
|||
}
|
||||
|
||||
override fun onStart() {
|
||||
with(lifecycleScope) {
|
||||
jobs += subscribe(vm.actions) { actionAdapter.submitList(it) }
|
||||
jobs += subscribe(vm.toasts) { toast(it) }
|
||||
jobs += subscribe(vm.isDismissed) { isDismissed -> if (isDismissed) dismiss() }
|
||||
jobs += subscribe(vm.commands) { command -> execute(command) }
|
||||
jobs += subscribe(vm.options) { options -> renderOptions(options) }
|
||||
}
|
||||
proceed(vm.actions) { actionAdapter.submitList(it) }
|
||||
proceed(vm.toasts) { toast(it) }
|
||||
proceed(vm.isDismissed) { isDismissed -> if (isDismissed) dismiss() }
|
||||
proceed(vm.commands) { command -> execute(command) }
|
||||
proceed(vm.options) { options -> renderOptions(options) }
|
||||
|
||||
super.onStart()
|
||||
vm.onStart(
|
||||
ctx = ctx,
|
||||
|
@ -110,11 +90,13 @@ abstract class ObjectMenuBaseFragment : BaseBottomSheetFragment<FragmentObjectMe
|
|||
|
||||
// TODO refactor to recycler view
|
||||
private fun renderOptions(options: ObjectMenuOptionsProvider.Options) {
|
||||
val iconVisibility = if (options.hasIcon) View.VISIBLE else View.GONE
|
||||
val coverVisibility = if (options.hasCover) View.VISIBLE else View.GONE
|
||||
val layoutVisibility = if (options.hasLayout) View.VISIBLE else View.GONE
|
||||
val relationsVisibility = if (options.hasRelations) View.VISIBLE else View.GONE
|
||||
val historyVisibility = if (options.hasHistory) View.VISIBLE else View.GONE
|
||||
val iconVisibility = options.hasIcon.toVisibility()
|
||||
val coverVisibility = options.hasCover.toVisibility()
|
||||
val layoutVisibility = options.hasLayout.toVisibility()
|
||||
val relationsVisibility = options.hasRelations.toVisibility()
|
||||
val historyVisibility = options.hasHistory.toVisibility()
|
||||
val objectDiagnosticsVisibility = options.hasDiagnosticsVisibility.toVisibility()
|
||||
|
||||
binding.optionIcon.visibility = iconVisibility
|
||||
binding.optionCover.visibility = coverVisibility
|
||||
binding.optionLayout.visibility = layoutVisibility
|
||||
|
@ -125,86 +107,104 @@ abstract class ObjectMenuBaseFragment : BaseBottomSheetFragment<FragmentObjectMe
|
|||
binding.layoutDivider.visibility = layoutVisibility
|
||||
binding.relationsDivider.visibility = relationsVisibility
|
||||
binding.historyDivider.visibility = historyVisibility
|
||||
binding.objectDiagnostics.visibility = objectDiagnosticsVisibility
|
||||
binding.objectDiagnosticsDivider.visibility = objectDiagnosticsVisibility
|
||||
}
|
||||
|
||||
private fun execute(command: ObjectMenuViewModelBase.Command) {
|
||||
when (command) {
|
||||
ObjectMenuViewModelBase.Command.OpenObjectCover -> {
|
||||
findNavController().navigate(
|
||||
R.id.objectCoverScreen,
|
||||
bundleOf(SelectCoverObjectFragment.CTX_KEY to ctx)
|
||||
)
|
||||
}
|
||||
ObjectMenuViewModelBase.Command.OpenObjectIcons -> {
|
||||
findNavController().navigate(
|
||||
R.id.objectIconPickerScreen,
|
||||
bundleOf(
|
||||
IconPickerFragmentBase.ARG_CONTEXT_ID_KEY to ctx,
|
||||
)
|
||||
)
|
||||
}
|
||||
ObjectMenuViewModelBase.Command.OpenObjectLayout -> {
|
||||
val fr = ObjectLayoutFragment.new(ctx)
|
||||
fr.show(childFragmentManager, null)
|
||||
}
|
||||
ObjectMenuViewModelBase.Command.OpenObjectRelations -> {
|
||||
findNavController().navigate(
|
||||
R.id.objectRelationListScreen,
|
||||
bundleOf(
|
||||
RelationListFragment.ARG_CTX to ctx,
|
||||
RelationListFragment.ARG_TARGET to null,
|
||||
RelationListFragment.ARG_LOCKED to isLocked,
|
||||
RelationListFragment.ARG_MODE to RelationListFragment.MODE_LIST
|
||||
)
|
||||
)
|
||||
}
|
||||
ObjectMenuViewModelBase.Command.OpenSetCover -> {
|
||||
findNavController().navigate(
|
||||
R.id.objectSetCoverScreen,
|
||||
bundleOf(SelectCoverObjectSetFragment.CTX_KEY to ctx)
|
||||
)
|
||||
}
|
||||
ObjectMenuViewModelBase.Command.OpenSetIcons -> {
|
||||
findNavController().navigate(
|
||||
R.id.objectSetIconPickerScreen,
|
||||
bundleOf(
|
||||
IconPickerFragmentBase.ARG_CONTEXT_ID_KEY to ctx,
|
||||
)
|
||||
)
|
||||
}
|
||||
ObjectMenuViewModelBase.Command.OpenSetLayout -> {
|
||||
toast(COMING_SOON_MSG)
|
||||
}
|
||||
ObjectMenuViewModelBase.Command.OpenSetRelations -> {
|
||||
toast(COMING_SOON_MSG)
|
||||
}
|
||||
ObjectMenuViewModelBase.Command.OpenLinkToChooser -> {
|
||||
val fr = MoveToFragment.new(
|
||||
ctx = ctx,
|
||||
blocks = emptyList(),
|
||||
restorePosition = null,
|
||||
restoreBlock = null,
|
||||
title = getString(R.string.link_to)
|
||||
)
|
||||
fr.show(childFragmentManager, null)
|
||||
}
|
||||
is ObjectMenuViewModelBase.Command.OpenSnackbar -> {
|
||||
binding.root.postDelayed({
|
||||
dialog?.window
|
||||
?.decorView
|
||||
?.showActionableSnackBar(
|
||||
command.currentObjectName,
|
||||
command.targetObjectName,
|
||||
command.icon,
|
||||
binding.anchor
|
||||
) {
|
||||
vm.proceedWithOpeningPage(command.id)
|
||||
}
|
||||
}, 300L)
|
||||
}
|
||||
ObjectMenuViewModelBase.Command.OpenObjectCover -> openObjectCover()
|
||||
ObjectMenuViewModelBase.Command.OpenObjectIcons -> openObjectIcons()
|
||||
ObjectMenuViewModelBase.Command.OpenObjectLayout -> openObjectLayout()
|
||||
ObjectMenuViewModelBase.Command.OpenObjectRelations -> openObjectRelations()
|
||||
ObjectMenuViewModelBase.Command.OpenSetCover -> openSetCover()
|
||||
ObjectMenuViewModelBase.Command.OpenSetIcons -> openSetIcons()
|
||||
ObjectMenuViewModelBase.Command.OpenSetLayout -> toast(COMING_SOON_MSG)
|
||||
ObjectMenuViewModelBase.Command.OpenSetRelations -> toast(COMING_SOON_MSG)
|
||||
ObjectMenuViewModelBase.Command.OpenLinkToChooser -> openLinkChooser(command)
|
||||
is ObjectMenuViewModelBase.Command.OpenSnackbar -> openSnackbar(command)
|
||||
is ObjectMenuViewModelBase.Command.ShareDebugTree -> shareFile(command.uri)
|
||||
}
|
||||
}
|
||||
|
||||
private fun openObjectCover() {
|
||||
findNavController().navigate(
|
||||
R.id.objectCoverScreen,
|
||||
bundleOf(SelectCoverObjectFragment.CTX_KEY to ctx)
|
||||
)
|
||||
}
|
||||
|
||||
private fun openObjectIcons() {
|
||||
findNavController().navigate(
|
||||
R.id.objectIconPickerScreen,
|
||||
bundleOf(
|
||||
IconPickerFragmentBase.ARG_CONTEXT_ID_KEY to ctx,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun openObjectLayout() {
|
||||
val fr = ObjectLayoutFragment.new(ctx)
|
||||
fr.show(childFragmentManager, null)
|
||||
}
|
||||
|
||||
private fun openObjectRelations() {
|
||||
findNavController().navigate(
|
||||
R.id.objectRelationListScreen,
|
||||
bundleOf(
|
||||
RelationListFragment.ARG_CTX to ctx,
|
||||
RelationListFragment.ARG_TARGET to null,
|
||||
RelationListFragment.ARG_LOCKED to isLocked,
|
||||
RelationListFragment.ARG_MODE to RelationListFragment.MODE_LIST
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun openSetCover() {
|
||||
findNavController().navigate(
|
||||
R.id.objectSetCoverScreen,
|
||||
bundleOf(SelectCoverObjectSetFragment.CTX_KEY to ctx)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
private fun openSetIcons() {
|
||||
findNavController().navigate(
|
||||
R.id.objectSetIconPickerScreen,
|
||||
bundleOf(
|
||||
IconPickerFragmentBase.ARG_CONTEXT_ID_KEY to ctx,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
private fun openLinkChooser(command: ObjectMenuViewModelBase.Command) {
|
||||
val fr = MoveToFragment.new(
|
||||
ctx = ctx,
|
||||
blocks = emptyList(),
|
||||
restorePosition = null,
|
||||
restoreBlock = null,
|
||||
title = getString(R.string.link_to)
|
||||
)
|
||||
fr.show(childFragmentManager, null)
|
||||
}
|
||||
|
||||
private fun openSnackbar(command: ObjectMenuViewModelBase.Command.OpenSnackbar) {
|
||||
binding.root.postDelayed({
|
||||
dialog?.window
|
||||
?.decorView
|
||||
?.showActionableSnackBar(
|
||||
command.currentObjectName,
|
||||
command.targetObjectName,
|
||||
command.icon,
|
||||
binding.anchor
|
||||
) {
|
||||
vm.proceedWithOpeningPage(command.id)
|
||||
}
|
||||
}, 300L)
|
||||
}
|
||||
|
||||
|
||||
override fun onMoveTo(
|
||||
target: Id,
|
||||
blocks: List<Id>,
|
||||
|
@ -243,4 +243,6 @@ abstract class ObjectMenuBaseFragment : BaseBottomSheetFragment<FragmentObjectMe
|
|||
fun onLayoutClicked()
|
||||
fun onUndoRedoClicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Boolean.toVisibility() = if (this) View.VISIBLE else View.GONE
|
|
@ -1,8 +1,5 @@
|
|||
package com.anytypeio.anytype.ui.settings
|
||||
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
|
@ -17,8 +14,8 @@ import androidx.lifecycle.lifecycleScope
|
|||
import androidx.navigation.fragment.findNavController
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.analytics.base.EventsDictionary
|
||||
import com.anytypeio.anytype.core_utils.ext.shareFile
|
||||
import com.anytypeio.anytype.core_utils.ext.subscribe
|
||||
import com.anytypeio.anytype.core_utils.ext.toast
|
||||
import com.anytypeio.anytype.core_utils.tools.FeatureToggles
|
||||
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetComposeFragment
|
||||
import com.anytypeio.anytype.di.common.componentManager
|
||||
|
@ -27,7 +24,6 @@ import com.anytypeio.anytype.ui.dashboard.ClearCacheAlertFragment
|
|||
import com.anytypeio.anytype.ui.profile.KeychainPhraseDialog
|
||||
import com.anytypeio.anytype.ui_settings.account.AccountAndDataScreen
|
||||
import com.anytypeio.anytype.ui_settings.account.AccountAndDataViewModel
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class AccountAndDataFragment : BaseBottomSheetComposeFragment() {
|
||||
|
@ -80,24 +76,7 @@ class AccountAndDataFragment : BaseBottomSheetComposeFragment() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun shareFile(uri: Uri) {
|
||||
try {
|
||||
val shareIntent: Intent = Intent().apply {
|
||||
action = Intent.ACTION_SEND
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
putExtra(Intent.EXTRA_STREAM, uri)
|
||||
type = requireContext().contentResolver.getType(uri)
|
||||
}
|
||||
startActivity(shareIntent)
|
||||
} catch (e: Exception) {
|
||||
if (e is ActivityNotFoundException) {
|
||||
toast("No application found to open the selected file")
|
||||
} else {
|
||||
toast("Could not open file: ${e.message}")
|
||||
}
|
||||
Timber.e(e, "Error while opening file")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun proceedWithClearFileCacheWarning() {
|
||||
vm.onClearCacheButtonClicked()
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.anytypeio.anytype.ui.settings
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
|
@ -13,11 +14,13 @@ import androidx.lifecycle.lifecycleScope
|
|||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.core_utils.tools.FeatureToggles
|
||||
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetComposeFragment
|
||||
import com.anytypeio.anytype.di.common.componentManager
|
||||
import com.anytypeio.anytype.presentation.settings.MainSettingsViewModel
|
||||
import com.anytypeio.anytype.presentation.settings.MainSettingsViewModel.Event
|
||||
import com.anytypeio.anytype.presentation.settings.MainSettingsViewModel.Command
|
||||
import com.anytypeio.anytype.ui.settings.system.SettingsActivity
|
||||
import com.anytypeio.anytype.ui_settings.main.MainSettingScreen
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -28,6 +31,9 @@ class MainSettingFragment : BaseBottomSheetComposeFragment() {
|
|||
@Inject
|
||||
lateinit var factory: MainSettingsViewModel.Factory
|
||||
|
||||
@Inject
|
||||
lateinit var featureToggles: FeatureToggles
|
||||
|
||||
private val vm by viewModels<MainSettingsViewModel> { factory }
|
||||
|
||||
private val onAccountAndDataClicked = {
|
||||
|
@ -46,6 +52,10 @@ class MainSettingFragment : BaseBottomSheetComposeFragment() {
|
|||
vm.onOptionClicked(Event.OnAppearanceClicked)
|
||||
}
|
||||
|
||||
private val onDebugClicked = {
|
||||
vm.onOptionClicked(Event.OnDebugClicked)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
|
@ -59,7 +69,9 @@ class MainSettingFragment : BaseBottomSheetComposeFragment() {
|
|||
onAccountAndDataClicked = onAccountAndDataClicked,
|
||||
onAboutAppClicked = onAboutAppClicked,
|
||||
onAppearanceClicked = onAppearanceClicked,
|
||||
onPersonalizationClicked = onPersonalizationClicked
|
||||
onDebugClicked = onDebugClicked,
|
||||
onPersonalizationClicked = onPersonalizationClicked,
|
||||
showDebugMenu = featureToggles.isDebug
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -89,12 +101,16 @@ class MainSettingFragment : BaseBottomSheetComposeFragment() {
|
|||
Command.OpenPersonalizationScreen -> {
|
||||
findNavController().navigate(R.id.actionOpenPersonalizationScreen)
|
||||
}
|
||||
Command.OpenDebugScreen -> {
|
||||
startActivity(Intent(requireActivity(), SettingsActivity::class.java))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun injectDependencies() {
|
||||
componentManager().mainSettingsComponent.get().inject(this)
|
||||
}
|
||||
|
||||
override fun releaseDependencies() {
|
||||
componentManager().mainSettingsComponent.release()
|
||||
}
|
||||
|
|
|
@ -137,6 +137,29 @@
|
|||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/optionHistory" />
|
||||
|
||||
<com.anytypeio.anytype.core_ui.widgets.ObjectMenuItemWidget
|
||||
android:id="@+id/objectDiagnostics"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@drawable/default_ripple"
|
||||
app:icon="@drawable/ic_object_menu_diagnostics"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/historyDivider"
|
||||
app:subtitle="@string/object_debug"
|
||||
app:title="@string/object_diagnostics" />
|
||||
|
||||
<View
|
||||
android:id="@+id/objectDiagnosticsDivider"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0.5dp"
|
||||
android:layout_marginTop="21dp"
|
||||
android:background="@color/shape_primary"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/objectDiagnostics" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/rvContainer"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -144,7 +167,7 @@
|
|||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/historyDivider">
|
||||
app:layout_constraintTop_toBottomOf="@+id/objectDiagnosticsDivider">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:layout_gravity="center_vertical"
|
||||
|
|
|
@ -233,6 +233,8 @@ Do the computation of an expensive paragraph of text on a background thread:
|
|||
<string name="relations_description">List of related objects</string>
|
||||
<string name="history_description">All version of object</string>
|
||||
<string name="history">History</string>
|
||||
<string name="object_debug">Object Debug</string>
|
||||
<string name="object_diagnostics">Diagnostics</string>
|
||||
<string name="provide_name">Provide name for new object</string>
|
||||
<string name="wait_a_bit_message">Please give us a moment. We’re almost there…</string>
|
||||
<string name="soon">Soon</string>
|
||||
|
|
|
@ -53,6 +53,7 @@ dependencies {
|
|||
implementation applicationDependencies.kotlin
|
||||
implementation applicationDependencies.coroutinesAndroid
|
||||
implementation applicationDependencies.androidxCore
|
||||
implementation applicationDependencies.lifecycleRuntime
|
||||
|
||||
implementation applicationDependencies.design
|
||||
implementation applicationDependencies.recyclerView
|
||||
|
|
|
@ -1,16 +1,27 @@
|
|||
package com.anytypeio.anytype.core_ui.reactive
|
||||
|
||||
import android.os.Looper
|
||||
import android.os.Parcelable
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.widget.EditText
|
||||
import android.widget.TextView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.anytypeio.anytype.core_utils.ext.throttleFirst
|
||||
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetFragment
|
||||
import com.anytypeio.anytype.core_utils.ui.BaseFragment
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.flow.conflate
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
|
||||
fun View.clicks(): Flow<Unit> = callbackFlow {
|
||||
checkMainThread()
|
||||
|
@ -42,20 +53,21 @@ fun EditText.focusChanges(): Flow<Boolean> = callbackFlow {
|
|||
}.conflate()
|
||||
|
||||
|
||||
fun View.touches(handled: (MotionEvent) -> Boolean = { true }): Flow<MotionEvent> = callbackFlow<MotionEvent> {
|
||||
checkMainThread()
|
||||
val listener = View.OnTouchListener { _, event ->
|
||||
performClick()
|
||||
if (handled(event)) {
|
||||
trySend(event)
|
||||
true
|
||||
} else {
|
||||
false
|
||||
fun View.touches(handled: (MotionEvent) -> Boolean = { true }): Flow<MotionEvent> =
|
||||
callbackFlow<MotionEvent> {
|
||||
checkMainThread()
|
||||
val listener = View.OnTouchListener { _, event ->
|
||||
performClick()
|
||||
if (handled(event)) {
|
||||
trySend(event)
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
setOnTouchListener(listener)
|
||||
awaitClose { setOnTouchListener(null) }
|
||||
}.conflate()
|
||||
setOnTouchListener(listener)
|
||||
awaitClose { setOnTouchListener(null) }
|
||||
}.conflate()
|
||||
|
||||
fun EditText.afterTextChanges(): Flow<CharSequence> = callbackFlow<CharSequence> {
|
||||
checkMainThread()
|
||||
|
@ -95,4 +107,32 @@ fun TextView.editorActionEvents(
|
|||
|
||||
fun checkMainThread() = check(Looper.myLooper() == Looper.getMainLooper()) {
|
||||
"Expected to be called on the main thread but was " + Thread.currentThread().name
|
||||
}
|
||||
|
||||
fun BaseFragment<*>.click(
|
||||
view: View,
|
||||
action: () -> Unit
|
||||
) {
|
||||
jobs += view.clicks()
|
||||
.throttleFirst()
|
||||
.onEach { action() }
|
||||
.launchIn(lifecycleScope)
|
||||
}
|
||||
|
||||
fun BaseBottomSheetFragment<*>.click(
|
||||
view: View,
|
||||
action: () -> Unit
|
||||
) {
|
||||
jobs += view.clicks()
|
||||
.throttleFirst()
|
||||
.onEach { action() }
|
||||
.launchIn(lifecycleScope)
|
||||
}
|
||||
|
||||
fun <T> BaseFragment<*>.proceed(flow: Flow<T>, body: suspend (T) -> Unit) {
|
||||
jobs += flow.onEach { body(it) }.launchIn(lifecycleScope)
|
||||
}
|
||||
|
||||
fun <T> BaseBottomSheetFragment<*>.proceed(flow: Flow<T>, body: suspend (T) -> Unit) {
|
||||
jobs += flow.onEach { body(it) }.launchIn(lifecycleScope)
|
||||
}
|
16
core-ui/src/main/res/drawable/ic_object_menu_diagnostics.xml
Normal file
16
core-ui/src/main/res/drawable/ic_object_menu_diagnostics.xml
Normal file
|
@ -0,0 +1,16 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="44dp"
|
||||
android:height="44dp"
|
||||
android:viewportWidth="44"
|
||||
android:viewportHeight="44">
|
||||
<path
|
||||
android:pathData="M22,0L22,0A22,22 0,0 1,44 22L44,22A22,22 0,0 1,22 44L22,44A22,22 0,0 1,0 22L0,22A22,22 0,0 1,22 0z"
|
||||
android:strokeAlpha="0.2"
|
||||
android:fillColor="@color/turquoise"
|
||||
android:fillAlpha="0.2"/>
|
||||
<path
|
||||
android:pathData="M24.76,34C24.517,34 24.306,33.823 24.247,33.569L19.37,12.889L17.189,22.14C17.129,22.394 16.917,22.571 16.675,22.571L10.53,22.571C10.237,22.571 10,22.315 10,22C10,21.684 10.237,21.428 10.53,21.428H16.263L18.857,10.431C18.917,10.177 19.128,10 19.37,10C19.612,10 19.824,10.177 19.883,10.431L24.76,31.111L26.941,21.86C27.001,21.606 27.212,21.429 27.454,21.429H33.47C33.763,21.429 34,21.685 34,22C34,22.316 33.763,22.572 33.47,22.572L27.866,22.572L25.273,33.569C25.213,33.823 25.002,34 24.76,34H24.76Z"
|
||||
android:strokeWidth="0.6"
|
||||
android:fillColor="@color/turquoise"
|
||||
android:strokeColor="@color/turquoise"/>
|
||||
</vector>
|
|
@ -21,6 +21,8 @@
|
|||
<color name="chapter_yellow">#ECD91B</color>
|
||||
<color name="default_toolbar_option_stroke_color">#DFDDD0</color>
|
||||
|
||||
<color name="turquoise">#0FC8BA</color>
|
||||
|
||||
<color name="toolbar_block_turn_into_toggle_selected">#2C2B27</color>
|
||||
<color name="toolbar_block_turn_into_toggle_default">#ACA996</color>
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.anytypeio.anytype.core_utils.ext
|
|||
|
||||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
|
@ -353,4 +354,23 @@ fun NavController.safeNavigate(
|
|||
if (currentDestinationId == currentDestination?.id) {
|
||||
navigate(id, args)
|
||||
}
|
||||
}
|
||||
|
||||
fun Fragment.shareFile(uri: Uri) {
|
||||
try {
|
||||
val shareIntent: Intent = Intent().apply {
|
||||
action = Intent.ACTION_SEND
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
putExtra(Intent.EXTRA_STREAM, uri)
|
||||
type = requireContext().contentResolver.getType(uri)
|
||||
}
|
||||
startActivity(shareIntent)
|
||||
} catch (e: Exception) {
|
||||
if (e is ActivityNotFoundException) {
|
||||
toast("No application found to open the selected file")
|
||||
} else {
|
||||
toast("Could not open file: ${e.message}")
|
||||
}
|
||||
Timber.e(e, "Error while opening file")
|
||||
}
|
||||
}
|
|
@ -21,7 +21,7 @@ abstract class BaseBottomSheetFragment<T : ViewBinding>(
|
|||
|
||||
val sheet: FrameLayout? get() = dialog?.findViewById(BOTTOM_SHEET_ID)
|
||||
|
||||
protected val jobs = mutableListOf<Job>()
|
||||
val jobs = mutableListOf<Job>()
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
|
|
|
@ -25,7 +25,7 @@ abstract class BaseFragment<T : ViewBinding>(
|
|||
val binding: T get() = _binding!!
|
||||
val hasBinding get() = _binding != null
|
||||
|
||||
protected val jobs = mutableListOf<Job>()
|
||||
val jobs = mutableListOf<Job>()
|
||||
|
||||
abstract fun injectDependencies()
|
||||
abstract fun releaseDependencies()
|
||||
|
|
|
@ -581,7 +581,7 @@ class MiddlewareServiceImplementation @Inject constructor(
|
|||
}
|
||||
|
||||
override fun debugTree(request: Rpc.Debug.Tree.Request): Rpc.Debug.Tree.Response {
|
||||
val encoded = Service.debugSync(Rpc.Debug.Tree.Request.ADAPTER.encode(request))
|
||||
val encoded = Service.debugTree(Rpc.Debug.Tree.Request.ADAPTER.encode(request))
|
||||
val response = Rpc.Debug.Tree.Response.ADAPTER.decode(encoded)
|
||||
val error = response.error
|
||||
if (error != null && error.code != Rpc.Debug.Tree.Response.Error.Code.NULL) {
|
||||
|
|
|
@ -51,6 +51,7 @@ import com.anytypeio.anytype.presentation.extension.sendAnalyticsReorderBlockEve
|
|||
import com.anytypeio.anytype.presentation.extension.sendAnalyticsSplitBlockEvent
|
||||
import com.anytypeio.anytype.presentation.extension.sendAnalyticsUndoEvent
|
||||
import com.anytypeio.anytype.presentation.extension.sendAnalyticsUploadMediaEvent
|
||||
import com.anytypeio.anytype.presentation.util.downloader.DocumentFileShareDownloader
|
||||
import com.anytypeio.anytype.presentation.util.downloader.MiddlewareShareDownloader
|
||||
import timber.log.Timber
|
||||
|
||||
|
@ -68,7 +69,7 @@ class Orchestrator(
|
|||
private val turnIntoStyle: TurnIntoStyle,
|
||||
private val updateCheckbox: UpdateCheckbox,
|
||||
private val downloadFile: DownloadFile,
|
||||
private val middlewareShareDownloader: MiddlewareShareDownloader,
|
||||
private val documentFileShareDownloader: DocumentFileShareDownloader,
|
||||
val updateText: UpdateText,
|
||||
private val updateAlignment: UpdateAlignment,
|
||||
private val uploadBlock: UploadBlock,
|
||||
|
@ -442,7 +443,7 @@ class Orchestrator(
|
|||
)
|
||||
}
|
||||
is Intent.Media.ShareFile -> {
|
||||
middlewareShareDownloader.execute(
|
||||
documentFileShareDownloader.execute(
|
||||
params = MiddlewareShareDownloader.Params(
|
||||
hash = intent.hash,
|
||||
name = intent.name
|
||||
|
|
|
@ -10,6 +10,7 @@ interface ObjectMenuOptionsProvider {
|
|||
val hasCover: Boolean,
|
||||
val hasLayout: Boolean,
|
||||
val hasRelations: Boolean,
|
||||
val hasDiagnosticsVisibility: Boolean,
|
||||
) {
|
||||
val hasHistory: Boolean = false
|
||||
companion object {
|
||||
|
@ -18,6 +19,7 @@ interface ObjectMenuOptionsProvider {
|
|||
hasCover = true,
|
||||
hasLayout = true,
|
||||
hasRelations = true,
|
||||
hasDiagnosticsVisibility = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import com.anytypeio.anytype.core_models.Id
|
|||
import com.anytypeio.anytype.core_models.ObjectType
|
||||
import com.anytypeio.anytype.core_models.ObjectWrapper
|
||||
import com.anytypeio.anytype.core_models.restrictions.ObjectRestriction
|
||||
import com.anytypeio.anytype.core_utils.tools.FeatureToggles
|
||||
import com.anytypeio.anytype.presentation.objects.menu.ObjectMenuOptionsProvider.Options
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
|
@ -15,6 +16,7 @@ import timber.log.Timber
|
|||
class ObjectMenuOptionsProviderImpl(
|
||||
private val details: Flow<Map<Id, Block.Fields>>,
|
||||
private val restrictions: Flow<List<ObjectRestriction>>,
|
||||
private val featureToggles: FeatureToggles,
|
||||
) : ObjectMenuOptionsProvider {
|
||||
|
||||
private fun observeLayout(ctx: Id): Flow<ObjectType.Layout?> = details
|
||||
|
@ -58,12 +60,14 @@ class ObjectMenuOptionsProviderImpl(
|
|||
hasIcon = hasIcon,
|
||||
hasCover = hasCover,
|
||||
hasLayout = hasLayout,
|
||||
hasDiagnosticsVisibility = featureToggles.isDebug,
|
||||
)
|
||||
ObjectType.Layout.TODO -> Options(
|
||||
hasIcon = false,
|
||||
hasCover = hasCover,
|
||||
hasLayout = hasLayout,
|
||||
hasRelations = true,
|
||||
hasDiagnosticsVisibility = featureToggles.isDebug,
|
||||
)
|
||||
|
||||
ObjectType.Layout.NOTE -> Options(
|
||||
|
@ -71,6 +75,7 @@ class ObjectMenuOptionsProviderImpl(
|
|||
hasCover = false,
|
||||
hasLayout = hasLayout,
|
||||
hasRelations = true,
|
||||
hasDiagnosticsVisibility = featureToggles.isDebug,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
|
@ -79,6 +84,7 @@ class ObjectMenuOptionsProviderImpl(
|
|||
hasIcon = hasIcon,
|
||||
hasCover = hasCover,
|
||||
hasLayout = hasLayout,
|
||||
hasDiagnosticsVisibility = featureToggles.isDebug,
|
||||
)
|
||||
}
|
||||
return options
|
||||
|
|
|
@ -11,6 +11,7 @@ import com.anytypeio.anytype.core_models.Id
|
|||
import com.anytypeio.anytype.core_models.Payload
|
||||
import com.anytypeio.anytype.core_models.restrictions.ObjectRestriction
|
||||
import com.anytypeio.anytype.domain.`object`.DuplicateObject
|
||||
import com.anytypeio.anytype.domain.base.fold
|
||||
import com.anytypeio.anytype.domain.block.interactor.UpdateFields
|
||||
import com.anytypeio.anytype.domain.dashboard.interactor.AddToFavorite
|
||||
import com.anytypeio.anytype.domain.dashboard.interactor.RemoveFromFavorite
|
||||
|
@ -22,6 +23,8 @@ import com.anytypeio.anytype.presentation.common.Delegator
|
|||
import com.anytypeio.anytype.presentation.editor.Editor
|
||||
import com.anytypeio.anytype.presentation.objects.ObjectAction
|
||||
import com.anytypeio.anytype.presentation.util.Dispatcher
|
||||
import com.anytypeio.anytype.presentation.util.downloader.DebugTreeShareDownloader
|
||||
import com.anytypeio.anytype.presentation.util.downloader.MiddlewareShareDownloader
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
||||
|
@ -35,6 +38,7 @@ class ObjectMenuViewModel(
|
|||
dispatcher: Dispatcher<Payload>,
|
||||
menuOptionsProvider: ObjectMenuOptionsProvider,
|
||||
duplicateObject: DuplicateObject,
|
||||
private val debugTreeShareDownloader: DebugTreeShareDownloader,
|
||||
private val storage: Editor.Storage,
|
||||
private val analytics: Analytics,
|
||||
private val updateFields: UpdateFields
|
||||
|
@ -88,6 +92,30 @@ class ObjectMenuViewModel(
|
|||
add(ObjectAction.SEARCH_ON_PAGE)
|
||||
}
|
||||
|
||||
override fun onDiagnosticsClicked(ctx: Id) {
|
||||
jobs += viewModelScope.launch {
|
||||
debugTreeShareDownloader.stream(
|
||||
MiddlewareShareDownloader.Params(hash = ctx, name = "$ctx.zip")
|
||||
).collect { result ->
|
||||
result.fold(
|
||||
onSuccess = { uri ->
|
||||
commands.emit(Command.ShareDebugTree(uri))
|
||||
},
|
||||
onLoading = {
|
||||
sendToast(
|
||||
"Do not go away from this menu and don't turn the screen off. " +
|
||||
"Tree diagnostic is started to collect."
|
||||
)
|
||||
},
|
||||
onFailure = {
|
||||
sendToast("Error while collecting tree diagnostics")
|
||||
Timber.e(it, "Error while adding link from object to object")
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onIconClicked(ctx: Id) {
|
||||
viewModelScope.launch {
|
||||
if (objectRestrictions.contains(ObjectRestriction.DETAILS)) {
|
||||
|
@ -255,6 +283,7 @@ class ObjectMenuViewModel(
|
|||
class Factory(
|
||||
private val setObjectIsArchived: SetObjectIsArchived,
|
||||
private val duplicateObject: DuplicateObject,
|
||||
private val debugTreeShareDownloader: DebugTreeShareDownloader,
|
||||
private val addToFavorite: AddToFavorite,
|
||||
private val removeFromFavorite: RemoveFromFavorite,
|
||||
private val addBackLinkToObject: AddBackLinkToObject,
|
||||
|
@ -270,6 +299,7 @@ class ObjectMenuViewModel(
|
|||
return ObjectMenuViewModel(
|
||||
setObjectIsArchived = setObjectIsArchived,
|
||||
duplicateObject = duplicateObject,
|
||||
debugTreeShareDownloader = debugTreeShareDownloader,
|
||||
addToFavorite = addToFavorite,
|
||||
removeFromFavorite = removeFromFavorite,
|
||||
addBackLinkToObject = addBackLinkToObject,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.anytypeio.anytype.presentation.objects.menu
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.anytypeio.anytype.analytics.base.Analytics
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
|
@ -42,7 +43,7 @@ abstract class ObjectMenuViewModelBase(
|
|||
private val duplicateObject: DuplicateObject
|
||||
) : BaseViewModel() {
|
||||
|
||||
private val jobs = mutableListOf<Job>()
|
||||
protected val jobs = mutableListOf<Job>()
|
||||
val isDismissed = MutableStateFlow(false)
|
||||
val isObjectArchived = MutableStateFlow(false)
|
||||
val commands = MutableSharedFlow<Command>(replay = 0)
|
||||
|
@ -54,6 +55,7 @@ abstract class ObjectMenuViewModelBase(
|
|||
hasCover = false,
|
||||
hasLayout = false,
|
||||
hasRelations = false,
|
||||
hasDiagnosticsVisibility = false
|
||||
)
|
||||
)
|
||||
val options: Flow<ObjectMenuOptionsProvider.Options> = _options
|
||||
|
@ -100,7 +102,7 @@ abstract class ObjectMenuViewModelBase(
|
|||
): List<ObjectAction>
|
||||
|
||||
protected fun proceedWithRemovingFromFavorites(ctx: Id) {
|
||||
viewModelScope.launch {
|
||||
jobs += viewModelScope.launch {
|
||||
removeFromFavorite(
|
||||
RemoveFromFavorite.Params(
|
||||
target = ctx
|
||||
|
@ -119,7 +121,7 @@ abstract class ObjectMenuViewModelBase(
|
|||
}
|
||||
|
||||
protected fun proceedWithAddingToFavorites(ctx: Id) {
|
||||
viewModelScope.launch {
|
||||
jobs += viewModelScope.launch {
|
||||
addToFavorite(
|
||||
AddToFavorite.Params(
|
||||
target = ctx
|
||||
|
@ -138,7 +140,7 @@ abstract class ObjectMenuViewModelBase(
|
|||
}
|
||||
|
||||
fun proceedWithUpdatingArchivedStatus(ctx: Id, isArchived: Boolean) {
|
||||
viewModelScope.launch {
|
||||
jobs += viewModelScope.launch {
|
||||
setObjectIsArchived(
|
||||
SetObjectIsArchived.Params(
|
||||
context = ctx,
|
||||
|
@ -169,11 +171,14 @@ abstract class ObjectMenuViewModelBase(
|
|||
).fold(
|
||||
onSuccess = { obj ->
|
||||
sendAnalyticsObjectLinkToEvent(analytics)
|
||||
commands.emit(Command.OpenSnackbar(
|
||||
id = addTo,
|
||||
currentObjectName = fromName,
|
||||
targetObjectName = obj.getProperName(),
|
||||
icon = ObjectIcon.from(obj, obj.layout, urlBuilder)))
|
||||
commands.emit(
|
||||
Command.OpenSnackbar(
|
||||
id = addTo,
|
||||
currentObjectName = fromName,
|
||||
targetObjectName = obj.getProperName(),
|
||||
icon = ObjectIcon.from(obj, obj.layout, urlBuilder)
|
||||
)
|
||||
)
|
||||
},
|
||||
onFailure = {
|
||||
Timber.e(it, "Error while adding link from object to object")
|
||||
|
@ -207,6 +212,10 @@ abstract class ObjectMenuViewModelBase(
|
|||
}
|
||||
}
|
||||
|
||||
open fun onDiagnosticsClicked(ctx: Id) {
|
||||
throw IllegalStateException("You should not call diagnostics for sets")
|
||||
}
|
||||
|
||||
sealed class Command {
|
||||
object OpenObjectIcons : Command()
|
||||
object OpenSetIcons : Command()
|
||||
|
@ -217,7 +226,8 @@ abstract class ObjectMenuViewModelBase(
|
|||
object OpenObjectRelations : Command()
|
||||
object OpenSetRelations : Command()
|
||||
object OpenLinkToChooser : Command()
|
||||
class OpenSnackbar(
|
||||
data class ShareDebugTree(val uri: Uri) : Command()
|
||||
data class OpenSnackbar(
|
||||
val id: Id,
|
||||
val currentObjectName: String?,
|
||||
val targetObjectName: String?,
|
||||
|
|
|
@ -38,6 +38,7 @@ class MainSettingsViewModel(
|
|||
Event.OnAccountAndDataClicked -> commands.emit(Command.OpenAccountAndDataScreen)
|
||||
Event.OnAppearanceClicked -> commands.emit(Command.OpenAppearanceScreen)
|
||||
Event.OnPersonalizationClicked -> commands.emit(Command.OpenPersonalizationScreen)
|
||||
Event.OnDebugClicked -> commands.emit(Command.OpenDebugScreen)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,6 +68,7 @@ class MainSettingsViewModel(
|
|||
eventName = EventsDictionary.personalisationSettingsShow
|
||||
)
|
||||
}
|
||||
Event.OnDebugClicked -> {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -83,15 +85,17 @@ class MainSettingsViewModel(
|
|||
|
||||
sealed class Event {
|
||||
object OnAboutClicked : Event()
|
||||
object OnAppearanceClicked: Event()
|
||||
object OnAccountAndDataClicked: Event()
|
||||
object OnPersonalizationClicked: Event()
|
||||
object OnAppearanceClicked : Event()
|
||||
object OnAccountAndDataClicked : Event()
|
||||
object OnPersonalizationClicked : Event()
|
||||
object OnDebugClicked : Event()
|
||||
}
|
||||
|
||||
sealed class Command {
|
||||
object OpenAboutScreen : Command()
|
||||
object OpenAppearanceScreen: Command()
|
||||
object OpenAccountAndDataScreen: Command()
|
||||
object OpenPersonalizationScreen: Command()
|
||||
object OpenAppearanceScreen : Command()
|
||||
object OpenAccountAndDataScreen : Command()
|
||||
object OpenPersonalizationScreen : Command()
|
||||
object OpenDebugScreen : Command()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package com.anytypeio.anytype.presentation.util.downloader
|
||||
|
||||
import android.content.Context
|
||||
import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
||||
|
||||
class DebugTreeShareDownloader(
|
||||
private val repo: BlockRepository,
|
||||
context: Context,
|
||||
uriFileProvider: UriFileProvider
|
||||
) : MiddlewareShareDownloader(context, uriFileProvider) {
|
||||
|
||||
override suspend fun downloadFile(hash: String, path: String) =
|
||||
repo.debugTree(objectId = hash, path = path)
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package com.anytypeio.anytype.presentation.util.downloader
|
||||
|
||||
import android.content.Context
|
||||
import com.anytypeio.anytype.core_models.Command
|
||||
import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
||||
|
||||
class DocumentFileShareDownloader(
|
||||
private val repo: BlockRepository,
|
||||
context: Context,
|
||||
uriFileProvider: UriFileProvider
|
||||
) : MiddlewareShareDownloader(context, uriFileProvider) {
|
||||
|
||||
override suspend fun downloadFile(hash: String, path: String) = repo.downloadFile(
|
||||
Command.DownloadFile(hash = hash, path = path)
|
||||
)
|
||||
}
|
|
@ -2,17 +2,11 @@ package com.anytypeio.anytype.presentation.util.downloader
|
|||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import com.anytypeio.anytype.core_models.Command
|
||||
import com.anytypeio.anytype.core_models.Hash
|
||||
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
||||
import com.anytypeio.anytype.domain.base.ResultInteractor
|
||||
import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
|
||||
class MiddlewareShareDownloader(
|
||||
private val repo: BlockRepository,
|
||||
private val dispatchers: AppCoroutineDispatchers,
|
||||
abstract class MiddlewareShareDownloader(
|
||||
private val context: Context,
|
||||
private val uriFileProvider: UriFileProvider
|
||||
) : ResultInteractor<MiddlewareShareDownloader.Params, Uri>() {
|
||||
|
@ -22,7 +16,9 @@ class MiddlewareShareDownloader(
|
|||
val name: String
|
||||
)
|
||||
|
||||
override suspend fun doWork(params: Params) = withContext(dispatchers.io) {
|
||||
abstract suspend fun downloadFile(hash: String, path: String): String
|
||||
|
||||
override suspend fun doWork(params: Params): Uri {
|
||||
val cacheDir = context.cacheDir
|
||||
|
||||
require(cacheDir != null) { "Impossible to cache files!" }
|
||||
|
@ -38,17 +34,11 @@ class MiddlewareShareDownloader(
|
|||
if (tempDir.exists()) tempDir.deleteRecursively()
|
||||
tempDir.mkdirs()
|
||||
|
||||
val tempResult = File(
|
||||
repo.downloadFile(
|
||||
Command.DownloadFile(
|
||||
hash = params.hash,
|
||||
path = tempFileFolderPath
|
||||
)
|
||||
)
|
||||
)
|
||||
val tempResult = File(downloadFile(params.hash, tempFileFolderPath))
|
||||
|
||||
tempResult.renameTo(resultFile)
|
||||
}
|
||||
uriFileProvider.getUriForFile(resultFile)
|
||||
return uriFileProvider.getUriForFile(resultFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ import com.anytypeio.anytype.core_models.ext.getChildrenIdsList
|
|||
import com.anytypeio.anytype.domain.`object`.ObjectTypesProvider
|
||||
import com.anytypeio.anytype.domain.auth.interactor.GetProfile
|
||||
import com.anytypeio.anytype.domain.base.Either
|
||||
import com.anytypeio.anytype.domain.base.Resultat
|
||||
import com.anytypeio.anytype.domain.block.interactor.Move
|
||||
import com.anytypeio.anytype.domain.config.DebugSettings
|
||||
import com.anytypeio.anytype.domain.config.Gateway
|
||||
|
@ -35,7 +36,6 @@ import com.anytypeio.anytype.presentation.MockBlockFactory.link
|
|||
import com.anytypeio.anytype.presentation.navigation.AppNavigation
|
||||
import com.anytypeio.anytype.presentation.util.CoroutinesTestRule
|
||||
import com.anytypeio.anytype.test_utils.MockDataFactory
|
||||
import com.anytypeio.anytype.test_utils.ValueClassAnswer
|
||||
import com.jraska.livedata.test
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
|
@ -372,7 +372,7 @@ class HomeDashboardViewModelTest {
|
|||
|
||||
private fun givenDelegateId(id: String) {
|
||||
createNewObject.stub {
|
||||
onBlocking { execute(Unit) } doAnswer ValueClassAnswer(id)
|
||||
onBlocking { execute(Unit) } doReturn Resultat.success(id)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ import com.anytypeio.anytype.domain.`object`.ConvertObjectToSet
|
|||
import com.anytypeio.anytype.domain.`object`.UpdateDetail
|
||||
import com.anytypeio.anytype.domain.base.Either
|
||||
import com.anytypeio.anytype.domain.base.Result
|
||||
import com.anytypeio.anytype.domain.base.Resultat
|
||||
import com.anytypeio.anytype.domain.block.UpdateDivider
|
||||
import com.anytypeio.anytype.domain.block.interactor.ClearBlockContent
|
||||
import com.anytypeio.anytype.domain.block.interactor.ClearBlockStyle
|
||||
|
@ -117,9 +118,9 @@ import com.anytypeio.anytype.presentation.util.CopyFileToCacheDirectory
|
|||
import com.anytypeio.anytype.presentation.util.CoroutinesTestRule
|
||||
import com.anytypeio.anytype.presentation.util.Dispatcher
|
||||
import com.anytypeio.anytype.presentation.util.TXT
|
||||
import com.anytypeio.anytype.presentation.util.downloader.DocumentFileShareDownloader
|
||||
import com.anytypeio.anytype.presentation.util.downloader.MiddlewareShareDownloader
|
||||
import com.anytypeio.anytype.test_utils.MockDataFactory
|
||||
import com.anytypeio.anytype.test_utils.ValueClassAnswer
|
||||
import com.jraska.livedata.test
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.delay
|
||||
|
@ -233,7 +234,7 @@ open class EditorViewModelTest {
|
|||
lateinit var downloadFile: DownloadFile
|
||||
|
||||
@Mock
|
||||
lateinit var middlewareShareDownloader: MiddlewareShareDownloader
|
||||
lateinit var documentFileShareDownloader: DocumentFileShareDownloader
|
||||
|
||||
@Mock
|
||||
lateinit var uploadBlock: UploadBlock
|
||||
|
@ -2665,7 +2666,7 @@ open class EditorViewModelTest {
|
|||
vm.startSharingFile(id = file.id)
|
||||
|
||||
runTest {
|
||||
verify(middlewareShareDownloader, times(1)).execute(
|
||||
verify(documentFileShareDownloader, times(1)).execute(
|
||||
params = eq(
|
||||
MiddlewareShareDownloader.Params(
|
||||
name = file.content<Block.Content.File>().name.orEmpty(),
|
||||
|
@ -3743,7 +3744,7 @@ open class EditorViewModelTest {
|
|||
objectRestrictions: List<ObjectRestriction> = emptyList()
|
||||
) {
|
||||
openPage.stub {
|
||||
onBlocking { execute(any()) } doAnswer ValueClassAnswer(
|
||||
onBlocking { execute(any()) } doReturn Resultat.success(
|
||||
Result.Success(
|
||||
Payload(
|
||||
context = root,
|
||||
|
@ -3768,7 +3769,7 @@ open class EditorViewModelTest {
|
|||
) {
|
||||
|
||||
closePage.stub {
|
||||
onBlocking { execute(any()) } doAnswer ValueClassAnswer(Unit)
|
||||
onBlocking { execute(any()) } doReturn Resultat.success(Unit)
|
||||
}
|
||||
|
||||
exception?.let {
|
||||
|
@ -3797,7 +3798,7 @@ open class EditorViewModelTest {
|
|||
events: List<Event> = emptyList()
|
||||
) {
|
||||
openPage.stub {
|
||||
onBlocking { execute(any()) } doAnswer ValueClassAnswer(
|
||||
onBlocking { execute(any()) } doReturn Resultat.success(
|
||||
Result.Success(
|
||||
Payload(
|
||||
context = context,
|
||||
|
@ -3857,7 +3858,7 @@ open class EditorViewModelTest {
|
|||
|
||||
private fun stubCreateBlock(root: String) {
|
||||
createBlock.stub {
|
||||
onBlocking { execute(any()) } doAnswer ValueClassAnswer(
|
||||
onBlocking { execute(any()) } doReturn Resultat.success(
|
||||
Pair(
|
||||
MockDataFactory.randomString(), Payload(
|
||||
context = root,
|
||||
|
@ -3881,8 +3882,8 @@ open class EditorViewModelTest {
|
|||
}
|
||||
|
||||
private fun givenSharedFile() {
|
||||
middlewareShareDownloader.stub {
|
||||
onBlocking { execute(any()) } doAnswer ValueClassAnswer(Uri.EMPTY)
|
||||
documentFileShareDownloader.stub {
|
||||
onBlocking { execute(any()) } doReturn Resultat.success(Uri.EMPTY)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3967,7 +3968,7 @@ open class EditorViewModelTest {
|
|||
updateTextColor = updateTextColor,
|
||||
duplicateBlock = duplicateBlock,
|
||||
downloadFile = downloadFile,
|
||||
middlewareShareDownloader = middlewareShareDownloader,
|
||||
documentFileShareDownloader = documentFileShareDownloader,
|
||||
undo = undo,
|
||||
redo = redo,
|
||||
updateText = updateText,
|
||||
|
@ -4523,7 +4524,7 @@ open class EditorViewModelTest {
|
|||
|
||||
private fun givenDelegateId(id: String) {
|
||||
createNewObject.stub {
|
||||
onBlocking { execute(Unit) } doAnswer ValueClassAnswer(id)
|
||||
onBlocking { execute(Unit) } doReturn Resultat.success(id)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ import com.anytypeio.anytype.core_models.Payload
|
|||
import com.anytypeio.anytype.core_models.ext.content
|
||||
import com.anytypeio.anytype.domain.base.Either
|
||||
import com.anytypeio.anytype.domain.base.Result
|
||||
import com.anytypeio.anytype.domain.base.Resultat
|
||||
import com.anytypeio.anytype.domain.event.interactor.InterceptEvents
|
||||
import com.anytypeio.anytype.domain.icon.DocumentEmojiIconProvider
|
||||
import com.anytypeio.anytype.domain.page.CreateNewDocument
|
||||
|
@ -23,7 +24,6 @@ import com.anytypeio.anytype.presentation.editor.render.parseThemeBackgroundColo
|
|||
import com.anytypeio.anytype.presentation.util.CoroutinesTestRule
|
||||
import com.anytypeio.anytype.presentation.util.TXT
|
||||
import com.anytypeio.anytype.test_utils.MockDataFactory
|
||||
import com.anytypeio.anytype.test_utils.ValueClassAnswer
|
||||
import com.jraska.livedata.test
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import net.lachlanmckee.timberjunit.TimberTestRule
|
||||
|
@ -875,29 +875,28 @@ class EditorMentionTest : EditorPresentationTestSetup() {
|
|||
val params = InterceptEvents.Params(context = root)
|
||||
|
||||
openPage.stub {
|
||||
onBlocking { execute(any()) } doAnswer
|
||||
ValueClassAnswer(
|
||||
Result.Success(
|
||||
Payload(
|
||||
onBlocking { execute(any()) } doReturn Resultat.success(
|
||||
Result.Success(
|
||||
Payload(
|
||||
context = root,
|
||||
events = listOf(
|
||||
Event.Command.ShowObject(
|
||||
context = root,
|
||||
events = listOf(
|
||||
Event.Command.ShowObject(
|
||||
context = root,
|
||||
root = root,
|
||||
details = Block.Details(),
|
||||
relations = emptyList(),
|
||||
blocks = document,
|
||||
objectRestrictions = emptyList()
|
||||
),
|
||||
Event.Command.Details.Amend(
|
||||
context = root,
|
||||
target = mentionTarget,
|
||||
details = mapOf(Block.Fields.NAME_KEY to "Foob")
|
||||
)
|
||||
)
|
||||
root = root,
|
||||
details = Block.Details(),
|
||||
relations = emptyList(),
|
||||
blocks = document,
|
||||
objectRestrictions = emptyList()
|
||||
),
|
||||
Event.Command.Details.Amend(
|
||||
context = root,
|
||||
target = mentionTarget,
|
||||
details = mapOf(Block.Fields.NAME_KEY to "Foob")
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
stubInterceptEvents()
|
||||
stubSearchObjects()
|
||||
|
@ -1023,29 +1022,28 @@ class EditorMentionTest : EditorPresentationTestSetup() {
|
|||
val params = InterceptEvents.Params(context = root)
|
||||
|
||||
openPage.stub {
|
||||
onBlocking { execute(any()) } doAnswer
|
||||
ValueClassAnswer(
|
||||
Result.Success(
|
||||
Payload(
|
||||
onBlocking { execute(any()) } doReturn Resultat.success(
|
||||
Result.Success(
|
||||
Payload(
|
||||
context = root,
|
||||
events = listOf(
|
||||
Event.Command.ShowObject(
|
||||
context = root,
|
||||
events = listOf(
|
||||
Event.Command.ShowObject(
|
||||
context = root,
|
||||
root = root,
|
||||
details = Block.Details(),
|
||||
relations = emptyList(),
|
||||
blocks = document,
|
||||
objectRestrictions = emptyList()
|
||||
),
|
||||
Event.Command.Details.Amend(
|
||||
context = root,
|
||||
target = mentionTarget,
|
||||
details = mapOf(Block.Fields.NAME_KEY to "")
|
||||
)
|
||||
)
|
||||
root = root,
|
||||
details = Block.Details(),
|
||||
relations = emptyList(),
|
||||
blocks = document,
|
||||
objectRestrictions = emptyList()
|
||||
),
|
||||
Event.Command.Details.Amend(
|
||||
context = root,
|
||||
target = mentionTarget,
|
||||
details = mapOf(Block.Fields.NAME_KEY to "")
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
stubInterceptEvents()
|
||||
stubSearchObjects()
|
||||
|
|
|
@ -14,6 +14,7 @@ import com.anytypeio.anytype.domain.`object`.ObjectTypesProvider
|
|||
import com.anytypeio.anytype.domain.`object`.UpdateDetail
|
||||
import com.anytypeio.anytype.domain.base.Either
|
||||
import com.anytypeio.anytype.domain.base.Result
|
||||
import com.anytypeio.anytype.domain.base.Resultat
|
||||
import com.anytypeio.anytype.domain.block.UpdateDivider
|
||||
import com.anytypeio.anytype.domain.block.interactor.ClearBlockContent
|
||||
import com.anytypeio.anytype.domain.block.interactor.ClearBlockStyle
|
||||
|
@ -99,9 +100,8 @@ import com.anytypeio.anytype.presentation.editor.template.EditorTemplateDelegate
|
|||
import com.anytypeio.anytype.presentation.editor.toggle.ToggleStateHolder
|
||||
import com.anytypeio.anytype.presentation.util.CopyFileToCacheDirectory
|
||||
import com.anytypeio.anytype.presentation.util.Dispatcher
|
||||
import com.anytypeio.anytype.presentation.util.downloader.MiddlewareShareDownloader
|
||||
import com.anytypeio.anytype.presentation.util.downloader.DocumentFileShareDownloader
|
||||
import com.anytypeio.anytype.test_utils.MockDataFactory
|
||||
import com.anytypeio.anytype.test_utils.ValueClassAnswer
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
|
@ -183,7 +183,7 @@ open class EditorPresentationTestSetup {
|
|||
lateinit var downloadFile: DownloadFile
|
||||
|
||||
@Mock
|
||||
lateinit var middlewareShareDownloader: MiddlewareShareDownloader
|
||||
lateinit var documentFileShareDownloader: DocumentFileShareDownloader
|
||||
|
||||
@Mock
|
||||
lateinit var uploadBlock: UploadBlock
|
||||
|
@ -296,22 +296,31 @@ open class EditorPresentationTestSetup {
|
|||
|
||||
@Mock
|
||||
lateinit var createTableColumn: CreateTableColumn
|
||||
|
||||
@Mock
|
||||
lateinit var createTableRow: CreateTableRow
|
||||
|
||||
@Mock
|
||||
lateinit var deleteTableColumn: DeleteTableColumn
|
||||
|
||||
@Mock
|
||||
lateinit var deleteTableRow: DeleteTableRow
|
||||
|
||||
@Mock
|
||||
lateinit var duplicateTableRow: DuplicateTableRow
|
||||
|
||||
@Mock
|
||||
lateinit var duplicateTableColumn: DuplicateTableColumn
|
||||
|
||||
@Mock
|
||||
lateinit var fillTableColumn: FillTableColumn
|
||||
|
||||
@Mock
|
||||
lateinit var moveTableRow: MoveTableRow
|
||||
|
||||
@Mock
|
||||
lateinit var moveTableColumn: MoveTableColumn
|
||||
|
||||
@Mock
|
||||
lateinit var setTableRowHeader: SetTableRowHeader
|
||||
|
||||
|
@ -347,7 +356,7 @@ open class EditorPresentationTestSetup {
|
|||
updateTextColor = updateTextColor,
|
||||
duplicateBlock = duplicateBlock,
|
||||
downloadFile = downloadFile,
|
||||
middlewareShareDownloader = middlewareShareDownloader,
|
||||
documentFileShareDownloader = documentFileShareDownloader,
|
||||
undo = undo,
|
||||
redo = redo,
|
||||
updateText = updateText,
|
||||
|
@ -445,25 +454,24 @@ open class EditorPresentationTestSetup {
|
|||
relationLinks: List<RelationLink> = emptyList()
|
||||
) {
|
||||
openPage.stub {
|
||||
onBlocking { execute(any()) } doAnswer
|
||||
ValueClassAnswer(
|
||||
Result.Success(
|
||||
Payload(
|
||||
onBlocking { execute(any()) } doReturn Resultat.success(
|
||||
Result.Success(
|
||||
Payload(
|
||||
context = root,
|
||||
events = listOf(
|
||||
Event.Command.ShowObject(
|
||||
context = root,
|
||||
events = listOf(
|
||||
Event.Command.ShowObject(
|
||||
context = root,
|
||||
root = root,
|
||||
details = details,
|
||||
relations = relations,
|
||||
relationLinks = relationLinks,
|
||||
blocks = document,
|
||||
objectRestrictions = objectRestrictions
|
||||
)
|
||||
)
|
||||
root = root,
|
||||
details = details,
|
||||
relations = relations,
|
||||
relationLinks = relationLinks,
|
||||
blocks = document,
|
||||
objectRestrictions = objectRestrictions
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -547,15 +555,14 @@ open class EditorPresentationTestSetup {
|
|||
|
||||
fun stubCreateBlock(root: String) {
|
||||
createBlock.stub {
|
||||
onBlocking { execute(any()) } doAnswer
|
||||
ValueClassAnswer(
|
||||
Pair(
|
||||
MockDataFactory.randomString(), Payload(
|
||||
context = root,
|
||||
events = listOf()
|
||||
)
|
||||
)
|
||||
onBlocking { execute(any()) } doReturn Resultat.success(
|
||||
Pair(
|
||||
MockDataFactory.randomString(), Payload(
|
||||
context = root,
|
||||
events = listOf()
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -620,7 +627,7 @@ open class EditorPresentationTestSetup {
|
|||
|
||||
fun stubClosePage() {
|
||||
closePage.stub {
|
||||
onBlocking { execute(any()) } doAnswer ValueClassAnswer(Unit)
|
||||
onBlocking { execute(any()) } doReturn Resultat.success(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -671,7 +678,7 @@ open class EditorPresentationTestSetup {
|
|||
}
|
||||
}
|
||||
|
||||
fun stubSearchObjects(objects : List<ObjectWrapper.Basic> = emptyList()) {
|
||||
fun stubSearchObjects(objects: List<ObjectWrapper.Basic> = emptyList()) {
|
||||
searchObjects.stub {
|
||||
onBlocking { invoke(any()) } doReturn Either.Right(objects)
|
||||
}
|
||||
|
@ -679,7 +686,7 @@ open class EditorPresentationTestSetup {
|
|||
|
||||
fun stubGetDefaultObjectType(type: String? = null, name: String? = null) {
|
||||
getDefaultEditorType.stub {
|
||||
onBlocking { execute(Unit) } doAnswer ValueClassAnswer(
|
||||
onBlocking { execute(Unit) } doReturn Resultat.success(
|
||||
GetDefaultEditorType.Response(
|
||||
type,
|
||||
name
|
||||
|
|
|
@ -5,12 +5,12 @@ import com.anytypeio.anytype.core_models.Block
|
|||
import com.anytypeio.anytype.core_models.Payload
|
||||
import com.anytypeio.anytype.core_models.Position
|
||||
import com.anytypeio.anytype.domain.base.Either
|
||||
import com.anytypeio.anytype.domain.base.Resultat
|
||||
import com.anytypeio.anytype.domain.block.interactor.CreateBlock
|
||||
import com.anytypeio.anytype.domain.event.interactor.InterceptEvents
|
||||
import com.anytypeio.anytype.domain.page.CreateDocument
|
||||
import com.anytypeio.anytype.presentation.util.CoroutinesTestRule
|
||||
import com.anytypeio.anytype.test_utils.MockDataFactory
|
||||
import com.anytypeio.anytype.test_utils.ValueClassAnswer
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
@ -541,16 +541,15 @@ class EditorTitleAddBlockTest : EditorPresentationTestSetup() {
|
|||
params: CreateBlock.Params
|
||||
) {
|
||||
createBlock.stub {
|
||||
onBlocking { execute(params) } doAnswer
|
||||
ValueClassAnswer(
|
||||
Pair(
|
||||
MockDataFactory.randomUuid(),
|
||||
Payload(
|
||||
context = root,
|
||||
events = emptyList()
|
||||
)
|
||||
)
|
||||
onBlocking { execute(params) } doReturn Resultat.success(
|
||||
Pair(
|
||||
MockDataFactory.randomUuid(),
|
||||
Payload(
|
||||
context = root,
|
||||
events = emptyList()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import org.mockito.kotlin.mock
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
|
@ -18,7 +19,7 @@ class ObjectMenuOptionsProviderImplTest {
|
|||
private val objectId: String = "objectId"
|
||||
private val details = MutableStateFlow<Map<Id, Fields>>(mapOf())
|
||||
private val restrictions = MutableStateFlow<List<ObjectRestriction>>(emptyList())
|
||||
private val provider = ObjectMenuOptionsProviderImpl(details, restrictions)
|
||||
private val provider = ObjectMenuOptionsProviderImpl(details, restrictions, mock())
|
||||
|
||||
@Test
|
||||
fun `when layout note - options are layout, relations, history`() {
|
||||
|
@ -30,6 +31,7 @@ class ObjectMenuOptionsProviderImplTest {
|
|||
hasCover = false,
|
||||
hasLayout = true,
|
||||
hasRelations = true,
|
||||
hasDiagnosticsVisibility = false
|
||||
)
|
||||
|
||||
assertOptions(
|
||||
|
@ -47,6 +49,7 @@ class ObjectMenuOptionsProviderImplTest {
|
|||
hasCover = true,
|
||||
hasLayout = true,
|
||||
hasRelations = true,
|
||||
hasDiagnosticsVisibility = false
|
||||
)
|
||||
|
||||
assertOptions(
|
||||
|
@ -61,7 +64,7 @@ class ObjectMenuOptionsProviderImplTest {
|
|||
)
|
||||
|
||||
assertOptions(
|
||||
expected = ObjectMenuOptionsProvider.Options.ALL
|
||||
expected = ObjectMenuOptionsProvider.Options.ALL.copy(hasDiagnosticsVisibility = false)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -73,7 +76,7 @@ class ObjectMenuOptionsProviderImplTest {
|
|||
)
|
||||
|
||||
assertOptions(
|
||||
expected = ObjectMenuOptionsProvider.Options.ALL
|
||||
expected = ObjectMenuOptionsProvider.Options.ALL.copy(hasDiagnosticsVisibility = false)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -85,7 +88,10 @@ class ObjectMenuOptionsProviderImplTest {
|
|||
restrictions.value = listOf(ObjectRestriction.LAYOUT_CHANGE)
|
||||
|
||||
assertOptions(
|
||||
expected = ObjectMenuOptionsProvider.Options.ALL.copy(hasLayout = false)
|
||||
expected = ObjectMenuOptionsProvider.Options.ALL.copy(
|
||||
hasLayout = false,
|
||||
hasDiagnosticsVisibility = false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -102,6 +108,7 @@ class ObjectMenuOptionsProviderImplTest {
|
|||
hasCover = false,
|
||||
hasLayout = false,
|
||||
hasRelations = true,
|
||||
hasDiagnosticsVisibility = false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import com.anytypeio.anytype.core_models.SearchResult
|
|||
import com.anytypeio.anytype.core_models.StubRelationObject
|
||||
import com.anytypeio.anytype.core_models.StubTitle
|
||||
import com.anytypeio.anytype.core_models.ext.content
|
||||
import com.anytypeio.anytype.domain.base.Resultat
|
||||
import com.anytypeio.anytype.presentation.navigation.AppNavigation
|
||||
import com.anytypeio.anytype.presentation.objects.SupportedLayouts
|
||||
import com.anytypeio.anytype.presentation.relations.ObjectSetConfig
|
||||
|
@ -24,7 +25,6 @@ import com.anytypeio.anytype.presentation.sets.ObjectSetViewModel
|
|||
import com.anytypeio.anytype.presentation.sets.model.Viewer
|
||||
import com.anytypeio.anytype.presentation.util.CoroutinesTestRule
|
||||
import com.anytypeio.anytype.test_utils.MockDataFactory
|
||||
import com.anytypeio.anytype.test_utils.ValueClassAnswer
|
||||
import com.jraska.livedata.test
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.After
|
||||
|
@ -33,6 +33,7 @@ import org.junit.Rule
|
|||
import org.junit.Test
|
||||
import org.mockito.MockitoAnnotations
|
||||
import org.mockito.kotlin.doAnswer
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.stub
|
||||
import org.mockito.kotlin.times
|
||||
import org.mockito.kotlin.verifyBlocking
|
||||
|
@ -596,7 +597,7 @@ class ObjectSetNavigationTest : ObjectSetViewModelTestSetup() {
|
|||
|
||||
private fun givenDelegateId(id: String) {
|
||||
createNewObject.stub {
|
||||
onBlocking { execute(Unit) } doAnswer ValueClassAnswer(id)
|
||||
onBlocking { execute(Unit) } doReturn Resultat.success(id)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ import com.anytypeio.anytype.domain.`object`.UpdateDetail
|
|||
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
||||
import com.anytypeio.anytype.domain.base.Either
|
||||
import com.anytypeio.anytype.domain.base.Result
|
||||
import com.anytypeio.anytype.domain.base.Resultat
|
||||
import com.anytypeio.anytype.domain.block.interactor.UpdateText
|
||||
import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
||||
import com.anytypeio.anytype.domain.config.Gateway
|
||||
|
@ -52,7 +53,6 @@ import com.anytypeio.anytype.presentation.sets.ObjectSetSession
|
|||
import com.anytypeio.anytype.presentation.sets.ObjectSetViewModel
|
||||
import com.anytypeio.anytype.presentation.util.Dispatcher
|
||||
import com.anytypeio.anytype.test_utils.MockDataFactory
|
||||
import com.anytypeio.anytype.test_utils.ValueClassAnswer
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
|
@ -244,7 +244,7 @@ open class ObjectSetViewModelTestSetup {
|
|||
|
||||
fun stubCloseBlock() {
|
||||
closeBlock.stub {
|
||||
onBlocking { execute(any()) } doAnswer ValueClassAnswer(Unit)
|
||||
onBlocking { execute(any()) } doReturn Resultat.success(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import com.anytypeio.anytype.domain.auth.interactor.LaunchWallet
|
|||
import com.anytypeio.anytype.domain.auth.model.AuthStatus
|
||||
import com.anytypeio.anytype.domain.auth.repo.AuthRepository
|
||||
import com.anytypeio.anytype.domain.base.Either
|
||||
import com.anytypeio.anytype.domain.base.Resultat
|
||||
import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
||||
import com.anytypeio.anytype.domain.launch.GetDefaultEditorType
|
||||
import com.anytypeio.anytype.domain.launch.SetDefaultEditorType
|
||||
|
@ -19,7 +20,6 @@ import com.anytypeio.anytype.domain.search.ObjectTypesSubscriptionManager
|
|||
import com.anytypeio.anytype.domain.search.RelationsSubscriptionManager
|
||||
import com.anytypeio.anytype.presentation.util.CoroutinesTestRule
|
||||
import com.anytypeio.anytype.test_utils.MockDataFactory
|
||||
import com.anytypeio.anytype.test_utils.ValueClassAnswer
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Before
|
||||
|
@ -31,6 +31,7 @@ import org.mockito.MockitoAnnotations
|
|||
import org.mockito.kotlin.any
|
||||
import org.mockito.kotlin.doAnswer
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.doThrow
|
||||
import org.mockito.kotlin.eq
|
||||
import org.mockito.kotlin.stub
|
||||
import org.mockito.kotlin.times
|
||||
|
@ -143,7 +144,7 @@ class SplashViewModelTest {
|
|||
stubLaunchAccount()
|
||||
stubGetLastOpenedObject()
|
||||
getDefaultEditorType.stub {
|
||||
onBlocking { execute(Unit) } doAnswer ValueClassAnswer (Exception("error"))
|
||||
onBlocking { execute(Unit) } doThrow Exception("error")
|
||||
}
|
||||
|
||||
initViewModel()
|
||||
|
@ -231,35 +232,37 @@ class SplashViewModelTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `should fallback to default object type if default object type contains deprecated prefix id`() = runTest {
|
||||
stubCheckAuthStatus(Either.Right(AuthStatus.AUTHORIZED))
|
||||
stubLaunchWallet()
|
||||
stubLaunchAccount()
|
||||
stubGetLastOpenedObject()
|
||||
stubGetDefaultObjectType(type = ObjectTypeIds.MARKETPLACE_OBJECT_TYPE_PREFIX + MockDataFactory.randomUuid())
|
||||
fun `should fallback to default object type if default object type contains deprecated prefix id`() =
|
||||
runTest {
|
||||
stubCheckAuthStatus(Either.Right(AuthStatus.AUTHORIZED))
|
||||
stubLaunchWallet()
|
||||
stubLaunchAccount()
|
||||
stubGetLastOpenedObject()
|
||||
stubGetDefaultObjectType(type = ObjectTypeIds.MARKETPLACE_OBJECT_TYPE_PREFIX + MockDataFactory.randomUuid())
|
||||
|
||||
initViewModel()
|
||||
initViewModel()
|
||||
|
||||
verify(setDefaultEditorType, times(1)).invoke(
|
||||
SetDefaultEditorType.Params(
|
||||
SplashViewModel.DEFAULT_TYPE_FIRST_INSTALL.first,
|
||||
SplashViewModel.DEFAULT_TYPE_FIRST_INSTALL.second
|
||||
verify(setDefaultEditorType, times(1)).invoke(
|
||||
SetDefaultEditorType.Params(
|
||||
SplashViewModel.DEFAULT_TYPE_FIRST_INSTALL.first,
|
||||
SplashViewModel.DEFAULT_TYPE_FIRST_INSTALL.second
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should not fallback to default object type if default object type does not contain deprecated prefix id`() = runTest {
|
||||
stubCheckAuthStatus(Either.Right(AuthStatus.AUTHORIZED))
|
||||
stubLaunchWallet()
|
||||
stubLaunchAccount()
|
||||
stubGetLastOpenedObject()
|
||||
stubGetDefaultObjectType(type = ObjectTypeIds.DEFAULT_OBJECT_TYPE_PREFIX + MockDataFactory.randomUuid())
|
||||
fun `should not fallback to default object type if default object type does not contain deprecated prefix id`() =
|
||||
runTest {
|
||||
stubCheckAuthStatus(Either.Right(AuthStatus.AUTHORIZED))
|
||||
stubLaunchWallet()
|
||||
stubLaunchAccount()
|
||||
stubGetLastOpenedObject()
|
||||
stubGetDefaultObjectType(type = ObjectTypeIds.DEFAULT_OBJECT_TYPE_PREFIX + MockDataFactory.randomUuid())
|
||||
|
||||
initViewModel()
|
||||
initViewModel()
|
||||
|
||||
verifyNoInteractions(setDefaultEditorType)
|
||||
}
|
||||
verifyNoInteractions(setDefaultEditorType)
|
||||
}
|
||||
|
||||
//Todo can't mock Amplitude
|
||||
// @Test
|
||||
|
@ -361,7 +364,7 @@ class SplashViewModelTest {
|
|||
|
||||
private fun stubGetDefaultObjectType(type: String? = null, name: String? = null) {
|
||||
getDefaultEditorType.stub {
|
||||
onBlocking { execute(Unit) } doAnswer ValueClassAnswer(
|
||||
onBlocking { execute(Unit) } doReturn Resultat.success(
|
||||
GetDefaultEditorType.Response(
|
||||
type,
|
||||
name
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
package com.anytypeio.anytype.test_utils
|
||||
|
||||
import org.mockito.invocation.InvocationOnMock
|
||||
import org.mockito.stubbing.Answer
|
||||
|
||||
class ValueClassAnswer(private val value: Any) : Answer<Any> {
|
||||
override fun answer(invocation: InvocationOnMock?): Any = value
|
||||
}
|
|
@ -18,8 +18,10 @@ import com.anytypeio.anytype.ui_settings.R
|
|||
fun MainSettingScreen(
|
||||
onAccountAndDataClicked: () -> Unit,
|
||||
onAboutAppClicked: () -> Unit,
|
||||
onDebugClicked: () -> Unit,
|
||||
onPersonalizationClicked: () -> Unit,
|
||||
onAppearanceClicked: () -> Unit
|
||||
onAppearanceClicked: () -> Unit,
|
||||
showDebugMenu: Boolean
|
||||
) {
|
||||
Column {
|
||||
Box(
|
||||
|
@ -51,6 +53,14 @@ fun MainSettingScreen(
|
|||
onClick = onAboutAppClicked
|
||||
)
|
||||
Divider()
|
||||
if (showDebugMenu) {
|
||||
Option(
|
||||
image = R.drawable.ic_debug,
|
||||
text = stringResource(R.string.debug),
|
||||
onClick = onDebugClicked
|
||||
)
|
||||
Divider()
|
||||
}
|
||||
Box(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
}
|
21
ui-settings/src/main/res/drawable/ic_debug.xml
Normal file
21
ui-settings/src/main/res/drawable/ic_debug.xml
Normal file
|
@ -0,0 +1,21 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="28dp"
|
||||
android:height="28dp"
|
||||
android:viewportWidth="28"
|
||||
android:viewportHeight="28">
|
||||
<path
|
||||
android:fillColor="@color/chapter_yellow"
|
||||
android:pathData="M6,0L22,0A6,6 0,0 1,28 6L28,22A6,6 0,0 1,22 28L6,28A6,6 0,0 1,0 22L0,6A6,6 0,0 1,6 0z" />
|
||||
<path
|
||||
android:fillColor="@color/white"
|
||||
android:pathData="M14,4C13,10 10,13 4,14H14V4Z" />
|
||||
<path
|
||||
android:fillColor="@color/white"
|
||||
android:pathData="M14,24C13,18 10,15 4,14H14V24Z" />
|
||||
<path
|
||||
android:fillColor="@color/white"
|
||||
android:pathData="M14,4C15,10 18,13 24,14H14V4Z" />
|
||||
<path
|
||||
android:fillColor="@color/white"
|
||||
android:pathData="M14,24C15,18 18,15 24,14H14V24Z" />
|
||||
</vector>
|
|
@ -11,6 +11,7 @@
|
|||
<string name="recovery_phrase">Recovery phrase</string>
|
||||
<string name="clear_file_cache">Clear file cache</string>
|
||||
<string name="debug_sync_report">Debug Sync Report</string>
|
||||
<string name="debug">Debug</string>
|
||||
<string name="account">Account</string>
|
||||
<string name="reset_account">Reset account</string>
|
||||
<string name="delete_account">Delete account</string>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue