1
0
Fork 0
mirror of https://github.com/anyproto/anytype-kotlin.git synced 2025-06-08 05:47:05 +09:00

DROID-99 Editor | Navigation | Fix double clicks / crashes (#2757)

This commit is contained in:
Mikhail 2022-12-12 15:05:56 +03:00 committed by GitHub
parent a71e8ec55c
commit b11fabeb11
Signed by: github
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 184 additions and 126 deletions

View file

@ -6,6 +6,7 @@ import androidx.viewbinding.ViewBinding
import com.anytypeio.anytype.BuildConfig
import com.anytypeio.anytype.core_utils.common.EventWrapper
import com.anytypeio.anytype.core_utils.ui.BaseFragment
import com.anytypeio.anytype.core_utils.ui.getNavigationId
import com.anytypeio.anytype.presentation.navigation.AppNavigation
import com.anytypeio.anytype.presentation.navigation.AppNavigation.Command
import timber.log.Timber
@ -14,10 +15,14 @@ abstract class NavigationFragment<BINDING : ViewBinding>(
@LayoutRes private val layout: Int
) : BaseFragment<BINDING>(layout) {
private val currentNavigationId by lazy { getNavigationId() }
val navObserver = Observer<EventWrapper<Command>> { event ->
event.getContentIfNotHandled()?.let {
try {
navigate(it)
if (currentNavigationId == getNavigationId()) {
throttle { navigate(it) }
}
} catch (e: Exception) {
Timber.e(e, "Navigation: $it")
if (BuildConfig.DEBUG) {
@ -51,7 +56,10 @@ abstract class NavigationFragment<BINDING : ViewBinding>(
is Command.OpenSettings -> navigation.openSettings()
is Command.OpenObject -> navigation.openDocument(command.id, command.editorSettings)
is Command.OpenArchive -> navigation.openArchive(command.target)
is Command.OpenObjectSet -> navigation.openObjectSet(command.target, command.isPopUpToDashboard)
is Command.OpenObjectSet -> navigation.openObjectSet(
command.target,
command.isPopUpToDashboard
)
is Command.LaunchObjectSet -> navigation.launchObjectSet(command.target)
is Command.LaunchDocument -> navigation.launchDocument(command.id)
is Command.LaunchObjectFromSplash -> navigation.launchObjectFromSplash(command.target)
@ -79,4 +87,4 @@ abstract class NavigationFragment<BINDING : ViewBinding>(
else -> Timber.d("Nav command ignored: $command")
}
}
}
}

View file

@ -17,12 +17,11 @@ import androidx.transition.TransitionSet
import androidx.viewpager2.widget.ViewPager2
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_models.ObjectWrapper
import com.anytypeio.anytype.core_ui.reactive.clicks
import com.anytypeio.anytype.core_ui.reactive.click
import com.anytypeio.anytype.core_utils.ext.argOrNull
import com.anytypeio.anytype.core_utils.ext.cancel
import com.anytypeio.anytype.core_utils.ext.gone
import com.anytypeio.anytype.core_utils.ext.invisible
import com.anytypeio.anytype.core_utils.ext.throttleFirst
import com.anytypeio.anytype.core_utils.ext.toast
import com.anytypeio.anytype.core_utils.ext.visible
import com.anytypeio.anytype.core_utils.ui.ViewState
@ -38,8 +37,6 @@ import com.anytypeio.anytype.ui.base.ViewStateFragment
import com.anytypeio.anytype.ui.editor.EditorFragment
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
@ -442,51 +439,15 @@ class DashboardFragment :
})
}
binding.btnAddDoc
.clicks()
.onEach { vm.onAddNewDocumentClicked() }
.launchIn(lifecycleScope)
binding.btnSearch
.clicks()
.onEach { vm.onPageSearchClicked() }
.launchIn(lifecycleScope)
binding.btnMarketplace
.clicks()
.onEach { toast(getString(R.string.coming_soon)) }
.launchIn(lifecycleScope)
binding.ivSettings
.clicks()
.throttleFirst()
.onEach { vm.onSettingsClicked() }
.launchIn(lifecycleScope)
binding.avatarContainer
.clicks()
.onEach { vm.onAvatarClicked() }
.launchIn(lifecycleScope)
binding.tvCancel
.clicks()
.onEach { vm.onCancelSelectionClicked() }
.launchIn(lifecycleScope)
binding.tvSelectAll
.clicks()
.onEach { vm.onSelectAllClicked() }
.launchIn(lifecycleScope)
binding.tvRestore
.clicks()
.onEach { vm.onPutBackClicked() }
.launchIn(lifecycleScope)
binding.tvDelete
.clicks()
.onEach { vm.onDeleteObjectsClicked() }
.launchIn(lifecycleScope)
click(binding.btnAddDoc) { vm.onAddNewDocumentClicked() }
click(binding.btnSearch) { vm.onPageSearchClicked() }
click(binding.btnMarketplace) { toast(getString(R.string.coming_soon)) }
click(binding.ivSettings) { vm.onSettingsClicked() }
click(binding.avatarContainer) { vm.onAvatarClicked() }
click(binding.tvCancel) { vm.onCancelSelectionClicked() }
click(binding.tvSelectAll) { vm.onSelectAllClicked() }
click(binding.tvRestore) { vm.onPutBackClicked() }
click(binding.tvDelete) { vm.onDeleteObjectsClicked() }
}
override fun inflateBinding(

View file

@ -132,10 +132,10 @@ import com.anytypeio.anytype.ui.linking.LinkToObjectOrWebPagesFragment
import com.anytypeio.anytype.ui.linking.OnLinkToAction
import com.anytypeio.anytype.ui.moving.MoveToFragment
import com.anytypeio.anytype.ui.moving.OnMoveToAction
import com.anytypeio.anytype.ui.objects.appearance.ObjectAppearanceSettingFragment
import com.anytypeio.anytype.ui.objects.types.pickers.DraftObjectSelectTypeFragment
import com.anytypeio.anytype.ui.objects.types.pickers.ObjectSelectTypeFragment
import com.anytypeio.anytype.ui.objects.types.pickers.OnObjectSelectTypeAction
import com.anytypeio.anytype.ui.objects.appearance.ObjectAppearanceSettingFragment
import com.anytypeio.anytype.ui.relations.RelationAddBaseFragment.Companion.CTX_KEY
import com.anytypeio.anytype.ui.relations.RelationAddResult
import com.anytypeio.anytype.ui.relations.RelationAddToObjectBlockFragment
@ -886,7 +886,7 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
is Command.OpenTextBlockIconPicker -> {
TextBlockIconPickerFragment.new(
context = ctx, blockId = command.block
).show(childFragmentManager, null)
).showChildFragment()
}
Command.OpenDocumentEmojiIconPicker -> {
hideSoftInput()
@ -901,7 +901,7 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
is Command.OpenBookmarkSetter -> {
CreateBookmarkFragment.newInstance(
target = command.target
).show(childFragmentManager, null)
).showChildFragment()
}
is Command.OpenGallery -> {
pickerDelegate.openFilePicker(command.mimeType, null)
@ -936,7 +936,7 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
isProfile = false,
fromName = getFrom()
)
fr.show(childFragmentManager, null)
fr.showChildFragment()
}
is Command.OpenProfileMenu -> {
hideKeyboard()
@ -963,7 +963,7 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
val fr = ObjectLayoutFragment.new(command.ctx).apply {
onDismissListener = { vm.onLayoutDialogDismissed() }
}
fr.show(childFragmentManager, null)
fr.showChildFragment()
}
is Command.OpenFullScreenImage -> {
val screen = FullScreenPictureFragment.new(command.target, command.url).apply {
@ -978,7 +978,7 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
}
is Command.AlertDialog -> {
if (childFragmentManager.findFragmentByTag(TAG_ALERT) == null) {
AlertUpdateAppFragment().show(childFragmentManager, TAG_ALERT)
AlertUpdateAppFragment().showChildFragment(TAG_ALERT)
} else {
// Do nothing
}
@ -988,7 +988,7 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
}
is Command.Dialog.SelectLanguage -> {
SelectProgrammingLanguageFragment.new(command.target)
.show(childFragmentManager, null)
.showChildFragment()
}
is Command.OpenObjectRelationScreen.RelationAdd -> {
hideKeyboard()
@ -998,7 +998,7 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
target = command.target,
mode = RelationListFragment.MODE_ADD
)
.show(childFragmentManager, null)
.showChildFragment()
}
is Command.OpenObjectRelationScreen.RelationList -> {
hideKeyboard()
@ -1023,7 +1023,7 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
targetObjectTypes = command.targetObjectTypes,
isLocked = command.isLocked
)
fr.show(childFragmentManager, null)
fr.showChildFragment()
}
is Command.OpenObjectRelationScreen.Value.Text -> {
hideKeyboard()
@ -1034,7 +1034,7 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
objectId = command.target,
isLocked = command.isLocked
)
fr.show(childFragmentManager, null)
fr.showChildFragment()
}
is Command.OpenObjectRelationScreen.Value.Date -> {
hideKeyboard()
@ -1044,7 +1044,7 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
relationId = command.relationId,
relationKey = command.relationKey
)
fr.show(childFragmentManager, null)
fr.showChildFragment()
}
Command.AddSlashWidgetTriggerToFocusedBlock -> {
binding.recycler.addTextFromSelectedStart(text = "/")
@ -1054,14 +1054,14 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
val fr = DraftObjectSelectTypeFragment.newInstance(
excludeTypes = command.excludedTypes
)
fr.show(childFragmentManager, null)
fr.showChildFragment()
}
is Command.OpenObjectSelectTypeScreen -> {
hideKeyboard()
val fr = ObjectSelectTypeFragment.newInstance(
excludeTypes = command.excludedTypes
)
fr.show(childFragmentManager, null)
fr.showChildFragment()
}
is Command.OpenMoveToScreen -> {
jobs += lifecycleScope.launch {
@ -1073,7 +1073,7 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
restorePosition = command.restorePosition,
restoreBlock = command.restoreBlock
)
fr.show(childFragmentManager, null)
fr.showChildFragment()
}
}
is Command.OpenObjectSnackbar -> {
@ -1099,7 +1099,7 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
position = command.position,
ignore = vm.context
)
fr.show(childFragmentManager, null)
fr.showChildFragment()
}
}
is Command.AddMentionWidgetTriggerToFocusedBlock -> {
@ -1125,7 +1125,7 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
rangeEnd = command.range.last,
isWholeBlockMarkup = command.isWholeBlockMarkup
)
fr.show(childFragmentManager, null)
fr.showChildFragment()
}
is Command.ShowKeyboard -> {
binding.recycler.findFocus()?.focusAndShowKeyboard()
@ -1144,7 +1144,7 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
ctx = command.ctx,
block = command.block
)
fr.show(childFragmentManager, null)
fr.showChildFragment()
}
is Command.ScrollToPosition -> {
val lm = binding.recycler.layoutManager as LinearLayoutManager
@ -1162,7 +1162,7 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
hideKeyboard()
}
}
fr.show(childFragmentManager, null)
fr.showChildFragment()
}
}
}

