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

DROID-2861 Vault | Enhancement | Show introduce-vault screen to new users or after the first login (#1618)

This commit is contained in:
Evgenii Kozlov 2024-10-02 22:11:01 +02:00 committed by GitHub
parent 9ab3237716
commit 9d28c5d61b
Signed by: github
GPG key ID: B5690EEEBB952194
15 changed files with 188 additions and 33 deletions

View file

@ -4,6 +4,7 @@ import androidx.lifecycle.ViewModelProvider
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.core_utils.di.scope.PerScreen
import com.anytypeio.anytype.di.common.ComponentDependencies
import com.anytypeio.anytype.domain.auth.repo.AuthRepository
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
import com.anytypeio.anytype.domain.block.repo.BlockRepository
import com.anytypeio.anytype.domain.config.UserSettingsRepository
@ -54,4 +55,5 @@ interface VaultComponentDependencies : ComponentDependencies {
fun spaceViewSubscriptionContainer(): SpaceViewSubscriptionContainer
fun userSettingsRepository(): UserSettingsRepository
fun spaceManager(): SpaceManager
fun auth(): AuthRepository
}

View file

@ -0,0 +1,39 @@
package com.anytypeio.anytype.ui.vault
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.material.MaterialTheme
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetComposeFragment
import com.anytypeio.anytype.ui.settings.typography
class IntroduceVaultFragment : BaseBottomSheetComposeFragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
MaterialTheme(typography = typography) {
IntroduceVaultScreen(
onDoneClicked = {
dismiss()
}
)
}
}
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
skipCollapsed()
expand()
}
}

View file

