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 GitHub
parent b2d56c5ec0
commit 7935f40a0a
Signed by: github
GPG key ID: B5690EEEBB952194
8 changed files with 136 additions and 27 deletions

View file

@ -11,6 +11,7 @@ import com.anytypeio.anytype.domain.block.repo.BlockRepository
import com.anytypeio.anytype.domain.config.ConfigStorage
import com.anytypeio.anytype.domain.config.UserSettingsRepository
import com.anytypeio.anytype.domain.debugging.DebugSpace
import com.anytypeio.anytype.domain.icon.RemoveObjectIcon
import com.anytypeio.anytype.domain.icon.SetDocumentImageIcon
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
import com.anytypeio.anytype.domain.misc.UrlBuilder
@ -54,7 +55,8 @@ object ProfileModule {
setDocumentImageIcon: SetDocumentImageIcon,
membershipProvider: MembershipProvider,
getNetworkMode: GetNetworkMode,
profileSubscriptionManager: ProfileSubscriptionManager
profileSubscriptionManager: ProfileSubscriptionManager,
removeObjectIcon: RemoveObjectIcon
): ProfileSettingsViewModel.Factory = ProfileSettingsViewModel.Factory(
analytics = analytics,
container = storelessSubscriptionContainer,
@ -64,7 +66,8 @@ object ProfileModule {
setDocumentImageIcon = setDocumentImageIcon,
membershipProvider = membershipProvider,
getNetworkMode = getNetworkMode,
profileSubscriptionManager = profileSubscriptionManager
profileSubscriptionManager = profileSubscriptionManager,
removeObjectIcon = removeObjectIcon
)
@Provides
@ -83,6 +86,13 @@ object ProfileModule {
spaceManager = spaceManager
)
@Provides
@PerScreen
fun provideRemoveObjectIcon(
repo: BlockRepository,
dispatchers: AppCoroutineDispatchers
): RemoveObjectIcon = RemoveObjectIcon(repo, dispatchers)
@JvmStatic
@PerScreen
@Provides

View file

@ -119,7 +119,8 @@ class ProfileSettingsFragment : BaseBottomSheetComposeFragment() {
findNavController().navigate(R.id.spaceListScreen)
}
}
)
),
clearProfileImage = { vm.onClearProfileImage() }
)
}
}

View file

@ -0,0 +1,19 @@
package com.anytypeio.anytype.domain.icon
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
import com.anytypeio.anytype.domain.base.ResultInteractor
import com.anytypeio.anytype.domain.block.repo.BlockRepository
import com.anytypeio.anytype.domain.icon.RemoveObjectIcon.Params
class RemoveObjectIcon(
private val repository: BlockRepository,
dispatchers: AppCoroutineDispatchers
): ResultInteractor<Params, Unit>(dispatchers.io) {
override suspend fun doWork(params: Params) {
repository.removeDocumentIcon(ctx = params.objectId)
}
data class Params(val objectId: Id)
}

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
}
}

View file

@ -61,6 +61,8 @@
<string name="default_space">Entry space</string>
<string name="space_settings_apply_random_gradient">Apply random solid color</string>
<string name="space_settings_apply_upload_image">Upload image</string>
<string name="profile_settings_apply_upload_image">Upload image</string>
<string name="profile_settings_remove_image">Remove image</string>
<string name="delete_space">Delete space</string>
<string name="you_can_store">You can store up to %1$s of your files on our encrypted backup node for free. If you reach the limit, files will be stored only locally.</string>
<string name="in_order_to_save">In order to save space on your local device, you can offload all your files to our encrypted backup node. The files will be loaded back when you open them.</string>

View file

@ -642,7 +642,8 @@ class HomeScreenViewModel(
.withLatestFrom(spaceManager.observe()) { dispatch, config ->
when (dispatch) {
is WidgetDispatchEvent.SourcePicked.Default -> {
if (dispatch.sourceLayout == ObjectType.Layout.DATE.code) {
if (dispatch.sourceLayout == ObjectType.Layout.DATE.code ||
dispatch.sourceLayout == ObjectType.Layout.PARTICIPANT.code) {
proceedWithCreatingWidget(
ctx = config.widgets,
source = dispatch.source,

View file

@ -208,7 +208,9 @@ class SelectWidgetSourceViewModel(
isInEditMode = curr.isInEditMode
)
}
if (view.layout == ObjectType.Layout.DATE) {
if (view.layout == ObjectType.Layout.DATE ||
view.layout == ObjectType.Layout.PARTICIPANT
) {
isDismissed.value = true
}
}