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

DROID-3067 Profile | Fix | Profile image menu (#2026)

Co-authored-by: Evgenii Kozlov <enklave.mare.balticum@protonmail.com>
Co-authored-by: Evgenii Kozlov <ubuphobos@gmail.com>
This commit is contained in:
Konstantin Ivanov 2025-01-22 23:25:18 +01:00 committed by Evgenii Kozlov
parent 7f627dacf2
commit fa17cd15b3
8 changed files with 136 additions and 27 deletions

View file

@ -1,5 +1,6 @@
package com.anytypeio.anytype.ui_settings.account
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
@ -7,7 +8,6 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
@ -20,6 +20,8 @@ import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.DropdownMenu
import androidx.compose.material.DropdownMenuItem
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
@ -35,10 +37,10 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
@ -46,10 +48,10 @@ import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import coil.compose.rememberAsyncImagePainter
import com.anytypeio.anytype.core_ui.foundation.Arrow
import com.anytypeio.anytype.core_ui.foundation.Divider
import com.anytypeio.anytype.core_ui.foundation.Dragger
import com.anytypeio.anytype.core_ui.foundation.Option
@ -81,7 +83,8 @@ fun ProfileSettingsScreen(
onSpacesClicked: () -> Unit,
onMembershipClicked: () -> Unit,
membershipStatus: MembershipStatus?,
showMembership: ShowMembership?
showMembership: ShowMembership?,
clearProfileImage: () -> Unit
) {
LazyColumn(
modifier = Modifier
@ -93,7 +96,8 @@ fun ProfileSettingsScreen(
Header(
account = account,
onNameSet = onNameChange,
onProfileIconClick = onProfileIconClick
onProfileIconClick = onProfileIconClick,
clearProfileImage = clearProfileImage
)
}
item {
@ -272,7 +276,8 @@ private fun Header(
modifier: Modifier = Modifier,
account: AccountProfile,
onProfileIconClick: () -> Unit,
onNameSet: (String) -> Unit
onNameSet: (String) -> Unit,
clearProfileImage: () -> Unit
) {
when (account) {
is AccountProfile.Data -> {
@ -286,7 +291,8 @@ private fun Header(
ProfileImageBlock(
name = account.name,
icon = account.icon,
onProfileIconClick = onProfileIconClick
onProfileIconClick = onProfileIconClick,
clearProfileImage = clearProfileImage
)
}
ProfileNameBlock(name = account.name, onNameSet = onNameSet)
@ -396,8 +402,15 @@ fun ProfileTitleBlock() {
fun ProfileImageBlock(
name: String,
icon: ProfileIconView,
onProfileIconClick: () -> Unit
onProfileIconClick: () -> Unit,
clearProfileImage: () -> Unit
) {
val isIconMenuExpanded = remember {
mutableStateOf(false)
}
val context = LocalContext.current
when (icon) {
is ProfileIconView.Image -> {
Image(
@ -408,7 +421,7 @@ fun ProfileImageBlock(
.size(96.dp)
.clip(RoundedCornerShape(48.dp))
.noRippleClickable {
onProfileIconClick.invoke()
isIconMenuExpanded.value = !isIconMenuExpanded.value
}
)
}
@ -438,6 +451,48 @@ fun ProfileImageBlock(
}
}
}
MaterialTheme(
shapes = MaterialTheme.shapes.copy(medium = RoundedCornerShape(16.dp))
) {
DropdownMenu(
expanded = isIconMenuExpanded.value,
offset = DpOffset(x = 0.dp, y = 6.dp),
onDismissRequest = {
isIconMenuExpanded.value = false
}
) {
if (ActivityResultContracts.PickVisualMedia.isPhotoPickerAvailable(context)) {
androidx.compose.material.Divider(
thickness = 0.5.dp,
color = colorResource(id = R.color.shape_primary)
)
DropdownMenuItem(
onClick = {
onProfileIconClick.invoke()
isIconMenuExpanded.value = false
},
) {
Text(
text = stringResource(R.string.profile_settings_apply_upload_image),
style = BodyRegular,
color = colorResource(id = R.color.text_primary)
)
}
}
DropdownMenuItem(
onClick = {
isIconMenuExpanded.value = false
clearProfileImage.invoke()
},
) {
Text(
text = stringResource(R.string.profile_settings_remove_image),
style = BodyRegular,
color = colorResource(id = R.color.text_primary)
)
}
}
}
}
@Preview
@ -459,7 +514,8 @@ private fun ProfileSettingPreview() {
onSpacesClicked = {},
onMembershipClicked = {},
membershipStatus = null,
showMembership = ShowMembership(true)
showMembership = ShowMembership(true),
clearProfileImage = {}
)
}

View file

@ -7,29 +7,23 @@ import androidx.lifecycle.viewModelScope
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.analytics.base.EventsDictionary
import com.anytypeio.anytype.analytics.base.sendEvent
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.NetworkMode
import com.anytypeio.anytype.core_models.Relations
import com.anytypeio.anytype.core_models.membership.MembershipStatus
import com.anytypeio.anytype.core_models.primitives.SpaceId
import com.anytypeio.anytype.domain.account.DeleteAccount
import com.anytypeio.anytype.domain.base.BaseUseCase
import com.anytypeio.anytype.domain.base.fold
import com.anytypeio.anytype.domain.config.ConfigStorage
import com.anytypeio.anytype.domain.icon.RemoveObjectIcon
import com.anytypeio.anytype.domain.icon.SetDocumentImageIcon
import com.anytypeio.anytype.domain.icon.SetImageIcon
import com.anytypeio.anytype.domain.library.StoreSearchByIdsParams
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.networkmode.GetNetworkMode
import com.anytypeio.anytype.domain.`object`.SetObjectDetails
import com.anytypeio.anytype.domain.search.PROFILE_SUBSCRIPTION_ID
import com.anytypeio.anytype.presentation.common.BaseViewModel
import com.anytypeio.anytype.presentation.extension.sendScreenSettingsDeleteEvent
import com.anytypeio.anytype.core_models.membership.MembershipStatus
import com.anytypeio.anytype.domain.search.ProfileSubscriptionManager
import com.anytypeio.anytype.presentation.common.BaseViewModel
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
import kotlinx.coroutines.flow.MutableStateFlow
@ -48,7 +42,8 @@ class ProfileSettingsViewModel(
private val setImageIcon: SetDocumentImageIcon,
private val membershipProvider: MembershipProvider,
private val getNetworkMode: GetNetworkMode,
private val profileContainer: ProfileSubscriptionManager
private val profileContainer: ProfileSubscriptionManager,
private val removeObjectIcon: RemoveObjectIcon
) : BaseViewModel() {
private val jobs = mutableListOf<Job>()
@ -150,6 +145,27 @@ class ProfileSettingsViewModel(
}
}
fun onClearProfileImage() {
viewModelScope.launch {
val config = configStorage.getOrNull()
if (config != null) {
val params = RemoveObjectIcon.Params(objectId = config.profile)
removeObjectIcon.async(
params = params
).fold(
onFailure = {
Timber.e("Error while removing profile image")
},
onSuccess = {
// do nothing
}
)
} else {
Timber.e("Missing config while trying to unset profile image")
}
}
}
class Factory(
private val analytics: Analytics,
private val container: StorelessSubscriptionContainer,
@ -159,7 +175,8 @@ class ProfileSettingsViewModel(
private val setDocumentImageIcon: SetDocumentImageIcon,
private val membershipProvider: MembershipProvider,
private val getNetworkMode: GetNetworkMode,
private val profileSubscriptionManager: ProfileSubscriptionManager
private val profileSubscriptionManager: ProfileSubscriptionManager,
private val removeObjectIcon: RemoveObjectIcon
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
@ -172,7 +189,8 @@ class ProfileSettingsViewModel(
setImageIcon = setDocumentImageIcon,
membershipProvider = membershipProvider,
getNetworkMode = getNetworkMode,
profileContainer = profileSubscriptionManager
profileContainer = profileSubscriptionManager,
removeObjectIcon = removeObjectIcon
) as T
}
}