diff --git a/app/src/main/java/com/anytypeio/anytype/ui/home/HomeScreen.kt b/app/src/main/java/com/anytypeio/anytype/ui/home/HomeScreen.kt index 10862ea0b5..4780290b3f 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/home/HomeScreen.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/home/HomeScreen.kt @@ -39,6 +39,22 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import com.anytypeio.anytype.R +import androidx.compose.foundation.layout.Column +import androidx.compose.material.Icon +import androidx.compose.material.IconButton +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Close +import androidx.compose.runtime.getValue +import androidx.compose.ui.draw.drawBehind +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.sp +import androidx.compose.ui.window.Popup +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import kotlinx.coroutines.flow.StateFlow import com.anytypeio.anytype.core_models.Id import com.anytypeio.anytype.core_models.ObjectWrapper import com.anytypeio.anytype.core_ui.extensions.throttledClick @@ -98,9 +114,13 @@ fun HomeScreen( onSeeAllObjectsClicked: (WidgetView.Gallery) -> Unit, onCreateObjectInsideWidget: (Id) -> Unit, onCreateDataViewObject: (WidgetId, ViewId?) -> Unit, - onBackLongClicked: () -> Unit + onBackLongClicked: () -> Unit, + showTooltip: StateFlow, + onTooltipDismissed: () -> Unit, ) { + val isTooltipVisible by showTooltip.collectAsStateWithLifecycle() + Box(modifier = Modifier.fillMaxSize()) { WidgetList( widgets = widgets, @@ -166,6 +186,15 @@ fun HomeScreen( addDocLongClick = onCreateNewObjectLongClicked, isOwnerOrEditor = mode !is InteractionMode.ReadOnly ) + + if (isTooltipVisible) { + SpaceSwitcherTooltip( + onDismissRequest = onTooltipDismissed, + modifier = Modifier + .align(Alignment.BottomCenter) + .padding(bottom = 90.dp), + ) + } } } @@ -809,4 +838,103 @@ fun WidgetEditModeButton( color = colorResource(id = R.color.text_white) ) } +} + +@Composable +fun SpaceSwitcherTooltip( + onDismissRequest: () -> Unit, + modifier: Modifier = Modifier +) { + TooltipPopup( + modifier = modifier, + tooltipContent = { + Box( + modifier = Modifier + .height(100.dp) + .background( + color = colorResource(id = R.color.home_screen_tooltip), + shape = RoundedCornerShape(16.dp) + ) + .padding(12.dp) + ) { + IconButton( + onClick = onDismissRequest, + modifier = Modifier.align(Alignment.TopEnd) + ) { + Icon( + imageVector = Icons.Default.Close, + contentDescription = "Close", + tint = colorResource(id = R.color.select_space_bottom_sheet_background_color) + ) + } + Column( + modifier = Modifier + .align(Alignment.CenterStart) + .padding(end = 40.dp) + ) { + Text( + text = "Switch spaces quicker", + color = Color.Black, + fontWeight = FontWeight.Bold, + fontSize = 14.sp + ) + Text( + text = "Long tap on back button to switch Space", + color = Color.Black, + fontSize = 12.sp + ) + } + } + } + ) +} + +@Composable +fun TooltipPopup( + modifier: Modifier = Modifier, + tooltipContent: @Composable () -> Unit +) { + Popup( + alignment = Alignment.BottomCenter, + offset = IntOffset(0, -180) + ) { + BubbleLayout( + arrowHeight = 8.dp, + arrowPositionX = 0.5f + ) { + tooltipContent() + } + } +} + +@Composable +fun BubbleLayout( + modifier: Modifier = Modifier, + arrowHeight: Dp, + arrowPositionX: Float, + content: @Composable () -> Unit +) { + val tooltipColor = colorResource(id = R.color.home_screen_tooltip) + val arrowHeightPx = with(LocalDensity.current) { arrowHeight.toPx() } + + Box( + modifier = modifier + .background(color = tooltipColor, shape = RoundedCornerShape(16.dp)) + .drawBehind { + val path = androidx.compose.ui.graphics.Path() + if (arrowPositionX in 0f..1f) { + val arrowCenter = Offset(size.width * arrowPositionX, size.height) + path.apply { + moveTo(arrowCenter.x, arrowCenter.y) + lineTo(arrowCenter.x + arrowHeightPx, arrowCenter.y) + lineTo(arrowCenter.x, arrowCenter.y + arrowHeightPx) + lineTo(arrowCenter.x - arrowHeightPx, arrowCenter.y) + close() + } + } + drawPath(path, color = tooltipColor) + } + ) { + content() + } } \ No newline at end of file diff --git a/app/src/main/java/com/anytypeio/anytype/ui/home/HomeScreenFragment.kt b/app/src/main/java/com/anytypeio/anytype/ui/home/HomeScreenFragment.kt index 764ee82990..026151363e 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/home/HomeScreenFragment.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/home/HomeScreenFragment.kt @@ -1,6 +1,7 @@ package com.anytypeio.anytype.ui.home import android.os.Bundle +import android.content.Context import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -27,7 +28,6 @@ import com.anytypeio.anytype.core_utils.ext.toast import com.anytypeio.anytype.core_utils.tools.FeatureToggles import com.anytypeio.anytype.core_utils.ui.BaseComposeFragment import com.anytypeio.anytype.di.common.componentManager -import com.anytypeio.anytype.feature_discussions.ui.DiscussionScreenWrapper import com.anytypeio.anytype.other.DefaultDeepLinkResolver import com.anytypeio.anytype.presentation.home.Command import com.anytypeio.anytype.presentation.home.HomeScreenViewModel @@ -73,6 +73,51 @@ class HomeScreenFragment : BaseComposeFragment(), private val vm by viewModels { factory } + private var spaceSwitchCount: Int = 0 + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val tooltipShownOnce = isTooltipShown() + spaceSwitchCount = getSpaceSwitchCount() + if (!tooltipShownOnce && spaceSwitchCount >= 2) { + vm.showTooltip() + saveTooltipShown(true) + spaceSwitchCount = 0 + } else if (!tooltipShownOnce) { + spaceSwitchCount++ + } + saveSpaceSwitchCount(spaceSwitchCount) + } + + private fun saveTooltipShown(shown: Boolean) { + val sharedPreferences = + requireContext().getSharedPreferences(SPACE_SWITCH_PREF, Context.MODE_PRIVATE) + sharedPreferences.edit().putBoolean(TOOLTIP_SHOWN_KEY, shown).apply() + } + + private fun isTooltipShown(): Boolean { + val sharedPreferences = + requireContext().getSharedPreferences(SPACE_SWITCH_PREF, Context.MODE_PRIVATE) + return sharedPreferences.getBoolean(TOOLTIP_SHOWN_KEY, false) + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + saveSpaceSwitchCount(spaceSwitchCount) + } + + private fun saveSpaceSwitchCount(count: Int) { + val sharedPreferences = + requireContext().getSharedPreferences(SPACE_SWITCH_PREF, Context.MODE_PRIVATE) + sharedPreferences.edit().putInt(SPACE_SWITCH_COUNT_KEY, count).apply() + } + + private fun getSpaceSwitchCount(): Int { + val sharedPreferences = + requireContext().getSharedPreferences(SPACE_SWITCH_PREF, Context.MODE_PRIVATE) + return sharedPreferences.getInt(SPACE_SWITCH_COUNT_KEY, 0) + } + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -125,7 +170,9 @@ class HomeScreenFragment : BaseComposeFragment(), onSeeAllObjectsClicked = vm::onSeeAllObjectsClicked, onCreateObjectInsideWidget = vm::onCreateObjectInsideWidget, onCreateDataViewObject = vm::onCreateDataViewObject, - onBackLongClicked = vm::onBackLongClicked + onBackLongClicked = vm::onBackLongClicked, + showTooltip = vm.showTooltip, + onTooltipDismissed = vm::onTooltipDismissed, ) } } @@ -390,6 +437,9 @@ class HomeScreenFragment : BaseComposeFragment(), } companion object { + private const val TOOLTIP_SHOWN_KEY = "tooltip_shown_key" + private const val SPACE_SWITCH_PREF = "HomeScreenPrefs" + private const val SPACE_SWITCH_COUNT_KEY = "space_switch_count_key" const val SHOW_MNEMONIC_KEY = "arg.home-screen.show-mnemonic" const val DEEP_LINK_KEY = "arg.home-screen.deep-link" fun args(deeplink: String?) : Bundle = bundleOf( diff --git a/core-ui/src/main/res/values-night/colors.xml b/core-ui/src/main/res/values-night/colors.xml index 15eaa7860c..ffaa50152b 100644 --- a/core-ui/src/main/res/values-night/colors.xml +++ b/core-ui/src/main/res/values-night/colors.xml @@ -130,4 +130,6 @@ #F2222222 + #F2F1F7 + diff --git a/core-ui/src/main/res/values/colors.xml b/core-ui/src/main/res/values/colors.xml index a467e8ab80..cfc4e14086 100644 --- a/core-ui/src/main/res/values/colors.xml +++ b/core-ui/src/main/res/values/colors.xml @@ -253,4 +253,6 @@ #007AFF + #F2F1F7 + diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/home/HomeScreenViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/home/HomeScreenViewModel.kt index a679d75360..1f753ffaa0 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/home/HomeScreenViewModel.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/home/HomeScreenViewModel.kt @@ -127,6 +127,7 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.combine @@ -340,6 +341,22 @@ class HomeScreenViewModel( proceedWithViewStatePipeline() } + private val _showTooltip = MutableStateFlow(false) + val showTooltip: StateFlow get() = _showTooltip + + private var tooltipShownOnce = false + + fun showTooltip() { + if (!tooltipShownOnce) { + _showTooltip.value = true + tooltipShownOnce = true + } + } + + fun onTooltipDismissed() { + _showTooltip.value = false + } + private fun proceedWithViewStatePipeline() { widgetObjectPipelineJobs += viewModelScope.launch { if (!isWidgetSessionRestored) {