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

Droid 3375 New settings | Enhancement | Setting wallpaper (#2159)

This commit is contained in:
Evgenii Kozlov 2025-03-14 09:09:29 +01:00 committed by GitHub
parent 50a65b0590
commit b17351edf0
Signed by: github
GPG key ID: B5690EEEBB952194
15 changed files with 201 additions and 103 deletions

View file

@ -10,6 +10,7 @@ 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.ConfigStorage
import com.anytypeio.anytype.domain.config.UserSettingsRepository
import com.anytypeio.anytype.domain.debugging.DebugSpace
import com.anytypeio.anytype.domain.debugging.DebugSpaceContentSaver
import com.anytypeio.anytype.domain.debugging.DebugSpaceShareDownloader
@ -103,6 +104,7 @@ object SpaceSettingsModule {
interface SpaceSettingsDependencies : ComponentDependencies {
fun blockRepo(): BlockRepository
fun auth(): AuthRepository
fun settings(): UserSettingsRepository
fun urlBuilder(): UrlBuilder
fun analytics(): Analytics
fun dispatchers(): AppCoroutineDispatchers

View file

@ -151,6 +151,13 @@ class SpaceSettingsFragment : BaseBottomSheetComposeFragment() {
Timber.e(it, "Error while exiting to vault screen from space settings")
}
}
is Command.OpenWallpaperPicker -> {
runCatching {
findNavController().navigate(R.id.wallpaperSetScreen)
}.onFailure {
Timber.e(it, "Error while opening space wallpaper picker")
}
}
}
}
}

View file

