diff --git a/app/src/main/java/com/anytypeio/anytype/ui/editor/sheets/ObjectMenuBaseFragment.kt b/app/src/main/java/com/anytypeio/anytype/ui/editor/sheets/ObjectMenuBaseFragment.kt index e4ba02ff08..c6fcf563ab 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/editor/sheets/ObjectMenuBaseFragment.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/editor/sheets/ObjectMenuBaseFragment.kt @@ -39,6 +39,7 @@ import com.anytypeio.anytype.ui.linking.BacklinkOrAddToObjectFragment import com.anytypeio.anytype.ui.moving.OnMoveToAction import com.anytypeio.anytype.ui.relations.ObjectRelationListFragment import com.google.android.material.snackbar.Snackbar +import timber.log.Timber abstract class ObjectMenuBaseFragment : BaseBottomSheetFragment(), @@ -177,6 +178,8 @@ abstract class ObjectMenuBaseFragment : spaceId = space ) ) + }.onFailure { + Timber.e(it, "Failed to open history screen") } } } diff --git a/app/src/main/java/com/anytypeio/anytype/ui/history/VersionHistoryFragment.kt b/app/src/main/java/com/anytypeio/anytype/ui/history/VersionHistoryFragment.kt index 0e9024d71f..3f73e9eac8 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/history/VersionHistoryFragment.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/history/VersionHistoryFragment.kt @@ -25,13 +25,19 @@ import com.anytypeio.anytype.core_ui.features.history.VersionHistoryPreviewScree import com.anytypeio.anytype.core_ui.features.history.VersionHistoryScreen import com.anytypeio.anytype.core_ui.tools.ClipboardInterceptor import com.anytypeio.anytype.core_utils.ext.argString +import com.anytypeio.anytype.core_utils.ext.safeNavigate import com.anytypeio.anytype.core_utils.ext.setupBottomSheetBehavior import com.anytypeio.anytype.core_utils.ext.subscribe import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetComposeFragment import com.anytypeio.anytype.di.common.componentManager -import com.anytypeio.anytype.presentation.history.VersionGroupNavigation import com.anytypeio.anytype.presentation.history.VersionHistoryVMFactory import com.anytypeio.anytype.presentation.history.VersionHistoryViewModel +import com.anytypeio.anytype.presentation.history.VersionHistoryViewModel.Command +import com.anytypeio.anytype.presentation.relations.value.tagstatus.RelationContext +import com.anytypeio.anytype.ui.relations.RelationDateValueFragment +import com.anytypeio.anytype.ui.relations.RelationTextValueFragment +import com.anytypeio.anytype.ui.relations.value.ObjectValueFragment +import com.anytypeio.anytype.ui.relations.value.TagOrStatusValueFragment import com.google.accompanist.navigation.material.BottomSheetNavigator import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi import com.google.accompanist.navigation.material.ModalBottomSheetLayout @@ -61,6 +67,9 @@ class VersionHistoryFragment : BaseBottomSheetComposeFragment() { onDragListener = { _, _ -> false }, lifecycle = lifecycle, dragAndDropSelector = DragAndDropAdapterDelegate(), + onClickListener = { + vm.proceedWithClick(it) + } ) @OptIn(ExperimentalMaterialNavigationApi::class) @@ -95,15 +104,15 @@ class VersionHistoryFragment : BaseBottomSheetComposeFragment() { private fun NavigationGraph(navController: NavHostController) { NavHost( navController = navController, - startDestination = VersionGroupNavigation.Main.route + startDestination = Command.Main.route ) { - composable(VersionGroupNavigation.Main.route) { + composable(Command.Main.route) { VersionHistoryScreen( state = vm.viewState.collectAsStateWithLifecycle().value, onItemClick = vm::onGroupItemClicked ) } - bottomSheet(VersionGroupNavigation.VersionPreview.route) { + bottomSheet(Command.VersionPreview.route) { VersionHistoryPreviewScreen( state = vm.previewViewState.collectAsStateWithLifecycle().value, editorAdapter = editorAdapter, @@ -118,20 +127,86 @@ class VersionHistoryFragment : BaseBottomSheetComposeFragment() { super.onViewCreated(view, savedInstanceState) setupBottomSheetBehavior(DEFAULT_PADDING_TOP) subscribe(vm.navigation){ navigation -> - when(navigation){ - is VersionGroupNavigation.VersionPreview -> { - navComposeController.navigate(VersionGroupNavigation.VersionPreview.route) - } - VersionGroupNavigation.Main -> { - navComposeController.popBackStack() - } - VersionGroupNavigation.ExitToObject -> { - findNavController().popBackStack(R.id.objectMenuScreen, true) - } + when (navigation) { + is Command.VersionPreview -> navigateToVersionPreview() + Command.Main -> navComposeController.popBackStack() + Command.ExitToObject -> exitToObjectMenu() + is Command.RelationMultiSelect -> navigateToRelationMultiSelect(navigation) + is Command.RelationDate -> navigateToRelationDate(navigation) + is Command.RelationObject -> navigateToRelationObject(navigation) + is Command.RelationText -> navigateToRelationText(navigation) } } } + private fun navigateToVersionPreview() { + navComposeController.navigate(Command.VersionPreview.route) + } + + private fun exitToObjectMenu() { + findNavController().popBackStack(R.id.objectMenuScreen, true) + } + + private fun navigateToRelationMultiSelect(navigation: Command.RelationMultiSelect) { + val relationContext = if (navigation.isSet) RelationContext.OBJECT_SET else RelationContext.OBJECT + val bundle = TagOrStatusValueFragment.args( + ctx = ctx, + space = spaceId, + obj = ctx, + relation = navigation.relationKey.key, + isLocked = true, + context = relationContext + ) + findNavController().safeNavigate( + R.id.versionHistoryScreen, + R.id.nav_relations, + bundle + ) + } + + private fun navigateToRelationDate(navigation: Command.RelationDate) { + val relationContext = if (navigation.isSet) RelationDateValueFragment.FLOW_SET_OR_COLLECTION else RelationDateValueFragment.FLOW_DEFAULT + val fr = RelationDateValueFragment.new( + ctx = ctx, + space = spaceId, + relationKey = navigation.relationKey.key, + objectId = ctx, + flow = relationContext, + isLocked = true + ) + fr.showChildFragment() + } + + private fun navigateToRelationObject(navigation: Command.RelationObject) { + val relationContext = if (navigation.isSet) RelationContext.OBJECT_SET else RelationContext.OBJECT + findNavController().safeNavigate( + R.id.versionHistoryScreen, + R.id.objectValueScreen, + ObjectValueFragment.args( + ctx = ctx, + space = spaceId, + obj = ctx, + relation = navigation.relationKey.key, + isLocked = true, + relationContext = relationContext + ) + ) + } + + private fun navigateToRelationText(navigation: Command.RelationText) { + val relationContext = + if (navigation.isSet) RelationTextValueFragment.FLOW_SET_OR_COLLECTION else RelationTextValueFragment.FLOW_DEFAULT + val fr = RelationTextValueFragment.new( + ctx = ctx, + space = spaceId, + relationKey = navigation.relationKey.key, + objectId = ctx, + flow = relationContext, + isLocked = true + ) + fr.showChildFragment() + } + override fun onStart() { super.onStart() vm.onStart() diff --git a/app/src/main/res/navigation/graph.xml b/app/src/main/res/navigation/graph.xml index fdbcca4156..0948b6d2f2 100644 --- a/app/src/main/res/navigation/graph.xml +++ b/app/src/main/res/navigation/graph.xml @@ -73,6 +73,11 @@ android:id="@+id/objectValueScreen" android:name="com.anytypeio.anytype.ui.relations.value.ObjectValueFragment" android:label="Relation-Object-Value-Screen" /> + + @@ -136,6 +141,10 @@ android:id="@+id/objectValueScreen" android:name="com.anytypeio.anytype.ui.relations.value.ObjectValueFragment" android:label="Relation-Object-Value-Screen" /> + - - RecyclerView(context).apply { + layoutParams = RecyclerView.LayoutParams( + RecyclerView.LayoutParams.MATCH_PARENT, + RecyclerView.LayoutParams.MATCH_PARENT + ) layoutManager = LinearLayoutManager(context) adapter = editorAdapter } diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/relations/ObjectValueCompose.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/relations/ObjectValueCompose.kt index 43c9c80355..ac64095222 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/relations/ObjectValueCompose.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/relations/ObjectValueCompose.kt @@ -89,8 +89,7 @@ fun RelationsViewContent( LazyColumn( state = lazyListState, modifier = Modifier - .fillMaxWidth() - .wrapContentHeight() + .fillMaxSize() ) { itemsIndexed( items = state.items, diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/history/VersionHistoryViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/history/VersionHistoryViewModel.kt index 56e268d5b7..33bc691924 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/history/VersionHistoryViewModel.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/history/VersionHistoryViewModel.kt @@ -6,8 +6,12 @@ import com.anytypeio.anytype.analytics.base.Analytics import com.anytypeio.anytype.core_models.Event import com.anytypeio.anytype.core_models.Id import com.anytypeio.anytype.core_models.ObjectWrapper +import com.anytypeio.anytype.core_models.Relation +import com.anytypeio.anytype.core_models.RelationFormat import com.anytypeio.anytype.core_models.ext.asMap import com.anytypeio.anytype.core_models.history.Version +import com.anytypeio.anytype.core_models.isDataView +import com.anytypeio.anytype.core_models.primitives.RelationKey import com.anytypeio.anytype.core_models.primitives.SpaceId import com.anytypeio.anytype.core_models.primitives.TimeInSeconds import com.anytypeio.anytype.domain.base.fold @@ -21,11 +25,13 @@ import com.anytypeio.anytype.domain.misc.UrlBuilder import com.anytypeio.anytype.domain.search.SearchObjects import com.anytypeio.anytype.presentation.editor.Editor.Mode import com.anytypeio.anytype.presentation.editor.EditorViewModel.Companion.INITIAL_INDENT +import com.anytypeio.anytype.presentation.editor.editor.listener.ListenerType import com.anytypeio.anytype.presentation.editor.editor.model.BlockView import com.anytypeio.anytype.presentation.editor.render.BlockViewRenderer import com.anytypeio.anytype.presentation.editor.render.DefaultBlockViewRenderer import com.anytypeio.anytype.presentation.history.VersionHistoryGroup.GroupTitle import com.anytypeio.anytype.presentation.objects.ObjectIcon +import com.anytypeio.anytype.presentation.relations.getRelationFormat import com.anytypeio.anytype.presentation.search.ObjectSearchConstants import java.time.Instant import java.time.LocalDate @@ -54,7 +60,7 @@ class VersionHistoryViewModel( private val _previewViewState = MutableStateFlow(VersionHistoryPreviewScreen.Hidden) val previewViewState = _previewViewState - val navigation = MutableSharedFlow(0) + val navigation = MutableSharedFlow(0) init { Timber.d("VersionHistoryViewModel created") @@ -68,30 +74,82 @@ class VersionHistoryViewModel( fun onGroupItemClicked(item: VersionHistoryGroup.Item) { viewModelScope.launch { _previewViewState.value = VersionHistoryPreviewScreen.Loading - navigation.emit(VersionGroupNavigation.VersionPreview) + navigation.emit(Command.VersionPreview) proceedShowVersion(item = item) } } + fun proceedWithClick(click: ListenerType) { + Timber.d("Click: $click") + viewModelScope.launch { + when (click) { + is ListenerType.Relation.Featured -> { + proceedWithRelationValueNavigation( + relation = RelationKey(click.relation.key), + relationFormat = click.relation.getRelationFormat() + ) + } + + is ListenerType.Relation.Related -> { + if (click.value is BlockView.Relation.Related) { + proceedWithRelationValueNavigation( + relation = RelationKey(click.value.view.key), + relationFormat = click.value.view.getRelationFormat() + ) + } + } + else -> {} + } + } + } + + private suspend fun proceedWithRelationValueNavigation( + relation: RelationKey, + relationFormat: Relation.Format + ) { + val currentState = (_previewViewState.value as? VersionHistoryPreviewScreen.Success) ?: return + val isSet = currentState.isSet + when (relationFormat) { + RelationFormat.SHORT_TEXT, + RelationFormat.LONG_TEXT, + RelationFormat.URL, + RelationFormat.PHONE, + RelationFormat.NUMBER, + RelationFormat.EMAIL -> navigation.emit(Command.RelationText(relation, isSet)) + + RelationFormat.DATE -> navigation.emit(Command.RelationDate(relation, isSet)) + Relation.Format.TAG, + Relation.Format.STATUS -> navigation.emit(Command.RelationMultiSelect(relation, isSet)) + + Relation.Format.OBJECT, + Relation.Format.FILE -> navigation.emit(Command.RelationObject(relation, isSet)) + + else -> { + Timber.d("No interaction allowed with this relation with format:$relationFormat") + } + } + } + fun proceedWithHidePreview() { _previewViewState.value = VersionHistoryPreviewScreen.Hidden viewModelScope.launch { - navigation.emit(VersionGroupNavigation.Main) + navigation.emit(Command.Main) } } fun proceedWithRestore() { - val currentVersionId = (_previewViewState.value as? VersionHistoryPreviewScreen.Success)?.versionId ?: return + val currentVersionId = + (_previewViewState.value as? VersionHistoryPreviewScreen.Success)?.versionId ?: return viewModelScope.launch { val params = SetVersion.Params( - objectId = vmParams.objectId, - versionId = currentVersionId - ) + objectId = vmParams.objectId, + versionId = currentVersionId + ) setVersion.async(params).fold( onSuccess = { Timber.d("Version restored") _previewViewState.value = VersionHistoryPreviewScreen.Hidden - navigation.emit(VersionGroupNavigation.ExitToObject) + navigation.emit(Command.ExitToObject) }, onFailure = { Timber.e(it, "Error while restoring version") @@ -265,9 +323,11 @@ class VersionHistoryViewModel( currentDate -> { GroupTitle.Today } + currentDate.minusDays(1) -> { GroupTitle.Yesterday } + else -> { val pattern = if (givenYear == currentYear) { GROUP_DATE_FORMAT_CURRENT_YEAR @@ -342,6 +402,7 @@ class VersionHistoryViewModel( val event = payload.events .filterIsInstance() .first() + val obj = ObjectWrapper.Basic(event.details.details[vmParams.objectId]?.map.orEmpty()) val root = event.blocks.first { it.id == vmParams.objectId } val blocks = event.blocks.asMap().render( mode = Mode.Read, @@ -361,7 +422,8 @@ class VersionHistoryViewModel( blocks = blocks, dateFormatted = item.dateFormatted, timeFormatted = item.timeFormatted, - icon = item.icon + icon = item.icon, + isSet = obj.layout.isDataView() ) } } @@ -374,8 +436,14 @@ class VersionHistoryViewModel( val spaceId: SpaceId ) - sealed class Command { - data class OpenVersion(val versionId: Id) : Command() + sealed class Command(val route: String) { + data object Main : Command("main") + data object VersionPreview : Command("version preview") + data object ExitToObject : Command("") + data class RelationMultiSelect(val relationKey: RelationKey, val isSet: Boolean) : Command("relation_select") + data class RelationObject(val relationKey: RelationKey, val isSet: Boolean) : Command("relation_object") + data class RelationDate(val relationKey: RelationKey, val isSet: Boolean) : Command("relation_date") + data class RelationText(val relationKey: RelationKey, val isSet: Boolean) : Command("relation_text") } companion object { @@ -403,7 +471,8 @@ sealed class VersionHistoryPreviewScreen { val blocks: List, val dateFormatted: String, val timeFormatted: String, - val icon: ObjectIcon? + val icon: ObjectIcon?, + val isSet: Boolean ) : VersionHistoryPreviewScreen() @@ -433,10 +502,4 @@ data class VersionHistoryGroup( data object Yesterday : GroupTitle() data class Date(val date: String) : GroupTitle() } -} - -sealed class VersionGroupNavigation(val route: String) { - data object Main : VersionGroupNavigation("main") - data object VersionPreview : VersionGroupNavigation("version preview") - data object ExitToObject : VersionGroupNavigation("") } \ No newline at end of file diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/relations/RelationExtensions.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/relations/RelationExtensions.kt index 60c27db32c..4ce9750dae 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/relations/RelationExtensions.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/relations/RelationExtensions.kt @@ -281,4 +281,18 @@ suspend fun getNotIncludedRecommendedRelations( val relationLinkKeys = relationLinks.map { it.key }.toSet() return storeOfRelations.getById(recommendedRelations) .filterNot { recommended -> recommended.key in relationLinkKeys } +} + +fun ObjectRelationView.getRelationFormat(): RelationFormat = when (this) { + is ObjectRelationView.Object -> RelationFormat.OBJECT + is ObjectRelationView.File -> RelationFormat.FILE + is ObjectRelationView.Default -> format + is ObjectRelationView.Status -> RelationFormat.STATUS + is ObjectRelationView.Tags -> RelationFormat.TAG + is ObjectRelationView.Checkbox -> RelationFormat.CHECKBOX + is ObjectRelationView.Links.Backlinks -> RelationFormat.OBJECT + is ObjectRelationView.Links.From -> RelationFormat.OBJECT + is ObjectRelationView.ObjectType.Base -> RelationFormat.OBJECT + is ObjectRelationView.ObjectType.Deleted -> RelationFormat.OBJECT + is ObjectRelationView.Source -> RelationFormat.OBJECT } \ No newline at end of file