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 1ec22c6450..ef3c7b3c83 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 @@ -5,7 +5,9 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.core.os.bundleOf +import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.LinearLayoutManager import com.anytypeio.anytype.R @@ -25,6 +27,8 @@ 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.feature_object_type.ui.conflict.ConflictScreen +import com.anytypeio.anytype.feature_object_type.ui.conflict.ConflictScreenPreview import com.anytypeio.anytype.presentation.objects.ObjectIcon import com.anytypeio.anytype.presentation.objects.menu.ObjectMenuOptionsProvider import com.anytypeio.anytype.presentation.objects.menu.ObjectMenuViewModelBase @@ -78,6 +82,16 @@ abstract class ObjectMenuBaseFragment : ) ) } + binding.objectLayoutConflictScreen.apply { + setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) + setContent { + ConflictScreen( + showScreen = vm.showLayoutConflictScreen.collectAsStateWithLifecycle().value, + onResetClick = { }, + onDismiss = { vm.onHideConflictScreen() } + ) + } + } } override fun onStart() { @@ -88,6 +102,7 @@ abstract class ObjectMenuBaseFragment : click(binding.optionRelations) { vm.onRelationsClicked() } click(binding.optionCover) { vm.onCoverClicked(ctx = ctx, space = space) } click(binding.debugGoroutines) { vm.onDiagnosticsGoroutinesClicked(ctx = ctx) } + click(binding.objectLayoutConflict) { vm.onShowConflictScreen(objectId = ctx, space = space) } proceed(vm.actions) { actionAdapter.submitList(it) } proceed(vm.toasts) { toast(it) } @@ -162,11 +177,15 @@ abstract class ObjectMenuBaseFragment : snackbar.anchorView = binding.anchor snackbar.show() } + is ObjectMenuViewModelBase.Command.ShareDeeplinkToObject -> { val intent = Intent().apply { action = Intent.ACTION_SEND putExtra(Intent.EXTRA_TEXT, command.link) - putExtra(Intent.EXTRA_TITLE, getString(R.string.multiplayer_deeplink_to_your_object)) + putExtra( + Intent.EXTRA_TITLE, + getString(R.string.multiplayer_deeplink_to_your_object) + ) type = "text/plain" } startActivity(Intent.createChooser(intent, null)) diff --git a/app/src/main/res/layout/fragment_object_menu.xml b/app/src/main/res/layout/fragment_object_menu.xml index c41ba63113..658669995e 100644 --- a/app/src/main/res/layout/fragment_object_menu.xml +++ b/app/src/main/res/layout/fragment_object_menu.xml @@ -1,192 +1,216 @@ - + android:layout_height="match_parent"> - - - + android:layout_height="match_parent" + android:orientation="vertical"> - + + + android:layout_height="0dp" + android:layout_weight="1" + app:layout_constraintBottom_toTopOf="@id/rvContainer" + app:layout_constraintTop_toTopOf="parent"> - - - - - + android:layout_height="match_parent" + android:orientation="vertical"> - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + android:layout_height="108dp" + android:layout_marginTop="12dp" + android:layout_weight="0"> - + - + + + + + + + android:layout_height="wrap_content" + android:layout_gravity="bottom|center_horizontal" /> - \ No newline at end of file + \ No newline at end of file diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/ObjectMenuItemWidget.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/ObjectMenuItemWidget.kt index 63d7f4a2c4..d2748b9ecc 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/ObjectMenuItemWidget.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/ObjectMenuItemWidget.kt @@ -6,6 +6,8 @@ import android.view.LayoutInflater import android.widget.LinearLayout import com.anytypeio.anytype.core_ui.R import com.anytypeio.anytype.core_ui.databinding.WidgetObjectMenuItemBinding +import com.anytypeio.anytype.core_utils.ext.invisible +import com.anytypeio.anytype.core_utils.ext.visible class ObjectMenuItemWidget @JvmOverloads constructor( context: Context, @@ -26,6 +28,12 @@ class ObjectMenuItemWidget @JvmOverloads constructor( val attrs = context.obtainStyledAttributes(set, R.styleable.ObjectMenuItemWidget, 0, 0) tvTitle.text = attrs.getString(R.styleable.ObjectMenuItemWidget_title) ivIcon.setImageResource(attrs.getResourceId(R.styleable.ObjectMenuItemWidget_icon, -1)) + val showArrow = attrs.getBoolean(R.styleable.ObjectMenuItemWidget_showArrow, true) + if (showArrow) { + ivArrow.visible() + } else { + ivArrow.invisible() + } attrs.recycle() } } \ No newline at end of file diff --git a/core-ui/src/main/res/drawable/ic_attention_24.xml b/core-ui/src/main/res/drawable/ic_attention_24.xml new file mode 100644 index 0000000000..fd6d520549 --- /dev/null +++ b/core-ui/src/main/res/drawable/ic_attention_24.xml @@ -0,0 +1,13 @@ + + + + diff --git a/core-ui/src/main/res/values/attrs.xml b/core-ui/src/main/res/values/attrs.xml index 5762924d05..694a221007 100644 --- a/core-ui/src/main/res/values/attrs.xml +++ b/core-ui/src/main/res/values/attrs.xml @@ -49,6 +49,7 @@ + diff --git a/feature-object-type/src/main/java/com/anytypeio/anytype/feature_object_type/ui/conflict/ConflictScreen.kt b/feature-object-type/src/main/java/com/anytypeio/anytype/feature_object_type/ui/conflict/ConflictScreen.kt new file mode 100644 index 0000000000..a42117aaa4 --- /dev/null +++ b/feature-object-type/src/main/java/com/anytypeio/anytype/feature_object_type/ui/conflict/ConflictScreen.kt @@ -0,0 +1,113 @@ +package com.anytypeio.anytype.feature_object_type.ui.conflict + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.Text +import androidx.compose.material3.rememberModalBottomSheetState +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import com.anytypeio.anytype.core_ui.common.DefaultPreviews +import com.anytypeio.anytype.core_ui.foundation.Dragger +import com.anytypeio.anytype.core_ui.views.BodyCalloutRegular +import com.anytypeio.anytype.core_ui.views.ButtonPrimary +import com.anytypeio.anytype.core_ui.views.ButtonSecondary +import com.anytypeio.anytype.core_ui.views.ButtonSize +import com.anytypeio.anytype.core_ui.views.HeadlineSubheading +import com.anytypeio.anytype.feature_object_type.R + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun ConflictScreen( + modifier: Modifier = Modifier, + showScreen: Boolean, + onResetClick: () -> Unit, + onDismiss: () -> Unit, +) { + val bottomSheetState = rememberModalBottomSheetState( + skipPartiallyExpanded = true + ) + + if (showScreen) { + ModalBottomSheet( + modifier = modifier, + dragHandle = { + Column { + Spacer(modifier = Modifier.height(6.dp)) + Dragger() + Spacer(modifier = Modifier.height(6.dp)) + } + }, + scrimColor = colorResource(id = R.color.modal_screen_outside_background), + containerColor = colorResource(id = R.color.background_secondary), + shape = RoundedCornerShape(16.dp), + sheetState = bottomSheetState, + onDismissRequest = { + onDismiss() + } + ) { + Spacer(modifier = Modifier.height(20.dp)) + Text( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 20.dp), + textAlign = TextAlign.Center, + style = HeadlineSubheading, + color = colorResource(id = R.color.text_primary), + text = stringResource(id = R.string.object_conflict_screen_title) + ) + Spacer(modifier = Modifier.height(4.dp)) + Text( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 20.dp), + textAlign = TextAlign.Center, + style = BodyCalloutRegular, + color = colorResource(id = R.color.text_primary), + text = stringResource(id = R.string.object_conflict_screen_description) + ) + Spacer(modifier = Modifier.height(20.dp)) + ButtonPrimary( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 20.dp), + text = stringResource(R.string.object_conflict_screen_action_button), + size = ButtonSize.LargeSecondary, + onClick = { + onResetClick() + } + ) + Spacer(modifier = Modifier.height(8.dp)) + ButtonSecondary( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 20.dp), + text = stringResource(R.string.cancel), + size = ButtonSize.LargeSecondary, + onClick = { + onDismiss() + } + ) + Spacer(modifier = Modifier.height(10.dp)) + } + } +} + +@DefaultPreviews +@Composable +fun ConflictScreenPreview() { + ConflictScreen( + showScreen = true, + onResetClick = {}, + onDismiss = {} + ) +} \ No newline at end of file diff --git a/localization/src/main/res/values/strings.xml b/localization/src/main/res/values/strings.xml index 2e06a3c053..372d4382e1 100644 --- a/localization/src/main/res/values/strings.xml +++ b/localization/src/main/res/values/strings.xml @@ -2010,4 +2010,10 @@ Please provide specific details of your needs here. Search..." Space invite link copied! + Resolve layout conflict + This layout differs from the type\'s default. Reset to match? + Reset to default + + Resolve layout conflict + \ No newline at end of file diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/objects/menu/ObjectMenuViewModelBase.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/objects/menu/ObjectMenuViewModelBase.kt index 91fdb6f3d5..08685fdd54 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/objects/menu/ObjectMenuViewModelBase.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/objects/menu/ObjectMenuViewModelBase.kt @@ -96,6 +96,8 @@ abstract class ObjectMenuViewModelBase( abstract fun onDescriptionClicked(ctx: Id, space: Id) abstract fun onRelationsClicked() + val showLayoutConflictScreen = MutableStateFlow(false) + fun onHistoryClicked(ctx: Id, space: Id) { viewModelScope.launch { commands.emit(Command.OpenHistoryScreen(ctx, space)) @@ -478,6 +480,18 @@ abstract class ObjectMenuViewModelBase( } } + fun onShowConflictScreen(objectId: Id, space: Id) { + viewModelScope.launch { + showLayoutConflictScreen.value = true + } + } + + fun onHideConflictScreen() { + viewModelScope.launch { + showLayoutConflictScreen.value = false + } + } + sealed class Command { data object OpenObjectIcons : Command() data object OpenSetIcons : Command()