mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-08 05:47:05 +09:00
DROID-3225 Vault | Enhancement | Replace settings icon by user icon (#2022)
This commit is contained in:
parent
4da849cae3
commit
497d8a5a09
8 changed files with 113 additions and 30 deletions
|
@ -16,6 +16,7 @@ import com.anytypeio.anytype.domain.misc.UrlBuilder
|
|||
import com.anytypeio.anytype.domain.multiplayer.SpaceInviteResolver
|
||||
import com.anytypeio.anytype.domain.multiplayer.SpaceViewSubscriptionContainer
|
||||
import com.anytypeio.anytype.domain.multiplayer.UserPermissionProvider
|
||||
import com.anytypeio.anytype.domain.search.ProfileSubscriptionManager
|
||||
import com.anytypeio.anytype.domain.workspace.SpaceManager
|
||||
import com.anytypeio.anytype.other.DefaultSpaceInviteResolver
|
||||
import com.anytypeio.anytype.presentation.navigation.DeepLinkToObjectDelegate
|
||||
|
@ -79,4 +80,5 @@ interface VaultComponentDependencies : ComponentDependencies {
|
|||
fun appActionManager(): AppActionManager
|
||||
fun logger(): Logger
|
||||
fun awaitAccount(): AwaitAccountStartManager
|
||||
fun profileContainer(): ProfileSubscriptionManager
|
||||
}
|
|
@ -57,7 +57,8 @@ class VaultFragment : BaseComposeFragment() {
|
|||
onSpaceClicked = vm::onSpaceClicked,
|
||||
onCreateSpaceClicked = vm::onCreateSpaceClicked,
|
||||
onSettingsClicked = vm::onSettingsClicked,
|
||||
onOrderChanged = vm::onOrderChanged
|
||||
onOrderChanged = vm::onOrderChanged,
|
||||
profile = vm.profileView.collectAsStateWithLifecycle().value
|
||||
)
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.anytypeio.anytype.ui.vault
|
||||
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Build.VERSION.SDK_INT
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
|
@ -21,7 +22,9 @@ import androidx.compose.foundation.layout.windowInsetsPadding
|
|||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
|
@ -34,13 +37,16 @@ import androidx.compose.ui.draw.alpha
|
|||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.core.graphics.toColorInt
|
||||
import coil.compose.rememberAsyncImagePainter
|
||||
import com.anytypeio.anytype.BuildConfig.USE_EDGE_TO_EDGE
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
|
@ -60,15 +66,22 @@ import com.anytypeio.anytype.core_ui.views.Relations3
|
|||
import com.anytypeio.anytype.core_ui.views.Title1
|
||||
import com.anytypeio.anytype.core_utils.insets.EDGE_TO_EDGE_MIN_SDK
|
||||
import com.anytypeio.anytype.presentation.editor.cover.CoverGradient
|
||||
import com.anytypeio.anytype.presentation.profile.AccountProfile
|
||||
import com.anytypeio.anytype.presentation.profile.ProfileIconView
|
||||
import com.anytypeio.anytype.presentation.spaces.SelectSpaceViewModel
|
||||
import com.anytypeio.anytype.presentation.spaces.SpaceIconView
|
||||
import com.anytypeio.anytype.presentation.vault.VaultViewModel.VaultSpaceView
|
||||
import com.anytypeio.anytype.presentation.wallpaper.WallpaperColor
|
||||
import com.anytypeio.anytype.ui.sharing.SharingData
|
||||
import com.anytypeio.anytype.ui.widgets.types.gradient
|
||||
import com.bumptech.glide.integration.compose.ExperimentalGlideComposeApi
|
||||
import com.bumptech.glide.integration.compose.GlideImage
|
||||
import com.bumptech.glide.request.RequestListener
|
||||
|
||||
|
||||
@Composable
|
||||
fun VaultScreen(
|
||||
profile: AccountProfile,
|
||||
spaces: List<VaultSpaceView>,
|
||||
onSpaceClicked: (VaultSpaceView) -> Unit,
|
||||
onCreateSpaceClicked: () -> Unit,
|
||||
|
@ -109,6 +122,7 @@ fun VaultScreen(
|
|||
) {
|
||||
|
||||
VaultScreenToolbar(
|
||||
profile = profile,
|
||||
onPlusClicked = onCreateSpaceClicked,
|
||||
onSettingsClicked = onSettingsClicked,
|
||||
spaceCountLimitReached = spaces.size >= SelectSpaceViewModel.MAX_SPACE_COUNT
|
||||
|
@ -170,8 +184,10 @@ fun VaultScreen(
|
|||
}
|
||||
|
||||
|
||||
@OptIn(ExperimentalGlideComposeApi::class)
|
||||
@Composable
|
||||
fun VaultScreenToolbar(
|
||||
profile: AccountProfile,
|
||||
spaceCountLimitReached: Boolean = false,
|
||||
onPlusClicked: () -> Unit,
|
||||
onSettingsClicked: () -> Unit
|
||||
|
@ -187,16 +203,57 @@ fun VaultScreenToolbar(
|
|||
color = colorResource(id = R.color.text_primary),
|
||||
modifier = Modifier.align(Alignment.Center)
|
||||
)
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_vault_settings),
|
||||
contentDescription = "Settings icon",
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterStart)
|
||||
.padding(start = 16.dp)
|
||||
.noRippleClickable {
|
||||
onSettingsClicked()
|
||||
when(profile) {
|
||||
is AccountProfile.Data -> {
|
||||
Box(
|
||||
Modifier
|
||||
.align(Alignment.CenterStart)
|
||||
.padding(start = 16.dp)
|
||||
.size(28.dp)
|
||||
.noRippleClickable {
|
||||
onSettingsClicked()
|
||||
}
|
||||
) {
|
||||
when(val icon = profile.icon) {
|
||||
is ProfileIconView.Image -> {
|
||||
GlideImage(
|
||||
model = icon.url,
|
||||
contentDescription = "Custom image profile",
|
||||
contentScale = ContentScale.Crop,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.clip(CircleShape)
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
val nameFirstChar = if (profile.name.isEmpty()) {
|
||||
stringResource(id = com.anytypeio.anytype.ui_settings.R.string.account_default_name)
|
||||
} else {
|
||||
profile.name.first().uppercaseChar().toString()
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.clip(CircleShape)
|
||||
.background(colorResource(id = com.anytypeio.anytype.ui_settings.R.color.text_tertiary))
|
||||
) {
|
||||
Text(
|
||||
text = nameFirstChar,
|
||||
style = MaterialTheme.typography.h3.copy(
|
||||
color = colorResource(id = com.anytypeio.anytype.ui_settings.R.color.text_white),
|
||||
fontSize = 20.sp
|
||||
),
|
||||
modifier = Modifier.align(Alignment.Center)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
AccountProfile.Idle -> {
|
||||
// Draw nothing
|
||||
}
|
||||
}
|
||||
if (!spaceCountLimitReached) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_vault_top_toolbar_plus),
|
||||
|
@ -411,7 +468,8 @@ fun LoadingSpaceCardPreview() {
|
|||
fun VaultScreenToolbarPreview() {
|
||||
VaultScreenToolbar(
|
||||
onPlusClicked = {},
|
||||
onSettingsClicked = {}
|
||||
onSettingsClicked = {},
|
||||
profile = AccountProfile.Idle
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -449,6 +507,7 @@ fun VaultScreenPreview() {
|
|||
onSpaceClicked = {},
|
||||
onCreateSpaceClicked = {},
|
||||
onSettingsClicked = {},
|
||||
onOrderChanged = {}
|
||||
onOrderChanged = {},
|
||||
profile = AccountProfile.Idle
|
||||
)
|
||||
}
|
|
@ -249,7 +249,7 @@ fun ParticipantScreenPreview() {
|
|||
ParticipantScreen(
|
||||
uiState = UiParticipantScreenState(
|
||||
name = "Jetpack Compose",
|
||||
icon = ProfileIconView.Emoji("M"),
|
||||
icon = ProfileIconView.Placeholder("M"),
|
||||
identity = "AnyId43",
|
||||
description = "some description",
|
||||
isOwner = true
|
||||
|
|
|
@ -59,6 +59,7 @@ import com.anytypeio.anytype.core_ui.views.BodyRegular
|
|||
import com.anytypeio.anytype.core_ui.views.Caption1Regular
|
||||
import com.anytypeio.anytype.core_ui.views.Title1
|
||||
import com.anytypeio.anytype.core_models.membership.MembershipStatus
|
||||
import com.anytypeio.anytype.presentation.profile.AccountProfile
|
||||
import com.anytypeio.anytype.presentation.profile.ProfileIconView
|
||||
import com.anytypeio.anytype.ui_settings.R
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
|
@ -73,7 +74,7 @@ fun ProfileSettingsScreen(
|
|||
isLogoutInProgress: Boolean,
|
||||
onNameChange: (String) -> Unit,
|
||||
onProfileIconClick: () -> Unit,
|
||||
account: ProfileSettingsViewModel.AccountProfile,
|
||||
account: AccountProfile,
|
||||
onAppearanceClicked: () -> Unit,
|
||||
onDataManagementClicked: () -> Unit,
|
||||
onAboutClicked: () -> Unit,
|
||||
|
@ -269,12 +270,12 @@ fun ActionWithProgressBar(
|
|||
@Composable
|
||||
private fun Header(
|
||||
modifier: Modifier = Modifier,
|
||||
account: ProfileSettingsViewModel.AccountProfile,
|
||||
account: AccountProfile,
|
||||
onProfileIconClick: () -> Unit,
|
||||
onNameSet: (String) -> Unit
|
||||
) {
|
||||
when (account) {
|
||||
is ProfileSettingsViewModel.AccountProfile.Data -> {
|
||||
is AccountProfile.Data -> {
|
||||
Box(modifier = modifier.padding(vertical = 6.dp)) {
|
||||
Dragger()
|
||||
}
|
||||
|
@ -290,7 +291,7 @@ private fun Header(
|
|||
}
|
||||
ProfileNameBlock(name = account.name, onNameSet = onNameSet)
|
||||
}
|
||||
is ProfileSettingsViewModel.AccountProfile.Idle -> {}
|
||||
is AccountProfile.Idle -> {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -448,7 +449,7 @@ private fun ProfileSettingPreview() {
|
|||
isLogoutInProgress = false,
|
||||
onNameChange = {},
|
||||
onProfileIconClick = {},
|
||||
account = ProfileSettingsViewModel.AccountProfile.Data(
|
||||
account = AccountProfile.Data(
|
||||
"Walter",
|
||||
icon = ProfileIconView.Placeholder("Walter")
|
||||
),
|
||||
|
|
|
@ -28,6 +28,7 @@ import com.anytypeio.anytype.presentation.extension.sendScreenSettingsDeleteEven
|
|||
import com.anytypeio.anytype.core_models.membership.MembershipStatus
|
||||
import com.anytypeio.anytype.domain.search.ProfileSubscriptionManager
|
||||
import com.anytypeio.anytype.presentation.membership.provider.MembershipProvider
|
||||
import com.anytypeio.anytype.presentation.profile.AccountProfile
|
||||
import com.anytypeio.anytype.presentation.profile.ProfileIconView
|
||||
import com.anytypeio.anytype.presentation.profile.profileIcon
|
||||
import kotlinx.coroutines.Job
|
||||
|
@ -149,14 +150,6 @@ class ProfileSettingsViewModel(
|
|||
}
|
||||
}
|
||||
|
||||
sealed class AccountProfile {
|
||||
data object Idle: AccountProfile()
|
||||
class Data(
|
||||
val name: String,
|
||||
val icon: ProfileIconView
|
||||
): AccountProfile()
|
||||
}
|
||||
|
||||
class Factory(
|
||||
private val analytics: Analytics,
|
||||
private val container: StorelessSubscriptionContainer,
|
||||
|
|
|
@ -7,7 +7,6 @@ import com.anytypeio.anytype.domain.misc.UrlBuilder
|
|||
sealed class ProfileIconView {
|
||||
object Loading : ProfileIconView()
|
||||
data class Placeholder(val name: String?) : ProfileIconView()
|
||||
data class Emoji(val unicode: String) : ProfileIconView()
|
||||
data class Image(val url: Url) : ProfileIconView()
|
||||
}
|
||||
|
||||
|
@ -19,4 +18,12 @@ fun ObjectWrapper.Basic.profileIcon(builder: UrlBuilder): ProfileIconView = when
|
|||
else -> ProfileIconView.Placeholder(
|
||||
name = name?.trim()?.ifEmpty { null }
|
||||
)
|
||||
}
|
||||
|
||||
sealed class AccountProfile {
|
||||
data object Idle: AccountProfile()
|
||||
class Data(
|
||||
val name: String,
|
||||
val icon: ProfileIconView
|
||||
): AccountProfile()
|
||||
}
|
|
@ -21,6 +21,7 @@ import com.anytypeio.anytype.domain.misc.DeepLinkResolver
|
|||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.domain.multiplayer.SpaceInviteResolver
|
||||
import com.anytypeio.anytype.domain.multiplayer.SpaceViewSubscriptionContainer
|
||||
import com.anytypeio.anytype.domain.search.ProfileSubscriptionManager
|
||||
import com.anytypeio.anytype.domain.spaces.SaveCurrentSpace
|
||||
import com.anytypeio.anytype.domain.vault.GetVaultSettings
|
||||
import com.anytypeio.anytype.domain.vault.ObserveVaultSettings
|
||||
|
@ -34,6 +35,8 @@ import com.anytypeio.anytype.presentation.home.OpenObjectNavigation
|
|||
import com.anytypeio.anytype.presentation.home.navigation
|
||||
import com.anytypeio.anytype.presentation.navigation.DeepLinkToObjectDelegate
|
||||
import com.anytypeio.anytype.presentation.navigation.NavigationViewModel
|
||||
import com.anytypeio.anytype.presentation.profile.AccountProfile
|
||||
import com.anytypeio.anytype.presentation.profile.profileIcon
|
||||
import com.anytypeio.anytype.presentation.spaces.SpaceGradientProvider
|
||||
import com.anytypeio.anytype.presentation.spaces.SpaceIconView
|
||||
import com.anytypeio.anytype.presentation.spaces.spaceIcon
|
||||
|
@ -42,10 +45,13 @@ import javax.inject.Inject
|
|||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.debounce
|
||||
import kotlinx.coroutines.flow.emitAll
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onCompletion
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.flow.take
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
@ -63,12 +69,24 @@ class VaultViewModel(
|
|||
private val analytics: Analytics,
|
||||
private val deepLinkToObjectDelegate: DeepLinkToObjectDelegate,
|
||||
private val appActionManager: AppActionManager,
|
||||
private val spaceInviteResolver: SpaceInviteResolver
|
||||
private val spaceInviteResolver: SpaceInviteResolver,
|
||||
private val profileContainer: ProfileSubscriptionManager
|
||||
) : NavigationViewModel<VaultViewModel.Navigation>(), DeepLinkToObjectDelegate by deepLinkToObjectDelegate {
|
||||
|
||||
val spaces = MutableStateFlow<List<VaultSpaceView>>(emptyList())
|
||||
val commands = MutableSharedFlow<Command>(replay = 0)
|
||||
|
||||
val profileView = profileContainer.observe().map { obj ->
|
||||
AccountProfile.Data(
|
||||
name = obj.name.orEmpty(),
|
||||
icon = obj.profileIcon(urlBuilder)
|
||||
)
|
||||
}.stateIn(
|
||||
viewModelScope,
|
||||
SharingStarted.WhileSubscribed(1000L),
|
||||
AccountProfile.Idle
|
||||
)
|
||||
|
||||
init {
|
||||
Timber.i("VaultViewModel, init")
|
||||
viewModelScope.launch {
|
||||
|
@ -343,7 +361,8 @@ class VaultViewModel(
|
|||
private val analytics: Analytics,
|
||||
private val deepLinkToObjectDelegate: DeepLinkToObjectDelegate,
|
||||
private val appActionManager: AppActionManager,
|
||||
private val spaceInviteResolver: SpaceInviteResolver
|
||||
private val spaceInviteResolver: SpaceInviteResolver,
|
||||
private val profileContainer: ProfileSubscriptionManager
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(
|
||||
|
@ -361,7 +380,8 @@ class VaultViewModel(
|
|||
analytics = analytics,
|
||||
deepLinkToObjectDelegate = deepLinkToObjectDelegate,
|
||||
appActionManager = appActionManager,
|
||||
spaceInviteResolver = spaceInviteResolver
|
||||
spaceInviteResolver = spaceInviteResolver,
|
||||
profileContainer = profileContainer
|
||||
) as T
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue