diff --git a/app/src/main/java/com/anytypeio/anytype/ui/main/MainActivity.kt b/app/src/main/java/com/anytypeio/anytype/ui/main/MainActivity.kt index 20367e3cfe..7d0640dda5 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/main/MainActivity.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/main/MainActivity.kt @@ -215,7 +215,13 @@ class MainActivity : AppCompatActivity(R.layout.activity_main), AppNavigation.Pr ) ) } else { - controller.navigate(R.id.actionOpenSpaceFromVault) + controller.navigate( + R.id.actionOpenSpaceFromVault, + HomeScreenFragment.args( + space = command.space, + deeplink = null + ) + ) } proceedWithOpenObjectNavigation(command.navigation) }.onFailure { diff --git a/app/src/main/java/com/anytypeio/anytype/ui/vault/VaultFragment.kt b/app/src/main/java/com/anytypeio/anytype/ui/vault/VaultFragment.kt index ae69abbdd0..4c8d246127 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/vault/VaultFragment.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/vault/VaultFragment.kt @@ -150,7 +150,13 @@ class VaultFragment : BaseComposeFragment() { private fun proceed(destination: Navigation) { when (destination) { is Navigation.OpenObject -> runCatching { - findNavController().navigate(R.id.actionOpenSpaceFromVault) + findNavController().navigate( + R.id.actionOpenSpaceFromVault, + HomeScreenFragment.args( + space = destination.space, + deeplink = null + ) + ) navigation().openDocument( target = destination.ctx, space = destination.space @@ -159,7 +165,13 @@ class VaultFragment : BaseComposeFragment() { Timber.e(it, "Error while opening object from vault") } is Navigation.OpenSet -> runCatching { - findNavController().navigate(R.id.actionOpenSpaceFromVault) + findNavController().navigate( + R.id.actionOpenSpaceFromVault, + HomeScreenFragment.args( + space = destination.space, + deeplink = null + ) + ) navigation().openObjectSet( target = destination.ctx, space = destination.space, @@ -169,7 +181,13 @@ class VaultFragment : BaseComposeFragment() { Timber.e(it, "Error while opening set or collection from vault") } is Navigation.OpenChat -> { - findNavController().navigate(R.id.actionOpenSpaceFromVault) + findNavController().navigate( + R.id.actionOpenSpaceFromVault, + HomeScreenFragment.args( + space = destination.space, + deeplink = null + ) + ) navigation().openChat( target = destination.ctx, space = destination.space @@ -177,7 +195,13 @@ class VaultFragment : BaseComposeFragment() { } is Navigation.OpenDateObject -> { runCatching { - findNavController().navigate(R.id.actionOpenSpaceFromVault) + findNavController().navigate( + R.id.actionOpenSpaceFromVault, + HomeScreenFragment.args( + space = destination.space, + deeplink = null + ) + ) navigation().openDateObject( objectId = destination.ctx, space = destination.space @@ -189,7 +213,13 @@ class VaultFragment : BaseComposeFragment() { is Navigation.OpenParticipant -> { runCatching { - findNavController().navigate(R.id.actionOpenSpaceFromVault) + findNavController().navigate( + R.id.actionOpenSpaceFromVault, + HomeScreenFragment.args( + space = destination.space, + deeplink = null + ) + ) navigation().openParticipantObject( objectId = destination.ctx, space = destination.space @@ -205,7 +235,7 @@ class VaultFragment : BaseComposeFragment() { } override fun onApplyWindowRootInsets(view: View) { - if (USE_EDGE_TO_EDGE && SDK_INT >= EDGE_TO_EDGE_MIN_SDK) { + if (SDK_INT >= EDGE_TO_EDGE_MIN_SDK) { // Do nothing. } else { super.onApplyWindowRootInsets(view) diff --git a/app/src/main/res/navigation/graph.xml b/app/src/main/res/navigation/graph.xml index 9c298a509f..c5b4ef95a4 100644 --- a/app/src/main/res/navigation/graph.xml +++ b/app/src/main/res/navigation/graph.xml @@ -401,15 +401,6 @@ android:name="com.anytypeio.anytype.ui.splash.SplashFragment" android:label="SplashFragment" tools:layout="@layout/fragment_splash"> - BaseBottomSheetComposeFragment.proceed(flow: Flow, body: suspend (T) -> Unit) { - jobs += flow.cancellable().onEach { body(it) }.launchIn(lifecycleScope) + jobs += flow + .cancellable() + .onEach { + try { + body(it) + } catch (e: Exception) { + Timber.e(e, "Unhandled exception in proceed flow") + } + } + .launchIn(lifecycleScope) } \ No newline at end of file diff --git a/core-utils/src/main/java/com/anytypeio/anytype/core_utils/ui/BaseBottomSheetFragment.kt b/core-utils/src/main/java/com/anytypeio/anytype/core_utils/ui/BaseBottomSheetFragment.kt index c78bcbdc3b..c957e11332 100644 --- a/core-utils/src/main/java/com/anytypeio/anytype/core_utils/ui/BaseBottomSheetFragment.kt +++ b/core-utils/src/main/java/com/anytypeio/anytype/core_utils/ui/BaseBottomSheetFragment.kt @@ -21,6 +21,7 @@ import kotlinx.coroutines.flow.cancellable import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch +import timber.log.Timber import com.google.android.material.R.id.design_bottom_sheet as BOTTOM_SHEET_ID abstract class BaseBottomSheetFragment( @@ -33,13 +34,18 @@ abstract class BaseBottomSheetFragment( val sheet: FrameLayout? get() = dialog?.findViewById(BOTTOM_SHEET_ID) private val throttleFlow = MutableSharedFlow<() -> Unit>(0) + val jobs = mutableListOf() protected fun throttle(task: () -> Unit) { - jobs += this.lifecycleScope.launch { throttleFlow.emit { task() } } + jobs += this.lifecycleScope.launch { + try { + throttleFlow.emit { task() } + } catch (e: Exception) { + Timber.e(e, "Error during emit in throttle()") + } + } } - val jobs = mutableListOf() - override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -64,7 +70,13 @@ abstract class BaseBottomSheetFragment( if (resources.configuration.orientation == ORIENTATION_LANDSCAPE) { expand() } - proceed(throttleFlow.throttleFirst(LONG_THROTTLE_DURATION)) { it() } + proceed(throttleFlow.throttleFirst(LONG_THROTTLE_DURATION)) { + try { + it() + } catch (e: Exception) { + Timber.e(e, "Unhandled exception in throttled flow execution") + } + } } override fun onStop() { @@ -77,7 +89,17 @@ abstract class BaseBottomSheetFragment( protected fun DialogFragment.showChildFragment(tag: String? = null) { jobs += this@BaseBottomSheetFragment.lifecycleScope.launch { - throttleFlow.emit { show(this@BaseBottomSheetFragment.childFragmentManager, tag) } + try { + throttleFlow.emit { + try { + show(this@BaseBottomSheetFragment.childFragmentManager, tag) + } catch (e: Exception) { + Timber.e(e, "Error while showing child dialog") + } + } + } catch (e: Exception) { + Timber.e(e, "Error during emit in showChildFragment") + } } } @@ -87,18 +109,14 @@ abstract class BaseBottomSheetFragment( } fun skipCollapsed() { - sheet?.let { sheet -> - BottomSheetBehavior.from(sheet).apply { - skipCollapsed = true - } + sheet?.let { + BottomSheetBehavior.from(it).skipCollapsed = true } } fun expand() { - sheet?.let { sheet -> - BottomSheetBehavior.from(sheet).apply { - state = BottomSheetBehavior.STATE_EXPANDED - } + sheet?.let { + BottomSheetBehavior.from(it).state = BottomSheetBehavior.STATE_EXPANDED } } @@ -106,17 +124,25 @@ abstract class BaseBottomSheetFragment( sheet?.layoutParams?.height = ViewGroup.LayoutParams.MATCH_PARENT } - abstract fun injectDependencies() - abstract fun releaseDependencies() - override fun onDestroyView() { super.onDestroyView() _binding = null } + abstract fun injectDependencies() + abstract fun releaseDependencies() protected abstract fun inflateBinding(inflater: LayoutInflater, container: ViewGroup?): T } fun BaseBottomSheetFragment<*>.proceed(flow: Flow, body: suspend (T) -> Unit) { - jobs += flow.cancellable().onEach { body(it) }.launchIn(lifecycleScope) + jobs += flow + .cancellable() + .onEach { + try { + body(it) + } catch (e: Exception) { + Timber.e(e, "Unhandled exception in proceed flow") + } + } + .launchIn(lifecycleScope) } \ No newline at end of file diff --git a/core-utils/src/main/java/com/anytypeio/anytype/core_utils/ui/BaseBottomSheetTextInputFragment.kt b/core-utils/src/main/java/com/anytypeio/anytype/core_utils/ui/BaseBottomSheetTextInputFragment.kt index a77a80bfa1..ada3ff9411 100644 --- a/core-utils/src/main/java/com/anytypeio/anytype/core_utils/ui/BaseBottomSheetTextInputFragment.kt +++ b/core-utils/src/main/java/com/anytypeio/anytype/core_utils/ui/BaseBottomSheetTextInputFragment.kt @@ -22,7 +22,7 @@ abstract class BaseBottomSheetTextInputFragment( } private fun setupWindowInsetAnimation() { - if (BuildConfig.USE_NEW_WINDOW_INSET_API && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { textInput.syncFocusWithImeVisibility() } } diff --git a/core-utils/src/main/java/com/anytypeio/anytype/core_utils/ui/BaseComposeFragment.kt b/core-utils/src/main/java/com/anytypeio/anytype/core_utils/ui/BaseComposeFragment.kt index 0751047af0..c205f87e85 100644 --- a/core-utils/src/main/java/com/anytypeio/anytype/core_utils/ui/BaseComposeFragment.kt +++ b/core-utils/src/main/java/com/anytypeio/anytype/core_utils/ui/BaseComposeFragment.kt @@ -16,6 +16,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.cancellable import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import timber.log.Timber abstract class BaseComposeFragment : Fragment() { @@ -42,26 +43,37 @@ abstract class BaseComposeFragment : Fragment() { } open fun onApplyWindowRootInsets(view: View) { - if (BuildConfig.USE_NEW_WINDOW_INSET_API && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { val deferringInsetsListener = RootViewDeferringInsetsCallback( persistentInsetTypes = WindowInsetsCompat.Type.systemBars(), deferredInsetTypes = WindowInsetsCompat.Type.ime() ) - ViewCompat.setWindowInsetsAnimationCallback(view, deferringInsetsListener) ViewCompat.setOnApplyWindowInsetsListener(view, deferringInsetsListener) } } protected fun DialogFragment.showChildFragment(tag: String? = null) { - show(this@BaseComposeFragment.childFragmentManager, tag) + try { + show(this@BaseComposeFragment.childFragmentManager, tag) + } catch (e: Exception) { + Timber.e(e, "Error while showing child fragment") + } } - abstract fun injectDependencies() abstract fun releaseDependencies() } fun BaseComposeFragment.proceed(flow: Flow, body: suspend (T) -> Unit) { - jobs += flow.cancellable().onEach { body(it) }.launchIn(lifecycleScope) + jobs += flow + .cancellable() + .onEach { + try { + body(it) + } catch (e: Exception) { + Timber.e(e, "Unhandled exception in proceed flow") + } + } + .launchIn(lifecycleScope) } \ No newline at end of file diff --git a/core-utils/src/main/java/com/anytypeio/anytype/core_utils/ui/BaseFragment.kt b/core-utils/src/main/java/com/anytypeio/anytype/core_utils/ui/BaseFragment.kt index b42227e85b..784ecefca1 100644 --- a/core-utils/src/main/java/com/anytypeio/anytype/core_utils/ui/BaseFragment.kt +++ b/core-utils/src/main/java/com/anytypeio/anytype/core_utils/ui/BaseFragment.kt @@ -90,7 +90,7 @@ abstract class BaseFragment( } open fun onApplyWindowRootInsets() { - if (BuildConfig.USE_NEW_WINDOW_INSET_API && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { val deferringInsetsListener = RootViewDeferringInsetsCallback( persistentInsetTypes = WindowInsetsCompat.Type.systemBars(), deferredInsetTypes = WindowInsetsCompat.Type.ime() @@ -102,17 +102,21 @@ abstract class BaseFragment( protected fun DialogFragment.showChildFragment(tag: String? = null) { jobs += this@BaseFragment.lifecycleScope.launch { - throttleFlow.emit { - runCatching { - show(this@BaseFragment.childFragmentManager, tag) - }.fold( - onSuccess = { - // Do nothing. - }, - onFailure = { - Timber.e(it, "Error while navigation") - } - ) + try { + throttleFlow.emit { + runCatching { + show(this@BaseFragment.childFragmentManager, tag) + }.fold( + onSuccess = { + // Do nothing. + }, + onFailure = { + Timber.e(it, "Error while navigation") + } + ) + } + } catch (e: Exception) { + Timber.e(e, "Error while navigation") } } } @@ -126,7 +130,16 @@ abstract class BaseFragment( } fun BaseFragment<*>.proceed(flow: Flow, body: suspend (T) -> Unit) { - jobs += flow.cancellable().onEach { body(it) }.launchIn(lifecycleScope) + jobs += flow + .cancellable() + .onEach { + try { + body(it) + } catch (e: Exception) { + Timber.e(e, "Unhandled exception in proceed flow") + } + } + .launchIn(lifecycleScope) } const val THROTTLE_DURATION = 300L \ No newline at end of file diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt index 9b2a1c5525..cf025667f8 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt @@ -3334,11 +3334,16 @@ class EditorViewModel( private fun onAddNewObjectClicked( objectTypeView: ObjectTypeView ) { - controlPanelInteractor.onEvent(ControlPanelMachine.Event.OnAddBlockToolbarOptionSelected) - val position: Position - val focused = blocks.first { it.id == orchestrator.stores.focus.current().targetOrNull() } + val focused = blocks.find { it.id == orchestrator.stores.focus.current().targetOrNull() } + + if (focused == null) { + Timber.e("Error while trying to add new object: focused block is null, target is unknown.") + return + } + + controlPanelInteractor.onEvent(ControlPanelMachine.Event.OnAddBlockToolbarOptionSelected) var target = focused.id @@ -3741,7 +3746,9 @@ class EditorViewModel( if (target == context) { position = Position.TOP - moveTarget = targetBlock.children.first() + moveTarget = targetBlock.children.firstOrNull().orEmpty().also { + Timber.e("Could not find move target in target block's children") + } } blocks.filter { selected.contains(it.id) }.forEach { block -> @@ -5076,7 +5083,11 @@ class EditorViewModel( ) } is SlashItem.Main.Color -> { - val block = blocks.first { it.id == targetId } + val block = blocks.find { it.id == targetId } + if (block == null) { + Timber.d("Could not find target block for slash item action: color") + return + } val blockColor = block.content.asText().color val color = if (blockColor != null) { ThemeColor.valueOf(blockColor.toUpperCase()) @@ -5092,7 +5103,11 @@ class EditorViewModel( ) } is SlashItem.Main.Background -> { - val block = blocks.first { it.id == targetId } + val block = blocks.find { it.id == targetId } + if (block == null) { + Timber.d("Could not find target block for slash item action: background") + return + } val blockBackground = block.backgroundColor val background = if (blockBackground == null) { ThemeColor.DEFAULT @@ -5977,12 +5992,16 @@ class EditorViewModel( private fun onMultiSelectAddBelow() { mode = EditorMode.Edit controlPanelInteractor.onEvent(ControlPanelMachine.Event.MultiSelect.OnExit) - val target = currentSelection().first() - clearSelections() - proceedWithCreatingNewTextBlock( - target = target, - style = Content.Text.Style.P - ) + val target = currentSelection().firstOrNull() + if (target != null) { + clearSelections() + proceedWithCreatingNewTextBlock( + target = target, + style = Content.Text.Style.P + ) + } else { + Timber.e("Could not define target in onMultiSelectAddBelow()") + } } fun onMultiSelectModeDeleteClicked() {