@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.pager.HorizontalPager
@ -63,7 +64,7 @@ fun IntroduceVaultScreen(
) {
val coroutineScope = rememberCoroutineScope()
val (title, first, second, third, pager, dots, btn) = createRefs()
val (title, first, pager, dots, btn) = createRefs()
val pagerState = rememberPagerState(pageCount = { 2 })
@ -89,39 +90,35 @@ fun IntroduceVaultScreen(
HorizontalPager(
state = pagerState,
modifier = Modifier
.padding(bottom = 16.dp, top = 38.dp)
.padding(bottom = 16.dp, top = 42.dp)
.height(452.dp)
.fillMaxWidth()
.constrainAs(pager) {
top.linkTo(title.bottom)
bottom.linkTo(dots.top)
height = Dimension.fillToConstraints
}
.fillMaxSize()
) { page ->
when(page) {
0 -> {
Box(modifier = Modifier.fillMaxSize()) {
Box(
modifier = Modifier.fillMaxWidth()
) {
Image(
painter = painterResource(id = R.drawable.img_introduce_vault_1),
contentDescription = "Screenshot 1",
modifier = Modifier.align(Alignment.BottomCenter)
modifier = Modifier.align(Alignment.TopCenter)
)
}
}
1 -> {
Box(modifier = Modifier.fillMaxSize()) {
Box(
modifier = Modifier.fillMaxWidth()
) {
Image(
painter = painterResource(id = R.drawable.img_introduce_vault_1),
painter = painterResource(id = R.drawable.img_introduce_vault_2),
contentDescription = "Screenshot 2",
modifier = Modifier.align(Alignment.BottomCenter)
)
}
}
2 -> {
Box(modifier = Modifier.fillMaxSize()) {
Image(
painter = painterResource(id = R.drawable.ic_sharing_step_third),
contentDescription = "Screenshot 3",
modifier = Modifier.align(Alignment.BottomCenter)
modifier = Modifier.align(Alignment.TopCenter)
)
}
}
@ -131,9 +128,9 @@ fun IntroduceVaultScreen(
Row(
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 46.dp)
.padding(top = 4.dp)
.constrainAs(dots) {
bottom.linkTo(first.top)
bottom.linkTo(pager.bottom)
},
horizontalArrangement = Arrangement.Center
) {
@ -164,10 +161,11 @@ fun IntroduceVaultScreen(
style = BodyRegular,
color = colorResource(id = R.color.text_primary),
modifier = Modifier
.padding(bottom = 8.dp, start = 24.dp, end = 24.dp)
.padding(bottom = 30.dp, start = 24.dp, end = 24.dp)
.constrainAs(first) {
bottom.linkTo(btn.top)
}
},
textAlign = TextAlign.Center
)
ButtonSecondary(

View file

@ -82,6 +82,13 @@ class VaultFragment : BaseComposeFragment() {
Timber.e(it, "Error while opening profile settings from vault")
}
}
is Command.ShowIntroduceVault -> {
runCatching {
findNavController().navigate(R.id.actionShowIntroduceVaultScreen)
}.onFailure {
Timber.e(it, "Error while opening introduce-vault-screen from vault")
}
}
}
}
@ -89,6 +96,11 @@ class VaultFragment : BaseComposeFragment() {
// TODO Do nothing ?
}
override fun onResume() {
super.onResume()
vm.onResume()
}
override fun injectDependencies() {
componentManager().vaultComponent.get().inject(this)
}

View file

@ -46,7 +46,7 @@
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="@+id/galleryView"
app:layout_constraintBottom_toTopOf="@id/bottomToolbar"
app:layout_constraintBottom_toTopOf="@id/bottomToolbarСontainer"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/controlDivider2" />
@ -89,7 +89,7 @@
android:id="@+id/paginatorToolbar"
android:layout_width="0dp"
android:layout_height="@dimen/default_toolbar_height"
app:layout_constraintBottom_toTopOf="@id/bottomToolbar"
app:layout_constraintBottom_toTopOf="@id/bottomToolbarСontainer"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />

View file

@ -214,6 +214,9 @@
<action
android:id="@+id/actionCreateSpaceFromVault"
app:destination="@id/createSpaceScreen" />
<action
android:id="@+id/actionShowIntroduceVaultScreen"
app:destination="@id/introduceVaultScreen" />
</fragment>
<dialog
@ -225,6 +228,11 @@
app:popUpToInclusive="false" />
</dialog>
<dialog
android:id="@+id/introduceVaultScreen"
android:name="com.anytypeio.anytype.ui.vault.IntroduceVaultFragment"
android:label="IntroduceVaultScreen"/>
<dialog
android:id="@+id/spaceSettingsScreen"
android:name="com.anytypeio.anytype.ui.settings.space.SpaceSettingsFragment"

View file

@ -3,5 +3,6 @@ package com.anytypeio.anytype.core_models.settings
import com.anytypeio.anytype.core_models.Id
data class VaultSettings(
val showIntroduceVault: Boolean,
val orderOfSpaces: List<Id> = emptyList()
)

View file

@ -16,6 +16,7 @@ interface UserSettingsCache {
suspend fun getVaultSettings(account: Account): VaultSettings
suspend fun observeVaultSettings(account: Account): Flow<VaultSettings>
suspend fun setVaultSpaceOrder(account: Account, order: List<Id>)
suspend fun setVaultSettings(account: Account, settings: VaultSettings)
suspend fun setCurrentSpace(space: SpaceId)
suspend fun getCurrentSpace(): SpaceId?

View file

@ -95,6 +95,10 @@ class UserSettingsDataRepository(private val cache: UserSettingsCache) : UserSet
return cache.getVaultSettings(account)
}
override suspend fun setVaultSettings(account: Account, settings: VaultSettings) {
cache.setVaultSettings(account, settings)
}
override suspend fun observeVaultSettings(account: Account): Flow<VaultSettings> {
return cache.observeVaultSettings(account)
}

View file

@ -16,6 +16,7 @@ interface UserSettingsRepository {
suspend fun getVaultSettings(account: Account): VaultSettings
suspend fun observeVaultSettings(account: Account): Flow<VaultSettings>
suspend fun setVaultSpaceOrder(account: Account, order: List<Id>)
suspend fun setVaultSettings(account: Account, settings: VaultSettings)
suspend fun setCurrentSpace(space: SpaceId)
suspend fun getCurrentSpace(): SpaceId?

View file

@ -0,0 +1,20 @@
package com.anytypeio.anytype.domain.vault
import com.anytypeio.anytype.core_models.settings.VaultSettings
import com.anytypeio.anytype.domain.auth.repo.AuthRepository
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
import com.anytypeio.anytype.domain.base.ResultInteractor
import com.anytypeio.anytype.domain.config.UserSettingsRepository
import javax.inject.Inject
class GetVaultSettings @Inject constructor(
private val settings: UserSettingsRepository,
private val auth: AuthRepository,
dispatchers: AppCoroutineDispatchers
): ResultInteractor<Unit, VaultSettings>(dispatchers.io) {
override suspend fun doWork(params: Unit): VaultSettings {
val acc = auth.getCurrentAccount()
return settings.getVaultSettings(acc)
}
}

View file

@ -0,0 +1,20 @@
package com.anytypeio.anytype.domain.vault
import com.anytypeio.anytype.core_models.settings.VaultSettings
import com.anytypeio.anytype.domain.auth.repo.AuthRepository
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
import com.anytypeio.anytype.domain.base.ResultInteractor
import com.anytypeio.anytype.domain.config.UserSettingsRepository
import javax.inject.Inject
class SetVaultSettings @Inject constructor(
private val settings: UserSettingsRepository,
private val auth: AuthRepository,
dispatchers: AppCoroutineDispatchers
): ResultInteractor<VaultSettings, Unit>(dispatchers.io) {
override suspend fun doWork(params: VaultSettings): Unit {
val acc = auth.getCurrentAccount()
return settings.setVaultSettings(acc, params)
}
}

View file

@ -396,11 +396,15 @@ class DefaultUserSettingsCache(
return context.vaultPrefsStore
.data
.map { prefs ->
val curr = prefs.preferences.getOrDefault(
key = account.id,
defaultValue = VaultPreference(
showIntroduceVault = true
)
)
VaultSettings(
orderOfSpaces = prefs.preferences.getOrDefault(
key = account.id,
defaultValue = VaultPreference()
).orderOfSpaces
orderOfSpaces = curr.orderOfSpaces,
showIntroduceVault = curr.showIntroduceVault
)
}
.first()
@ -410,11 +414,15 @@ class DefaultUserSettingsCache(
return context.vaultPrefsStore
.data
.map { prefs ->
val curr = prefs.preferences.getOrDefault(
key = account.id,
defaultValue = VaultPreference(
showIntroduceVault = true
)
)
VaultSettings(
orderOfSpaces = prefs.preferences.getOrDefault(
key = account.id,
defaultValue = VaultPreference()
).orderOfSpaces
orderOfSpaces = curr.orderOfSpaces,
showIntroduceVault = curr.showIntroduceVault
)
}
}
@ -423,7 +431,9 @@ class DefaultUserSettingsCache(
context.vaultPrefsStore.updateData { existingPreferences ->
val curr = existingPreferences.preferences.getOrDefault(
key = account.id,
defaultValue = VaultPreference()
defaultValue = VaultPreference(
showIntroduceVault = true
)
)
existingPreferences.copy(
preferences = existingPreferences.preferences + mapOf(
@ -435,6 +445,19 @@ class DefaultUserSettingsCache(
}
}
override suspend fun setVaultSettings(account: Account, settings: VaultSettings) {
context.vaultPrefsStore.updateData { existingPreferences ->
existingPreferences.copy(
preferences = existingPreferences.preferences + mapOf(
account.id to VaultPreference(
orderOfSpaces = settings.orderOfSpaces,
showIntroduceVault = settings.showIntroduceVault
)
)
)
}
}
override suspend fun getAllContentSort(space: SpaceId): Id {
return context.spacePrefsStore
.data

View file

@ -15,6 +15,7 @@ message VaultPreferences {
message VaultPreference {
repeated string orderOfSpaces = 1;
bool showIntroduceVault = 2;
}
message SpacePreference {

View file

@ -9,9 +9,12 @@ import com.anytypeio.anytype.core_models.multiplayer.SpaceAccessType
import com.anytypeio.anytype.core_models.primitives.SpaceId
import com.anytypeio.anytype.core_models.restrictions.SpaceStatus
import com.anytypeio.anytype.domain.base.fold
import com.anytypeio.anytype.domain.base.onSuccess
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.multiplayer.SpaceViewSubscriptionContainer
import com.anytypeio.anytype.domain.spaces.SaveCurrentSpace
import com.anytypeio.anytype.domain.vault.GetVaultSettings
import com.anytypeio.anytype.domain.vault.SetVaultSettings
import com.anytypeio.anytype.domain.wallpaper.GetSpaceWallpapers
import com.anytypeio.anytype.domain.workspace.SpaceManager
import com.anytypeio.anytype.presentation.common.BaseViewModel
@ -31,6 +34,8 @@ class VaultViewModel(
private val getSpaceWallpapers: GetSpaceWallpapers,
private val spaceManager: SpaceManager,
private val saveCurrentSpace: SaveCurrentSpace,
private val getVaultSettings: GetVaultSettings,
private val setVaultSettings: SetVaultSettings
) : BaseViewModel() {
val spaces = MutableStateFlow<List<VaultSpaceView>>(emptyList())
@ -109,6 +114,21 @@ class VaultViewModel(
}
}
fun onResume() {
viewModelScope.launch {
getVaultSettings.async(Unit).onSuccess { settings ->
if (settings.showIntroduceVault) {
commands.emit(Command.ShowIntroduceVault)
setVaultSettings.async(
params = settings.copy(
showIntroduceVault = false
)
)
}
}
}
}
private suspend fun proceedWithSavingCurrentSpace(targetSpace: String) {
saveCurrentSpace.async(
SaveCurrentSpace.Params(SpaceId(targetSpace))
@ -128,6 +148,8 @@ class VaultViewModel(
private val urlBuilder: UrlBuilder,
private val spaceManager: SpaceManager,
private val saveCurrentSpace: SaveCurrentSpace,
private val getVaultSettings: GetVaultSettings,
private val setVaultSettings: SetVaultSettings
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(
@ -137,7 +159,9 @@ class VaultViewModel(
getSpaceWallpapers = getSpaceWallpapers,
urlBuilder = urlBuilder,
spaceManager = spaceManager,
saveCurrentSpace = saveCurrentSpace
saveCurrentSpace = saveCurrentSpace,
getVaultSettings = getVaultSettings,
setVaultSettings = setVaultSettings
) as T
}
@ -151,5 +175,6 @@ class VaultViewModel(
data object EnterSpaceHomeScreen: Command()
data object CreateNewSpace: Command()
data class OpenProfileSettings(val space: SpaceId): Command()
data object ShowIntroduceVault : Command()
}
}