View file

@ -12,11 +12,12 @@ 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.click
import com.anytypeio.anytype.core_ui.reactive.proceed
import com.anytypeio.anytype.core_utils.ext.arg
import com.anytypeio.anytype.core_utils.ext.shareFile
import com.anytypeio.anytype.core_utils.ext.throttleFirst
import com.anytypeio.anytype.core_utils.ext.toast
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetFragment
import com.anytypeio.anytype.core_utils.ui.proceed
import com.anytypeio.anytype.core_utils.ui.showActionableSnackBar
import com.anytypeio.anytype.databinding.FragmentObjectMenuBinding
import com.anytypeio.anytype.presentation.objects.ObjectIcon
@ -75,7 +76,7 @@ abstract class ObjectMenuBaseFragment :
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.commands.throttleFirst()) { command -> execute(command) }
proceed(vm.options) { options -> renderOptions(options) }
super.onStart()
@ -121,7 +122,7 @@ abstract class ObjectMenuBaseFragment :
ObjectMenuViewModelBase.Command.OpenSetIcons -> openSetIcons()
ObjectMenuViewModelBase.Command.OpenSetLayout -> toast(COMING_SOON_MSG)
ObjectMenuViewModelBase.Command.OpenSetRelations -> toast(COMING_SOON_MSG)
ObjectMenuViewModelBase.Command.OpenLinkToChooser -> openLinkChooser(command)
ObjectMenuViewModelBase.Command.OpenLinkToChooser -> openLinkChooser()
is ObjectMenuViewModelBase.Command.OpenSnackbar -> openSnackbar(command)
is ObjectMenuViewModelBase.Command.ShareDebugTree -> shareFile(command.uri)
}
@ -178,7 +179,7 @@ abstract class ObjectMenuBaseFragment :
}
private fun openLinkChooser(command: ObjectMenuViewModelBase.Command) {
private fun openLinkChooser() {
val fr = MoveToFragment.new(
ctx = ctx,
blocks = emptyList(),

View file

@ -594,7 +594,7 @@ open class ObjectSetFragment :
flow = RelationTextValueFragment.FLOW_DATAVIEW,
relationKey = command.relationKey
)
fr.show(childFragmentManager, EMPTY_TAG)
fr.showChildFragment(EMPTY_TAG)
}
is ObjectSetCommand.Modal.EditGridDateCell -> {
//todo Relation as object, fix relationKey
@ -605,7 +605,7 @@ open class ObjectSetFragment :
flow = RelationDateValueFragment.FLOW_DATAVIEW,
relationKey = ""
)
fr.show(childFragmentManager, EMPTY_TAG)
fr.showChildFragment(EMPTY_TAG)
}
is ObjectSetCommand.Modal.EditRelationCell -> {
findNavController().safeNavigate(
@ -628,25 +628,25 @@ open class ObjectSetFragment :
ctx = command.ctx,
viewer = command.viewer
)
fr.show(childFragmentManager, EMPTY_TAG)
fr.showChildFragment(EMPTY_TAG)
}
is ObjectSetCommand.Modal.CreateViewer -> {
val fr = CreateDataViewViewerFragment.new(
ctx = command.ctx,
target = command.target
)
fr.show(childFragmentManager, EMPTY_TAG)
fr.showChildFragment(EMPTY_TAG)
}
is ObjectSetCommand.Modal.EditDataViewViewer -> {
val fr = EditDataViewViewerFragment.new(
ctx = command.ctx,
viewer = command.viewer
)
fr.show(childFragmentManager, EMPTY_TAG)
fr.showChildFragment(EMPTY_TAG)
}
is ObjectSetCommand.Modal.ManageViewer -> {
val fr = ManageViewerFragment.new(ctx = command.ctx, dataview = command.dataview)
fr.show(childFragmentManager, EMPTY_TAG)
fr.showChildFragment(EMPTY_TAG)
}
is ObjectSetCommand.Modal.OpenSettings -> {
val fr = ObjectSetSettingsFragment.new(
@ -654,7 +654,7 @@ open class ObjectSetFragment :
dv = command.dv,
viewer = command.viewer
)
fr.show(childFragmentManager, EMPTY_TAG)
fr.showChildFragment(EMPTY_TAG)
}
is ObjectSetCommand.Modal.SetNameForCreatedObject -> {
findNavController().safeNavigate(
@ -712,11 +712,11 @@ open class ObjectSetFragment :
val fr = ViewerFilterFragment.new(
ctx = command.ctx
)
fr.show(childFragmentManager, EMPTY_TAG)
fr.showChildFragment(EMPTY_TAG)
}
is ObjectSetCommand.Modal.ModifyViewerSorts -> {
val fr = ViewerSortFragment.new(ctx)
fr.show(childFragmentManager, EMPTY_TAG)
fr.showChildFragment(EMPTY_TAG)
}
is ObjectSetCommand.Modal.OpenCoverActionMenu -> {
findNavController().safeNavigate(
@ -735,11 +735,11 @@ open class ObjectSetFragment :
val fr = DataViewSelectSourceFragment.newInstance(
selectedTypes = command.selectedTypes
)
fr.show(childFragmentManager, null)
fr.showChildFragment()
}
is ObjectSetCommand.Modal.OpenEmptyDataViewSelectSourceScreen -> {
val fr = EmptyDataViewSelectSourceFragment()
fr.show(childFragmentManager, null)
fr.showChildFragment()
}
}
}