@ -1,7 +1,6 @@
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
@ -46,7 +45,6 @@ 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
@ -57,6 +55,7 @@ import com.anytypeio.anytype.core_models.ext.EMPTY_STRING_VALUE
import com.anytypeio.anytype.core_models.multiplayer.SpaceAccessType
import com.anytypeio.anytype.core_ui.common.DefaultPreviews
import com.anytypeio.anytype.core_ui.features.SpaceIconView
import com.anytypeio.anytype.core_ui.features.wallpaper.gradient
import com.anytypeio.anytype.core_ui.foundation.noRippleClickable
import com.anytypeio.anytype.core_ui.foundation.util.DraggableItem
import com.anytypeio.anytype.core_ui.foundation.util.dragContainer
@ -72,11 +71,8 @@ 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
@ -298,7 +294,6 @@ fun VaultSpaceCard(
Modifier
}
}
is Wallpaper.Gradient -> {
Modifier.background(
brush = Brush.verticalGradient(
@ -310,7 +305,6 @@ fun VaultSpaceCard(
shape = RoundedCornerShape(20.dp)
)
}
is Wallpaper.Default -> {
Modifier.background(
brush = Brush.verticalGradient(

View file

@ -41,6 +41,7 @@ import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.ObjectWrapper
import com.anytypeio.anytype.core_models.Relations
import com.anytypeio.anytype.core_ui.features.wallpaper.gradient
import com.anytypeio.anytype.core_ui.foundation.noRippleClickable
import com.anytypeio.anytype.core_ui.views.Caption1Medium
import com.anytypeio.anytype.core_ui.views.PreviewTitle2Medium
@ -572,67 +573,6 @@ private fun GalleryWidgetItemCard(
}
}
fun gradient(
gradient: String,
alpha: Float = 1.0f
) : List<Color> {
return when(gradient) {
CoverGradient.YELLOW -> {
return listOf(
Color(0xFFecd91b).copy(alpha = alpha),
Color(0xFFffb522).copy(alpha = alpha)
)
}
CoverGradient.RED -> {
return listOf(
Color(0xFFe51ca0).copy(alpha = alpha),
Color(0xFFf55522).copy(alpha = alpha)
)
}
CoverGradient.BLUE -> {
return listOf(
Color(0xFF3e58eb).copy(alpha = alpha),
Color(0xFFab50cc).copy(alpha = alpha)
)
}
CoverGradient.TEAL -> {
return listOf(
Color(0xFF0fc8ba).copy(alpha = alpha),
Color(0xFF2aa7ee).copy(alpha = alpha)
)
}
CoverGradient.PINK_ORANGE -> {
return listOf(
Color(0xFFD8A4E1).copy(alpha = alpha),
Color(0xFFFDD0CD).copy(alpha = alpha),
Color(0xFFFFCC81).copy(alpha = alpha)
)
}
CoverGradient.BLUE_PINK -> {
return listOf(
Color(0xFF73B7F0).copy(alpha = alpha),
Color(0xFFABB6ED).copy(alpha = alpha),
Color(0xFFF3BFAC).copy(alpha = alpha)
)
}
CoverGradient.GREEN_ORANGE -> {
return listOf(
Color(0xFF63B3CB).copy(alpha = alpha),
Color(0xFFC5D3AC).copy(alpha = alpha),
Color(0xFFF6C47A).copy(alpha = alpha)
)
}
CoverGradient.SKY -> {
return listOf(
Color(0xFF6EB6E4).copy(alpha = alpha),
Color(0xFFA4CFEC).copy(alpha = alpha),
Color(0xFFDAEAF3).copy(alpha = alpha)
)
}
else -> return emptyList()
}
}
@Preview(showBackground = true, uiMode = UI_MODE_NIGHT_YES, name = "Light Mode")
@Preview(showBackground = true, uiMode = UI_MODE_NIGHT_NO, name = "Dark Mode")
@Composable

View file

@ -149,4 +149,5 @@ class WallpaperSelectAdapter(
binding.gradient.background.alpha = WallpaperView.WALLPAPER_DEFAULT_ALPHA
}
}
}
}

View file

@ -0,0 +1,65 @@
package com.anytypeio.anytype.core_ui.features.wallpaper
import androidx.compose.ui.graphics.Color
import com.anytypeio.anytype.presentation.editor.cover.CoverGradient
fun gradient(
gradient: String,
alpha: Float = 1.0f
) : List<Color> {
return when(gradient) {
CoverGradient.YELLOW -> {
return listOf(
Color(0xFFecd91b).copy(alpha = alpha),
Color(0xFFffb522).copy(alpha = alpha)
)
}
CoverGradient.RED -> {
return listOf(
Color(0xFFe51ca0).copy(alpha = alpha),
Color(0xFFf55522).copy(alpha = alpha)
)
}
CoverGradient.BLUE -> {
return listOf(
Color(0xFF3e58eb).copy(alpha = alpha),
Color(0xFFab50cc).copy(alpha = alpha)
)
}
CoverGradient.TEAL -> {
return listOf(
Color(0xFF0fc8ba).copy(alpha = alpha),
Color(0xFF2aa7ee).copy(alpha = alpha)
)
}
CoverGradient.PINK_ORANGE -> {
return listOf(
Color(0xFFD8A4E1).copy(alpha = alpha),
Color(0xFFFDD0CD).copy(alpha = alpha),
Color(0xFFFFCC81).copy(alpha = alpha)
)
}
CoverGradient.BLUE_PINK -> {
return listOf(
Color(0xFF73B7F0).copy(alpha = alpha),
Color(0xFFABB6ED).copy(alpha = alpha),
Color(0xFFF3BFAC).copy(alpha = alpha)
)
}
CoverGradient.GREEN_ORANGE -> {
return listOf(
Color(0xFF63B3CB).copy(alpha = alpha),
Color(0xFFC5D3AC).copy(alpha = alpha),
Color(0xFFF6C47A).copy(alpha = alpha)
)
}
CoverGradient.SKY -> {
return listOf(
Color(0xFF6EB6E4).copy(alpha = alpha),
Color(0xFFA4CFEC).copy(alpha = alpha),
Color(0xFFDAEAF3).copy(alpha = alpha)
)
}
else -> return emptyList()
}
}

View file

@ -9,7 +9,7 @@ import javax.inject.Inject
class GetSpaceWallpapers @Inject constructor(
private val repo: UserSettingsRepository,
private val dispatchers: AppCoroutineDispatchers
dispatchers: AppCoroutineDispatchers
) : ResultInteractor<Unit, Map<Id, Wallpaper>>(dispatchers.io) {
override suspend fun doWork(params: Unit): Map<Id, Wallpaper> {

View file

@ -3,9 +3,10 @@ package com.anytypeio.anytype.domain.wallpaper
import com.anytypeio.anytype.core_models.Wallpaper
import com.anytypeio.anytype.domain.base.BaseUseCase
import com.anytypeio.anytype.domain.base.FlowUseCase
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
class ObserveWallpaper() : FlowUseCase<Wallpaper, BaseUseCase.None>() {
class ObserveWallpaper @Inject constructor () : FlowUseCase<Wallpaper, BaseUseCase.None>() {
override fun build(params: BaseUseCase.None?): Flow<Wallpaper> {
return WallpaperStore.Default.observe()
}

View file

@ -4,6 +4,7 @@ import com.anytypeio.anytype.core_models.Wallpaper
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
// TODo store data about wallpaper's space
interface WallpaperStore {
fun set(wallpaper: Wallpaper)
fun get() : Wallpaper

View file

@ -30,6 +30,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.platform.LocalFocusManager
@ -43,7 +44,9 @@ import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.anytypeio.anytype.core_models.Wallpaper
import com.anytypeio.anytype.core_ui.extensions.light
import com.anytypeio.anytype.core_ui.features.wallpaper.gradient
import com.anytypeio.anytype.core_ui.foundation.Section
import com.anytypeio.anytype.core_ui.foundation.noRippleThrottledClickable
import com.anytypeio.anytype.core_ui.views.BodyBold
@ -52,6 +55,7 @@ import com.anytypeio.anytype.core_ui.views.BodyRegular
import com.anytypeio.anytype.core_ui.views.Caption1Regular
import com.anytypeio.anytype.core_ui.views.PreviewTitle1Regular
import com.anytypeio.anytype.core_ui.widgets.ListWidgetObjectIcon
import com.anytypeio.anytype.presentation.editor.cover.CoverGradient
import com.anytypeio.anytype.presentation.objects.ObjectIcon
import com.anytypeio.anytype.presentation.spaces.UiEvent
import com.anytypeio.anytype.presentation.spaces.UiSpaceSettingsItem
@ -61,7 +65,6 @@ import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.dropWhile
import kotlinx.coroutines.flow.filter
import timber.log.Timber
@Composable
fun MembersItem(
@ -155,15 +158,54 @@ fun WallpaperItem(
style = PreviewTitle1Regular,
color = colorResource(id = R.color.text_primary),
)
Box(
modifier = Modifier
.size(20.dp)
.background(
color = light(item.color),
shape = RoundedCornerShape(4.dp)
when(val wallpaper = item.current) {
is Wallpaper.Color -> {
Box(
modifier = Modifier
.size(20.dp)
.background(
color = light(wallpaper.code),
shape = RoundedCornerShape(4.dp)
)
.padding(horizontal = 8.dp),
)
.padding(horizontal = 6.dp),
)
}
is Wallpaper.Gradient -> {
Box(
modifier = Modifier
.size(20.dp)
.background(
brush = Brush.verticalGradient(
colors = gradient(
gradient = wallpaper.code,
alpha = 0.3f
)
),
shape = RoundedCornerShape(20.dp)
)
.padding(horizontal = 8.dp),
)
}
is Wallpaper.Default -> {
Box(
modifier = Modifier
.size(20.dp)
.background(
brush = Brush.verticalGradient(
colors = gradient(
gradient = CoverGradient.SKY,
alpha = 0.3f
)
),
shape = RoundedCornerShape(20.dp)
)
.padding(horizontal = 8.dp),
)
}
else -> {
// Do nothing.
}
}
Image(
painter = painterResource(id = R.drawable.ic_disclosure_8_24),
contentDescription = "Members icon",

View file

@ -265,7 +265,12 @@ fun NewSpaceSettingsScreen(
WallpaperItem(
modifier = Modifier
.fillMaxWidth()
.animateItem(),
.animateItem()
.clip(RoundedCornerShape(16.dp))
.clickable {
uiEvent(UiEvent.OnWallpaperClicked)
}
,
item = item
)
}

View file

@ -35,7 +35,7 @@ fun NewSpaceSettingsScreenPreview() {
icon = ObjectIcon.Empty.ObjectType
),
UiSpaceSettingsItem.Spacer(height = 8),
UiSpaceSettingsItem.Wallpapers(color = ThemeColor.TEAL),
UiSpaceSettingsItem.Wallpapers(null),
UiSpaceSettingsItem.Spacer(height = 8),
UiSpaceSettingsItem.Section.DataManagement,
UiSpaceSettingsItem.Spacer(height = 8),

View file

@ -1,5 +1,6 @@
package com.anytypeio.anytype.presentation.spaces
import android.R
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
@ -14,12 +15,15 @@ import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.ObjectWrapper
import com.anytypeio.anytype.core_models.Relations
import com.anytypeio.anytype.core_models.SpaceType
import com.anytypeio.anytype.core_models.ThemeColor
import com.anytypeio.anytype.core_models.multiplayer.ParticipantStatus
import com.anytypeio.anytype.core_models.multiplayer.SpaceMemberPermissions
import com.anytypeio.anytype.core_models.primitives.SpaceId
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.debugging.DebugSpaceShareDownloader
import com.anytypeio.anytype.domain.launch.GetDefaultObjectType
import com.anytypeio.anytype.domain.media.UploadFile
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.multiplayer.ActiveSpaceMemberSubscriptionContainer
@ -30,6 +34,8 @@ import com.anytypeio.anytype.domain.payments.GetMembershipStatus
import com.anytypeio.anytype.domain.search.ProfileSubscriptionManager
import com.anytypeio.anytype.domain.spaces.DeleteSpace
import com.anytypeio.anytype.domain.spaces.SetSpaceDetails
import com.anytypeio.anytype.domain.wallpaper.GetSpaceWallpapers
import com.anytypeio.anytype.domain.wallpaper.ObserveWallpaper
import com.anytypeio.anytype.domain.workspace.SpaceManager
import com.anytypeio.anytype.presentation.common.BaseViewModel
import com.anytypeio.anytype.presentation.spaces.UiSpaceSettingsItem.Spacer
@ -37,6 +43,7 @@ import javax.inject.Inject
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import timber.log.Timber
@ -57,7 +64,9 @@ class SpaceSettingsViewModel(
private val activeSpaceMemberSubscriptionContainer: ActiveSpaceMemberSubscriptionContainer,
private val getMembership: GetMembershipStatus,
private val uploadFile: UploadFile,
private val profileContainer: ProfileSubscriptionManager
private val profileContainer: ProfileSubscriptionManager,
private val getDefaultObjectType: GetDefaultObjectType,
private val observeWallpaper: ObserveWallpaper
): BaseViewModel() {
val commands = MutableSharedFlow<Command>()
@ -78,27 +87,38 @@ class SpaceSettingsViewModel(
}
private fun proceedWithObservingSpaceView() {
val restrictions = combine(
userPermissionProvider.observe(params.space),
spaceViewContainer.sharedSpaceCount(userPermissionProvider.all()),
profileContainer
.observe()
.map { wrapper ->
wrapper.getValue<Double?>(Relations.SHARED_SPACES_LIMIT)?.toInt() ?: 0
},
) { permission, sharedSpaceCount, sharedSpaceLimit ->
Triple(permission, sharedSpaceCount, sharedSpaceLimit)
}
val otherFlows = combine(
spaceViewContainer.observe(params.space),
activeSpaceMemberSubscriptionContainer.observe(params.space),
observeWallpaper.build()
) { spaceView, spaceMembers, wallpaper ->
Triple(spaceView, spaceMembers, wallpaper)
}
viewModelScope.launch {
combine(
spaceViewContainer.observe(params.space),
userPermissionProvider.observe(params.space),
profileContainer
.observe()
.map { wrapper ->
wrapper.getValue<Double?>(Relations.SHARED_SPACES_LIMIT)?.toInt() ?: 0
},
spaceViewContainer.sharedSpaceCount(userPermissionProvider.all()),
activeSpaceMemberSubscriptionContainer.observe(params.space),
) { spaceView, permission, sharedSpaceLimit: Int, sharedSpaceCount: Int, store ->
restrictions,
otherFlows
) { (permission, sharedSpaceCount, sharedSpaceLimit), (spaceView, spaceMembers, wallpaper) ->
Timber.d("Got shared space limit: $sharedSpaceLimit, shared space count: $sharedSpaceCount")
val requests: Int = if (store is ActiveSpaceMemberSubscriptionContainer.Store.Data) {
store.members.count { it.status == ParticipantStatus.JOINING }
} else {
0
}
val spaceMember = if (store is ActiveSpaceMemberSubscriptionContainer.Store.Data) {
store.members.find { it.id == spaceView.getValue<Id>(Relations.CREATOR) }
val spaceMember = if (spaceMembers is ActiveSpaceMemberSubscriptionContainer.Store.Data) {
spaceMembers.members.find { it.id == spaceView.getValue<Id>(Relations.CREATOR) }
} else {
null
}
@ -107,6 +127,12 @@ class SpaceSettingsViewModel(
//todo add logic
val membersNumber = 3
val requests: Int = if (spaceMembers is ActiveSpaceMemberSubscriptionContainer.Store.Data) {
spaceMembers.members.count { it.status == ParticipantStatus.JOINING }
} else {
0
}
val spaceTechInfo = SpaceTechInfo(
spaceId = params.space,
createdBy = createdBy.orEmpty(),
@ -139,6 +165,8 @@ class SpaceSettingsViewModel(
Spacer(height = 8),
UiSpaceSettingsItem.Section.Collaboration,
UiSpaceSettingsItem.Members(count = membersNumber),
UiSpaceSettingsItem.Section.Preferences,
UiSpaceSettingsItem.Wallpapers(current = wallpaper),
UiSpaceSettingsItem.Section.Misc,
UiSpaceSettingsItem.SpaceInfo
),
@ -205,6 +233,11 @@ class SpaceSettingsViewModel(
is UiEvent.OnSpaceImagePicked -> {
proceedWithSettingSpaceImage(uiEvent.uri)
}
is UiEvent.OnWallpaperClicked -> {
viewModelScope.launch {
commands.emit(Command.OpenWallpaperPicker)
}
}
}
}
@ -459,6 +492,7 @@ class SpaceSettingsViewModel(
data object ShowShareLimitReachedError : Command()
data object NavigateToMembership : Command()
data object NavigateToMembershipUpdate : Command()
data object OpenWallpaperPicker : Command()
}
class Factory @Inject constructor(
@ -477,7 +511,9 @@ class SpaceSettingsViewModel(
private val activeSpaceMemberSubscriptionContainer: ActiveSpaceMemberSubscriptionContainer,
private val getMembership: GetMembershipStatus,
private val uploadFile: UploadFile,
private val profileContainer: ProfileSubscriptionManager
private val profileContainer: ProfileSubscriptionManager,
private val getDefaultObjectType: GetDefaultObjectType,
private val observeWallpaper: ObserveWallpaper
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(
@ -498,7 +534,9 @@ class SpaceSettingsViewModel(
activeSpaceMemberSubscriptionContainer = activeSpaceMemberSubscriptionContainer,
getMembership = getMembership,
uploadFile = uploadFile,
profileContainer = profileContainer
profileContainer = profileContainer,
getDefaultObjectType = getDefaultObjectType,
observeWallpaper = observeWallpaper
) as T
}

View file

@ -11,6 +11,7 @@ sealed class UiEvent {
data class OnSpaceImagePicked(val uri: String) : UiEvent()
data object OnInviteClicked : UiEvent()
data object OnQrCodeClicked : UiEvent()
data object OnWallpaperClicked : UiEvent()
sealed class IconMenu : UiEvent() {
data object OnRemoveIconClicked : IconMenu()

View file

@ -2,6 +2,7 @@ package com.anytypeio.anytype.presentation.spaces
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.ThemeColor
import com.anytypeio.anytype.core_models.Wallpaper
import com.anytypeio.anytype.core_models.ext.EMPTY_STRING_VALUE
import com.anytypeio.anytype.core_models.primitives.SpaceId
import com.anytypeio.anytype.presentation.objects.ObjectIcon
@ -46,7 +47,7 @@ sealed class UiSpaceSettingsItem {
data class Chat(val isOn: Boolean) : UiSpaceSettingsItem()
data object ObjectTypes : UiSpaceSettingsItem()
data class DefaultObjectType(val name: String, val icon: ObjectIcon) : UiSpaceSettingsItem()
data class Wallpapers(val color: ThemeColor) : UiSpaceSettingsItem()
data class Wallpapers(val current: Wallpaper?) : UiSpaceSettingsItem()
data class RemoteStorage(val size: Int) : UiSpaceSettingsItem()
data object SpaceInfo : UiSpaceSettingsItem()
data object DeleteSpace : UiSpaceSettingsItem()