View file

@ -11,7 +11,6 @@ import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.core.os.bundleOf
import androidx.fragment.app.viewModels
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
@ -39,11 +38,11 @@ class AccountAndDataFragment : BaseBottomSheetComposeFragment() {
private val onKeychainPhraseClicked = {
val bundle =
bundleOf(KeychainPhraseDialog.ARG_SCREEN_TYPE to EventsDictionary.Type.screenSettings)
findNavController().navigate(R.id.keychainDialog, bundle)
safeNavigate(R.id.keychainDialog, bundle)
}
private val onLogoutClicked = {
findNavController().navigate(R.id.logoutWarningScreen)
safeNavigate(R.id.logoutWarningScreen)
}
override fun onCreateView(
@ -62,10 +61,10 @@ class AccountAndDataFragment : BaseBottomSheetComposeFragment() {
MaterialTheme(typography = typography) {
AccountAndDataScreen(
onKeychainPhraseClicked = onKeychainPhraseClicked,
onClearFileCachedClicked = { proceedWithClearFileCacheWarning() },
onDeleteAccountClicked = { proceedWithAccountDeletion() },
onClearFileCachedClicked = { throttle { proceedWithClearFileCacheWarning() } },
onDeleteAccountClicked = { throttle { proceedWithAccountDeletion() } },
onLogoutClicked = onLogoutClicked,
onDebugSyncReportClicked = { vm.onDebugSyncReportClicked() },
onDebugSyncReportClicked = { throttle { vm.onDebugSyncReportClicked() } },
isLogoutInProgress = vm.isLoggingOut.collectAsState().value,
isClearCacheInProgress = vm.isClearFileCacheInProgress.collectAsState().value,
isDebugSyncReportInProgress = vm.isDebugSyncReportInProgress.collectAsState().value,

View file

@ -9,7 +9,6 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetComposeFragment
import com.anytypeio.anytype.di.common.componentManager
@ -25,7 +24,7 @@ class AppearanceFragment : BaseBottomSheetComposeFragment() {
private val vm by viewModels<AppearanceViewModel> { factory }
private val onWallpaperClicked = {
findNavController().navigate(R.id.wallpaperSetScreen)
safeNavigate(R.id.wallpaperSetScreen)
}
override fun onCreateView(
@ -39,9 +38,9 @@ class AppearanceFragment : BaseBottomSheetComposeFragment() {
MaterialTheme(typography = typography) {
AppearanceScreen(
onWallpaperClicked = onWallpaperClicked,
light = { vm.onLight() },
dark = { vm.onDark() },
system = { vm.onSystem() },
light = { throttle { vm.onLight() } },
dark = { throttle { vm.onDark() } },
system = { throttle { vm.onSystem() } },
selectedMode = vm.selectedTheme.collectAsState().value
)
}

View file

@ -12,17 +12,15 @@ import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
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.presentation.settings.MainSettingsViewModel.Event
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
import javax.inject.Inject
@ -90,16 +88,16 @@ class MainSettingFragment : BaseBottomSheetComposeFragment() {
private fun processCommands(command: Command) {
when (command) {
Command.OpenAboutScreen -> {
findNavController().navigate(R.id.actionOpenAboutAppScreen)
safeNavigate(R.id.actionOpenAboutAppScreen)
}
Command.OpenAccountAndDataScreen -> {
findNavController().navigate(R.id.actionOpenAccountAndDataScreen)
safeNavigate(R.id.actionOpenAccountAndDataScreen)
}
Command.OpenAppearanceScreen -> {
findNavController().navigate(R.id.actionOpenAppearanceScreen)
safeNavigate(R.id.actionOpenAppearanceScreen)
}
Command.OpenPersonalizationScreen -> {
findNavController().navigate(R.id.actionOpenPersonalizationScreen)
safeNavigate(R.id.actionOpenPersonalizationScreen)
}
Command.OpenDebugScreen -> {
startActivity(Intent(requireActivity(), SettingsActivity::class.java))

View file

@ -5,11 +5,10 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_utils.ext.subscribe
import com.anytypeio.anytype.core_utils.ext.toast
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetFragment
import com.anytypeio.anytype.core_utils.ui.proceed
import com.anytypeio.anytype.databinding.FragmentUserSettingsBinding
import com.anytypeio.anytype.di.common.componentManager
import com.anytypeio.anytype.presentation.settings.OtherSettingsViewModel
@ -38,26 +37,23 @@ class OtherSettingsFragment : BaseBottomSheetFragment<FragmentUserSettingsBindin
override fun onStart() {
super.onStart()
with(lifecycleScope) {
jobs += subscribe(vm.commands) { observe(it) }
jobs += subscribe(vm.defaultObjectTypeName) { binding.objectType.text = it }
}
proceed(vm.commands) { observe(it) }
proceed(vm.defaultObjectTypeName) { binding.objectType.text = it }
}
private fun observe(command: OtherSettingsViewModel.Command) {
when (command) {
is OtherSettingsViewModel.Command.Exit -> dismiss()
is OtherSettingsViewModel.Command.Exit -> throttle { dismiss() }
is OtherSettingsViewModel.Command.NavigateToObjectTypesScreen -> {
val fr = AppDefaultObjectTypeFragment.newInstance(
AppDefaultObjectTypeFragment.newInstance(
excludeTypes = command.excludeTypes
)
fr.show(childFragmentManager, null)
).showChildFragment()
}
is OtherSettingsViewModel.Command.Toast -> toast(command.msg)
is OtherSettingsViewModel.Command.ShowClearCacheAlert -> {
val dialog = ClearCacheAlertFragment.new()
dialog.onClearAccepted = { vm.proceedWithClearCache() }
dialog.show(childFragmentManager, null)
dialog.showChildFragment()
}
}
}

View file

@ -127,12 +127,4 @@ fun BaseBottomSheetFragment<*>.click(
.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)
}

View file

@ -104,4 +104,5 @@ fun MutableList<Job>.cancel() {
clear()
}
const val DEFAULT_THROTTLE_DURATION = 1000L
const val DEFAULT_THROTTLE_DURATION = 1000L
const val LONG_THROTTLE_DURATION = 2000L

View file

@ -2,13 +2,49 @@ package com.anytypeio.anytype.core_utils.ui
import android.os.Bundle
import android.view.View
import androidx.annotation.IdRes
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import com.anytypeio.anytype.core_utils.R
import com.anytypeio.anytype.core_utils.ext.LONG_THROTTLE_DURATION
import com.anytypeio.anytype.core_utils.ext.throttleFirst
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
abstract class BaseBottomSheetComposeFragment : BottomSheetDialogFragment() {
protected val jobs = mutableListOf<Job>()
val jobs = mutableListOf<Job>()
private val currentNavigationId by lazy { getNavigationId() }
private val throttleFlow = MutableSharedFlow<() -> Unit>(0)
protected fun safeNavigate(
@IdRes id: Int,
args: Bundle? = null
) {
jobs += this.lifecycleScope.launch {
throttleFlow.emit {
if (currentNavigationId == getNavigationId()) {
findNavController().navigate(id, args)
}
}
}
}
protected fun throttle(task: () -> Unit) {
jobs += this.lifecycleScope.launch { throttleFlow.emit { task() } }
}
override fun onStart() {
super.onStart()
proceed(throttleFlow.throttleFirst(LONG_THROTTLE_DURATION)) { it() }
}
override fun onStop() {
super.onStop()
@ -35,4 +71,10 @@ abstract class BaseBottomSheetComposeFragment : BottomSheetDialogFragment() {
abstract fun injectDependencies()
abstract fun releaseDependencies()
}
fun Fragment.getNavigationId() = findNavController().currentDestination?.id
fun <T> BaseBottomSheetComposeFragment.proceed(flow: Flow<T>, body: suspend (T) -> Unit) {
jobs += flow.onEach { body(it) }.launchIn(lifecycleScope)
}

View file

@ -5,11 +5,20 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.fragment.app.DialogFragment
import androidx.lifecycle.lifecycleScope
import androidx.viewbinding.ViewBinding
import com.anytypeio.anytype.core_utils.R
import com.anytypeio.anytype.core_utils.ext.LONG_THROTTLE_DURATION
import com.anytypeio.anytype.core_utils.ext.throttleFirst
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import com.google.android.material.R.id.design_bottom_sheet as BOTTOM_SHEET_ID
abstract class BaseBottomSheetFragment<T : ViewBinding>(
@ -21,6 +30,12 @@ abstract class BaseBottomSheetFragment<T : ViewBinding>(
val sheet: FrameLayout? get() = dialog?.findViewById(BOTTOM_SHEET_ID)
private val throttleFlow = MutableSharedFlow<() -> Unit>(0)
protected fun throttle(task: () -> Unit) {
jobs += this.lifecycleScope.launch { throttleFlow.emit { task() } }
}
val jobs = mutableListOf<Job>()
override fun onCreateView(
@ -42,6 +57,11 @@ abstract class BaseBottomSheetFragment<T : ViewBinding>(
injectDependencies()
}
override fun onStart() {
super.onStart()
proceed(throttleFlow.throttleFirst(LONG_THROTTLE_DURATION)) { it() }
}
override fun onStop() {
super.onStop()
jobs.apply {
@ -50,6 +70,12 @@ abstract class BaseBottomSheetFragment<T : ViewBinding>(
}
}
protected fun DialogFragment.showChildFragment(tag: String? = null) {
jobs += this@BaseBottomSheetFragment.lifecycleScope.launch {
throttleFlow.emit { show(this@BaseBottomSheetFragment.childFragmentManager, tag) }
}
}
override fun onDestroy() {
super.onDestroy()
if (fragmentScope) releaseDependencies()
@ -80,4 +106,8 @@ abstract class BaseBottomSheetFragment<T : ViewBinding>(
}
protected abstract fun inflateBinding(inflater: LayoutInflater, container: ViewGroup?): T
}
fun <T> BaseBottomSheetFragment<*>.proceed(flow: Flow<T>, body: suspend (T) -> Unit) {
jobs += flow.onEach { body(it) }.launchIn(lifecycleScope)
}

View file

@ -8,11 +8,20 @@ import android.view.ViewGroup
import androidx.annotation.LayoutRes
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.viewbinding.ViewBinding
import com.anytypeio.anytype.core_utils.BuildConfig
import com.anytypeio.anytype.core_utils.ext.LONG_THROTTLE_DURATION
import com.anytypeio.anytype.core_utils.ext.throttleFirst
import com.anytypeio.anytype.core_utils.insets.RootViewDeferringInsetsCallback
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
abstract class BaseFragment<T : ViewBinding>(
@LayoutRes private val layout: Int,
@ -26,6 +35,7 @@ abstract class BaseFragment<T : ViewBinding>(
val hasBinding get() = _binding != null
val jobs = mutableListOf<Job>()
private val throttleFlow = MutableSharedFlow<() -> Unit>(0)
abstract fun injectDependencies()
abstract fun releaseDependencies()
@ -35,6 +45,15 @@ abstract class BaseFragment<T : ViewBinding>(
injectDependencies()
}
override fun onStart() {
super.onStart()
proceed(throttleFlow.throttleFirst(LONG_THROTTLE_DURATION)) { it() }
}
protected fun throttle(task: () -> Unit) {
jobs += this.lifecycleScope.launch { throttleFlow.emit { task() } }
}
override fun onStop() {
super.onStop()
jobs.apply {
@ -59,7 +78,9 @@ abstract class BaseFragment<T : ViewBinding>(
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if (applyWindowRootInsets) { onApplyWindowRootInsets() }
if (applyWindowRootInsets) {
onApplyWindowRootInsets()
}
}
open fun onApplyWindowRootInsets() {
@ -73,10 +94,20 @@ abstract class BaseFragment<T : ViewBinding>(
}
}
protected fun DialogFragment.showChildFragment(tag: String? = null) {
jobs += this@BaseFragment.lifecycleScope.launch {
throttleFlow.emit { show(this@BaseFragment.childFragmentManager, tag) }
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
protected abstract fun inflateBinding(inflater: LayoutInflater, container: ViewGroup?): T
}
fun <T> BaseFragment<*>.proceed(flow: Flow<T>, body: suspend (T) -> Unit) {
jobs += flow.onEach { body(it) }.launchIn(lifecycleScope)
}

View file

@ -910,7 +910,7 @@ class ObjectSetViewModel(
}
fun onViewerSettingsClicked() {
Timber.d("onViewerRelationsClicked, ")
Timber.d("onViewerSettingsClicked, ")
if (isRestrictionPresent(DataViewRestriction.RELATION)) {
toast(NOT_ALLOWED)
} else {