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

DROID-1643 App | Enhancement | Display space gradient for profile icon if it is set as icon option — in search, widgets, editor, etc. (#429)

This commit is contained in:
Evgenii Kozlov 2023-10-11 16:13:41 +02:00 committed by uburoiubu
parent 324d20e3f9
commit 852f9cd907
No known key found for this signature in database
GPG key ID: C8FB80E0A595FBB6
30 changed files with 285 additions and 52 deletions

View file

@ -236,7 +236,7 @@ object SetupNewAccountModule {
@JvmStatic
@Provides
@PerScreen
fun gradientProvider(): SpaceGradientProvider = SpaceGradientProvider.Impl()
fun gradientProvider(): SpaceGradientProvider = SpaceGradientProvider.Default
@JvmStatic
@Provides

View file

@ -204,7 +204,7 @@ object HomeScreenModule {
@JvmStatic
@Provides
@PerScreen
fun gradientProvider(): SpaceGradientProvider = SpaceGradientProvider.Impl()
fun gradientProvider(): SpaceGradientProvider = SpaceGradientProvider.Default
@Module
interface Declarations {

View file

@ -54,7 +54,7 @@ object OnboardingSoulCreationAnimModule {
@JvmStatic
@Provides
@SoulCreationAnimScreenScope
fun provideSpaceGradientProvider(): SpaceGradientProvider = SpaceGradientProvider.Impl()
fun provideSpaceGradientProvider(): SpaceGradientProvider = SpaceGradientProvider.Default
@Module
interface Declarations {

View file

@ -49,7 +49,7 @@ object OnboardingVoidModule {
@JvmStatic
@Provides
@PerScreen
fun gradientProvider(): SpaceGradientProvider = SpaceGradientProvider.Impl()
fun gradientProvider(): SpaceGradientProvider = SpaceGradientProvider.Default
@JvmStatic
@Provides

View file

@ -67,7 +67,7 @@ object FilesStorageModule {
@JvmStatic
@Provides
@PerScreen
fun provideSpaceGradientProvider(): SpaceGradientProvider = SpaceGradientProvider.Impl()
fun provideSpaceGradientProvider(): SpaceGradientProvider = SpaceGradientProvider.Default
@JvmStatic
@Provides

View file

@ -56,7 +56,7 @@ object MainSettingsModule {
@JvmStatic
@Provides
@PerScreen
fun provideSpaceGradientProvider(): SpaceGradientProvider = SpaceGradientProvider.Impl()
fun provideSpaceGradientProvider(): SpaceGradientProvider = SpaceGradientProvider.Default
@JvmStatic
@Provides

View file

@ -62,7 +62,7 @@ object ProfileModule {
@Provides
@PerScreen
fun provideSpaceGradientProvider(): SpaceGradientProvider = SpaceGradientProvider.Impl()
fun provideSpaceGradientProvider(): SpaceGradientProvider = SpaceGradientProvider.Default
@Provides
@PerScreen

View file

@ -29,6 +29,7 @@ import com.anytypeio.anytype.core_models.Url
import com.anytypeio.anytype.core_ui.foundation.noRippleClickable
import com.anytypeio.anytype.emojifier.Emojifier
import com.anytypeio.anytype.presentation.objects.ObjectIcon
import com.anytypeio.anytype.ui_settings.main.GradientComposeView
@Composable
fun TreeWidgetObjectIcon(
@ -67,6 +68,14 @@ fun TreeWidgetObjectIcon(
modifier = modifier.padding(start = paddingStart, end = paddingEnd)
)
}
is ObjectIcon.Profile.Gradient -> {
GradientComposeView(
modifier = modifier.padding(start = paddingStart, end = paddingEnd),
from = icon.from,
to = icon.to,
size = 18.dp
)
}
is ObjectIcon.Basic.Emoji -> {
UriImage(
uri = Emojifier.safeUri(icon.unicode),
@ -127,6 +136,12 @@ fun ListWidgetObjectIcon(
when (icon) {
is ObjectIcon.Profile.Avatar -> DefaultProfileAvatarIcon(modifier, iconSize, icon)
is ObjectIcon.Profile.Image -> defaultProfileIconImage(icon, modifier, iconSize)
is ObjectIcon.Profile.Gradient -> GradientComposeView(
modifier = modifier,
from = icon.from,
to = icon.to,
size = iconSize
)
is ObjectIcon.Basic.Emoji -> DefaultEmojiObjectIcon(modifier, iconSize, icon)
is ObjectIcon.Basic.Image -> DefaultObjectImageIcon(icon.hash, modifier, iconSize)
is ObjectIcon.Bookmark -> DefaultObjectBookmarkIcon(icon.image, modifier, iconSize)

View file

@ -31,6 +31,7 @@ data class Block(
val featuredRelations: List<String>? by default
val name: String? by default
val iconEmoji: String? by default
val iconOption: Double? by default
val coverId: String? by default
val coverType: Double? by default
val iconImage: String? by default

View file

@ -5,6 +5,9 @@ import android.view.View
import android.view.inputmethod.InputMethodManager
import android.widget.ImageView
import android.widget.TextView
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.unit.dp
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.postDelayed
import androidx.core.view.updateLayoutParams
@ -20,6 +23,7 @@ import com.anytypeio.anytype.core_ui.features.editor.BlockViewDiffUtil
import com.anytypeio.anytype.core_ui.features.editor.BlockViewHolder
import com.anytypeio.anytype.core_ui.features.editor.holders.`interface`.TextHolder
import com.anytypeio.anytype.core_ui.tools.DefaultSpannableFactory
import com.anytypeio.anytype.core_ui.widgets.RadialGradientComposeView
import com.anytypeio.anytype.core_ui.widgets.text.TextInputWidget
import com.anytypeio.anytype.core_utils.ext.dimen
import com.anytypeio.anytype.core_utils.ext.gone
@ -381,6 +385,10 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder {
override val content: TextInputWidget = binding.title
override val selectionView: View = itemView
val gradientView : ComposeView get() = binding
.docProfileIconContainer
.findViewById(R.id.gradient)
private val iconText = binding.imageText
private var hasImage = false
@ -407,6 +415,7 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder {
override fun setImage(item: BlockView.Title) {
item.image?.let { url ->
iconText.text = ""
gradientView.gone()
hasImage = true
image.visible()
Glide
@ -417,8 +426,23 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder {
.into(image)
} ?: apply {
hasImage = false
setIconText(item.text)
image.setImageDrawable(null)
if (item is BlockView.Title.Profile && item.spaceGradient != null) {
val gradient = item.spaceGradient
requireNotNull(gradient)
gradientView.visible()
gradientView.setContent {
RadialGradientComposeView(
modifier = Modifier,
from = gradient.from,
to = gradient.to,
size = 0.dp
)
}
} else {
gradientView.gone()
setIconText(item.text)
image.setImageDrawable(null)
}
}
}

View file

@ -8,6 +8,9 @@ import android.view.LayoutInflater
import android.view.View
import android.widget.FrameLayout
import android.widget.ImageView
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.compose.ui.unit.dp
import androidx.core.view.updateLayoutParams
import com.anytypeio.anytype.core_models.Url
import com.anytypeio.anytype.core_ui.R
@ -139,6 +142,7 @@ class ObjectIconWidget @JvmOverloads constructor(
is ObjectIcon.Basic.Avatar -> setBasicInitials(icon.name)
is ObjectIcon.Profile.Avatar -> setProfileInitials(icon.name)
is ObjectIcon.Profile.Image -> setCircularImage(icon.hash)
is ObjectIcon.Profile.Gradient -> setSpaceGradientView(icon)
is ObjectIcon.Task -> setCheckbox(icon.isChecked)
is ObjectIcon.Bookmark -> setBookmark(icon.image)
is ObjectIcon.None -> removeIcon()
@ -149,6 +153,30 @@ class ObjectIconWidget @JvmOverloads constructor(
}
}
private fun setSpaceGradientView(icon: ObjectIcon.Profile.Gradient) {
with(binding) {
ivCheckbox.gone()
initialContainer.gone()
emojiContainer.gone()
ivBookmark.gone()
ivImage.gone()
ivBookmark.setImageDrawable(null)
ivEmoji.setImageDrawable(null)
}
binding.composeView.visible()
binding.composeView.apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
RadialGradientComposeView(
modifier = Modifier,
from = icon.from,
to = icon.to,
size = 0.dp
)
}
}
}
fun setIcon(
emoji: String?,
image: Url?,
@ -177,6 +205,7 @@ class ObjectIconWidget @JvmOverloads constructor(
ivBookmark.setImageDrawable(null)
ivBookmark.gone()
initialContainer.visible()
composeView.gone()
if (initialContainer.background == null) {
initialContainer.setBackgroundResource(R.drawable.object_in_list_background_profile_initial)
}
@ -195,6 +224,7 @@ class ObjectIconWidget @JvmOverloads constructor(
ivBookmark.setImageDrawable(null)
ivBookmark.gone()
initialContainer.visible()
composeView.gone()
if (initialContainer.background == null) {
initialContainer.setBackgroundResource(R.drawable.object_in_list_background_basic_initial)
}
@ -213,6 +243,7 @@ class ObjectIconWidget @JvmOverloads constructor(
ivBookmark.setImageDrawable(null)
ivBookmark.gone()
emojiContainer.visible()
composeView.gone()
}
try {
Glide
@ -238,6 +269,7 @@ class ObjectIconWidget @JvmOverloads constructor(
initialContainer.invisible()
ivBookmark.invisible()
emojiContainer.invisible()
composeView.gone()
}
}
@ -251,6 +283,7 @@ class ObjectIconWidget @JvmOverloads constructor(
ivBookmark.setImageDrawable(null)
ivBookmark.gone()
ivImage.setCircularShape()
composeView.gone()
if (isImageWithCorners) {
ivImage.setStrokeWidthResource(R.dimen.dp_2)
ivImage.strokeColor =
@ -277,6 +310,7 @@ class ObjectIconWidget @JvmOverloads constructor(
ivBookmark.setImageDrawable(null)
ivImage.visible()
ivImage.setCorneredShape(imageCornerRadius)
composeView.gone()
if (isImageWithCorners) {
ivImage.setStrokeWidthResource(R.dimen.dp_2)
ivImage.strokeColor =
@ -295,6 +329,7 @@ class ObjectIconWidget @JvmOverloads constructor(
fun setImageDrawable(drawable: Drawable) {
with(binding) {
composeView.gone()
ivCheckbox.invisible()
initialContainer.invisible()
ivImage.invisible()
@ -307,6 +342,7 @@ class ObjectIconWidget @JvmOverloads constructor(
fun setCheckbox(isChecked: Boolean?) {
with(binding) {
composeView.gone()
ivCheckbox.visible()
ivCheckbox.isActivated = isChecked ?: false
initialContainer.invisible()
@ -319,6 +355,7 @@ class ObjectIconWidget @JvmOverloads constructor(
private fun setBookmark(image: Url) {
with(binding) {
composeView.gone()
ivCheckbox.invisible()
initialContainer.invisible()
emojiContainer.invisible()
@ -334,10 +371,12 @@ class ObjectIconWidget @JvmOverloads constructor(
private fun removeIcon() {
with(binding) {
composeView.gone()
ivEmoji.setImageDrawable(null)
ivImage.setImageDrawable(null)
ivBookmark.setImageDrawable(null)
ivCheckbox.invisible()
binding.composeView.gone()
}
}
}

View file

@ -0,0 +1,34 @@
package com.anytypeio.anytype.core_ui.widgets
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.Dp
import androidx.core.graphics.toColorInt
@Composable
fun RadialGradientComposeView(
modifier: Modifier,
from: String,
to: String,
size: Dp
) {
val gradient = Brush.radialGradient(
colors = listOf(
Color(from.toColorInt()),
Color(to.toColorInt())
)
)
Box(
modifier = modifier
.size(size)
.clip(CircleShape)
.background(gradient)
)
}

View file

@ -35,6 +35,12 @@
android:layout_height="match_parent"
android:layout_margin="4dp" />
<androidx.compose.ui.platform.ComposeView
android:id="@+id/gradient"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="4dp" />
<TextView
android:id="@+id/imageText"
style="@style/TextView.ContentStyle.Initials"

View file

@ -54,4 +54,9 @@
android:contentDescription="@string/bookmark_icon"
android:visibility="gone" />
<androidx.compose.ui.platform.ComposeView
android:id="@+id/composeView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</merge>

View file

@ -94,8 +94,8 @@ import com.anytypeio.anytype.presentation.common.Action
import com.anytypeio.anytype.presentation.common.Delegator
import com.anytypeio.anytype.presentation.common.StateReducer
import com.anytypeio.anytype.presentation.common.SupportCommand
import com.anytypeio.anytype.presentation.editor.ControlPanelMachine.Interactor
import com.anytypeio.anytype.presentation.editor.ControlPanelMachine.Event.ObjectTypesWidgetEvent
import com.anytypeio.anytype.presentation.editor.ControlPanelMachine.Interactor
import com.anytypeio.anytype.presentation.editor.Editor.Restore
import com.anytypeio.anytype.presentation.editor.editor.Command
import com.anytypeio.anytype.presentation.editor.editor.Intent
@ -228,6 +228,7 @@ import com.anytypeio.anytype.presentation.relations.getObjectRelations
import com.anytypeio.anytype.presentation.relations.views
import com.anytypeio.anytype.presentation.search.ObjectSearchConstants
import com.anytypeio.anytype.presentation.search.ObjectSearchViewModel
import com.anytypeio.anytype.presentation.spaces.SpaceGradientProvider
import com.anytypeio.anytype.presentation.util.CopyFileStatus
import com.anytypeio.anytype.presentation.util.CopyFileToCacheDirectory
import com.anytypeio.anytype.presentation.util.Dispatcher
@ -5818,7 +5819,8 @@ class EditorViewModel(
val objects = result
.toView(
urlBuilder = urlBuilder,
objectTypes = storeOfObjectTypes.getAll()
objectTypes = storeOfObjectTypes.getAll(),
gradientProvider = SpaceGradientProvider.Default
)
.filter {
SupportedLayouts.layouts.contains(it.layout)

View file

@ -17,9 +17,9 @@ import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_CHECKBOX
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_CODE_SNIPPET
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_DATA_VIEW_DEFAULT
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_DATA_VIEW_SOURCE_DELETED
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_DATA_VIEW_EMPTY_DATA
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_DATA_VIEW_EMPTY_SOURCE
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_DATA_VIEW_SOURCE_DELETED
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_DESCRIPTION
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_DIVIDER_DOTS
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_DIVIDER_LINE
@ -75,6 +75,7 @@ import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER
import com.anytypeio.anytype.presentation.objects.ObjectIcon
import com.anytypeio.anytype.presentation.objects.appearance.choose.ObjectAppearanceChooseSettingsView
import com.anytypeio.anytype.presentation.relations.ObjectRelationView
import com.anytypeio.anytype.presentation.spaces.SpaceIconView
/**
* UI-models for different types of blocks.
@ -686,7 +687,8 @@ sealed class BlockView : ViewType {
override val mode: Mode = Mode.EDIT,
override var cursor: Int? = null,
override val searchFields: List<Searchable.Field> = emptyList(),
override val hint: String? = null
override val hint: String? = null,
val spaceGradient: SpaceIconView.Gradient? = null
) : Title(), Searchable {
override fun getViewType() = HOLDER_PROFILE_TITLE
}

View file

@ -39,6 +39,8 @@ import com.anytypeio.anytype.presentation.relations.ObjectRelationView
import com.anytypeio.anytype.presentation.relations.getCover
import com.anytypeio.anytype.presentation.relations.objectTypeRelation
import com.anytypeio.anytype.presentation.relations.view
import com.anytypeio.anytype.presentation.spaces.SpaceGradientProvider
import com.anytypeio.anytype.presentation.spaces.SpaceIconView
import javax.inject.Inject
import timber.log.Timber
import com.anytypeio.anytype.presentation.editor.Editor.Mode as EditorMode
@ -1516,6 +1518,13 @@ class DefaultBlockViewRenderer @Inject constructor(
else
null
},
spaceGradient = details.details[root.id]?.iconOption?.let { code ->
val gradient = SpaceGradientProvider.Default.get(code)
SpaceIconView.Gradient(
from = gradient.from,
to = gradient.to
)
},
isFocused = resolveIsFocused(focus, block),
cursor = cursor,
coverColor = coverContainer.coverColor,

View file

@ -213,7 +213,8 @@ class HomeScreenViewModel(
urlBuilder = urlBuilder,
workspace = config.workspace,
config = config,
objectWatcher = objectWatcher
objectWatcher = objectWatcher,
spaceGradientProvider = spaceGradientProvider
)
is Widget.List -> if (BundledWidgetSourceIds.ids.contains(widget.source.id)) {
ListWidgetContainer(
@ -223,6 +224,7 @@ class HomeScreenViewModel(
storage = storelessSubscriptionContainer,
isWidgetCollapsed = isCollapsed(widget.id),
urlBuilder = urlBuilder,
spaceGradientProvider = spaceGradientProvider,
isSessionActive = isSessionActive,
objectWatcher = objectWatcher,
config = config
@ -236,7 +238,8 @@ class HomeScreenViewModel(
activeView = observeCurrentWidgetView(widget.id),
isWidgetCollapsed = isCollapsed(widget.id),
isSessionActive = isSessionActive,
urlBuilder = urlBuilder
urlBuilder = urlBuilder,
gradientProvider = spaceGradientProvider
)
}
}

View file

@ -21,6 +21,7 @@ import com.anytypeio.anytype.presentation.objects.toLinkToObjectView
import com.anytypeio.anytype.presentation.objects.toLinkToView
import com.anytypeio.anytype.presentation.search.ObjectSearchConstants
import com.anytypeio.anytype.presentation.search.ObjectSearchViewModel
import com.anytypeio.anytype.presentation.spaces.SpaceGradientProvider
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
@ -107,6 +108,7 @@ class LinkToObjectOrWebViewModel(
LinkToItemView.Subheading.LinkedTo,
obj.toLinkToObjectView(
urlBuilder = urlBuilder,
gradientProvider = SpaceGradientProvider.Default,
objectTypes = storeOfObjectTypes.getAll()
),
LinkToItemView.Subheading.Actions,
@ -127,7 +129,8 @@ class LinkToObjectOrWebViewModel(
val objectViews = filteredSearchResponse.toLinkToView(
urlBuilder = urlBuilder,
objectTypes = storeOfObjectTypes.getAll()
objectTypes = storeOfObjectTypes.getAll(),
gradientProvider = SpaceGradientProvider.Default
)
val views = mutableListOf<LinkToItemView>()
if (clipboardUrl != null && userInput.value.isBlank()) {

View file

@ -5,6 +5,7 @@ import com.anytypeio.anytype.core_models.ObjectType
import com.anytypeio.anytype.core_models.ObjectWrapper
import com.anytypeio.anytype.core_models.Url
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.presentation.spaces.SpaceGradientProvider
sealed class ObjectIcon {
object None : ObjectIcon()
@ -17,6 +18,7 @@ sealed class ObjectIcon {
sealed class Profile : ObjectIcon() {
data class Avatar(val name: String) : Profile()
data class Image(val hash: Hash) : Profile()
data class Gradient(val from: String, val to: String) : Profile()
}
data class Task(val isChecked: Boolean) : ObjectIcon()
@ -30,7 +32,8 @@ sealed class ObjectIcon {
obj: ObjectWrapper.Basic,
layout: ObjectType.Layout?,
builder: UrlBuilder,
objectTypeNoIcon: Boolean = false
objectTypeNoIcon: Boolean = false,
gradientProvider: SpaceGradientProvider = SpaceGradientProvider.Default,
): ObjectIcon {
val img = obj.iconImage
val emoji = obj.iconEmoji
@ -46,10 +49,18 @@ sealed class ObjectIcon {
!emoji.isNullOrBlank() -> Basic.Emoji(unicode = emoji)
else -> Basic.Avatar(obj.name.orEmpty())
}
ObjectType.Layout.PROFILE -> if (!img.isNullOrBlank()) {
Profile.Image(hash = builder.thumbnail(img))
} else {
Profile.Avatar(name = obj.name.orEmpty())
ObjectType.Layout.PROFILE -> {
val option = obj.iconOption
val gradient = option?.let { code ->
gradientProvider.get(code)
}
if (!img.isNullOrBlank()) {
Profile.Image(hash = builder.thumbnail(img))
} else if (gradient != null) {
Profile.Gradient(from = gradient.from, to = gradient.to)
} else {
Profile.Avatar(name = obj.name.orEmpty())
}
}
ObjectType.Layout.SET, ObjectType.Layout.COLLECTION -> if (!img.isNullOrBlank()) {
Basic.Image(hash = builder.thumbnail(img))

View file

@ -15,13 +15,15 @@ import com.anytypeio.anytype.presentation.navigation.DefaultObjectView
import com.anytypeio.anytype.presentation.relations.DateParser
import com.anytypeio.anytype.presentation.relations.RelationValueView
import com.anytypeio.anytype.presentation.sets.filter.CreateFilterView
import com.anytypeio.anytype.presentation.spaces.SpaceGradientProvider
import com.anytypeio.anytype.presentation.widgets.collection.CollectionView
import timber.log.Timber
@Deprecated("To be deleted")
fun List<ObjectWrapper.Basic>.toView(
urlBuilder: UrlBuilder,
objectTypes: List<ObjectWrapper.Type>
objectTypes: List<ObjectWrapper.Type>,
gradientProvider: SpaceGradientProvider = SpaceGradientProvider.Default
): List<DefaultObjectView> =
this.map { obj ->
val typeUrl = obj.getProperType()
@ -39,14 +41,16 @@ fun List<ObjectWrapper.Basic>.toView(
icon = ObjectIcon.from(
obj = obj,
layout = layout,
builder = urlBuilder
builder = urlBuilder,
gradientProvider = gradientProvider
)
)
}
fun List<ObjectWrapper.Basic>.toViews(
urlBuilder: UrlBuilder,
objectTypes: List<ObjectWrapper.Type>
objectTypes: List<ObjectWrapper.Type>,
gradientProvider: SpaceGradientProvider = SpaceGradientProvider.Default
): List<DefaultObjectView> = map { obj ->
val typeUrl = obj.getProperType()
val layout = obj.getProperLayout()
@ -60,7 +64,8 @@ fun List<ObjectWrapper.Basic>.toViews(
icon = ObjectIcon.from(
obj = obj,
layout = layout,
builder = urlBuilder
builder = urlBuilder,
gradientProvider = gradientProvider
),
lastModifiedDate = DateParser.parseInMillis(obj.lastModifiedDate) ?: 0L,
lastOpenedDate = DateParser.parseInMillis(obj.lastOpenedDate) ?: 0L,
@ -70,6 +75,7 @@ fun List<ObjectWrapper.Basic>.toViews(
fun List<ObjectWrapper.Basic>.toLibraryViews(
urlBuilder: UrlBuilder,
gradientProvider: SpaceGradientProvider = SpaceGradientProvider.Default,
): List<LibraryView> = map { obj ->
when (val type = obj.getProperType()) {
MarketplaceObjectTypeIds.OBJECT_TYPE -> {
@ -79,7 +85,8 @@ fun List<ObjectWrapper.Basic>.toLibraryViews(
icon = ObjectIcon.from(
obj = obj,
layout = obj.getProperLayout(),
builder = urlBuilder
builder = urlBuilder,
gradientProvider = gradientProvider
),
)
}
@ -90,7 +97,8 @@ fun List<ObjectWrapper.Basic>.toLibraryViews(
icon = ObjectIcon.from(
obj = obj,
layout = obj.getProperLayout(),
builder = urlBuilder
builder = urlBuilder,
gradientProvider = gradientProvider
),
sourceObject = obj.map[SOURCE_OBJECT]?.toString(),
readOnly = obj.restrictions.contains(ObjectRestriction.DELETE),
@ -126,7 +134,8 @@ fun List<ObjectWrapper.Basic>.toLibraryViews(
fun List<ObjectWrapper.Basic>.toLinkToView(
urlBuilder: UrlBuilder,
objectTypes: List<ObjectWrapper.Type>
objectTypes: List<ObjectWrapper.Type>,
gradientProvider: SpaceGradientProvider = SpaceGradientProvider.Default,
): List<LinkToItemView.Object> =
this.mapIndexed { index, obj ->
val typeUrl = obj.getProperType()
@ -140,7 +149,8 @@ fun List<ObjectWrapper.Basic>.toLinkToView(
icon = ObjectIcon.from(
obj = obj,
layout = layout,
builder = urlBuilder
builder = urlBuilder,
gradientProvider = gradientProvider
),
position = index
)
@ -148,6 +158,7 @@ fun List<ObjectWrapper.Basic>.toLinkToView(
fun ObjectWrapper.Basic.toLinkToObjectView(
urlBuilder: UrlBuilder,
gradientProvider: SpaceGradientProvider = SpaceGradientProvider.Default,
objectTypes: List<ObjectWrapper.Type>
): LinkToItemView.LinkedTo.Object {
val typeUrl = this.getProperType()
@ -161,7 +172,8 @@ fun ObjectWrapper.Basic.toLinkToObjectView(
icon = ObjectIcon.from(
obj = this,
layout = layout,
builder = urlBuilder
builder = urlBuilder,
gradientProvider = gradientProvider
)
)
}
@ -169,7 +181,8 @@ fun ObjectWrapper.Basic.toLinkToObjectView(
fun List<ObjectWrapper.Basic>.toCreateFilterObjectView(
ids: List<*>? = null,
urlBuilder: UrlBuilder,
objectTypes: List<ObjectWrapper.Type>
objectTypes: List<ObjectWrapper.Type>,
gradientProvider: SpaceGradientProvider = SpaceGradientProvider.Default,
): List<CreateFilterView.Object> =
this.map { obj ->
CreateFilterView.Object(
@ -182,7 +195,8 @@ fun List<ObjectWrapper.Basic>.toCreateFilterObjectView(
icon = ObjectIcon.from(
obj = obj,
layout = obj.getProperLayout(),
builder = urlBuilder
builder = urlBuilder,
gradientProvider = gradientProvider
),
isSelected = ids?.contains(obj.id) ?: false
)
@ -191,7 +205,8 @@ fun List<ObjectWrapper.Basic>.toCreateFilterObjectView(
fun List<ObjectWrapper.Basic>.toRelationObjectValueView(
excluded: List<Id>,
urlBuilder: UrlBuilder,
objectTypes: List<ObjectWrapper.Type>
objectTypes: List<ObjectWrapper.Type>,
gradientProvider: SpaceGradientProvider = SpaceGradientProvider.Default
): List<RelationValueView.Object> =
this.mapNotNull { obj ->
val typeUrl = obj.getProperType()
@ -210,7 +225,8 @@ fun List<ObjectWrapper.Basic>.toRelationObjectValueView(
icon = ObjectIcon.from(
obj = obj,
layout = layout,
builder = urlBuilder
builder = urlBuilder,
gradientProvider = gradientProvider
),
isSelected = false,
removable = false

View file

@ -28,7 +28,6 @@ import com.anytypeio.anytype.presentation.common.Delegator
import com.anytypeio.anytype.presentation.editor.Editor
import com.anytypeio.anytype.presentation.extension.sendAnalyticsCreateTemplateEvent
import com.anytypeio.anytype.presentation.extension.sendAnalyticsDefaultTemplateEvent
import com.anytypeio.anytype.presentation.extension.sendAnalyticsCreateTemplateEvent
import com.anytypeio.anytype.presentation.objects.ObjectAction
import com.anytypeio.anytype.presentation.objects.ObjectIcon
import com.anytypeio.anytype.presentation.objects.getProperName
@ -323,7 +322,11 @@ class ObjectMenuViewModel(
ObjectWrapper.Basic(storage.details.current().details[type]?.map ?: emptyMap())
return Command.OpenTemplate(
template = template,
icon = ObjectIcon.from(objType, objType.layout, urlBuilder),
icon = ObjectIcon.from(
objType,
objType.layout,
urlBuilder
),
typeName = objType.getProperName()
)
}

View file

@ -574,6 +574,7 @@ object ObjectSearchConstants {
Relations.NAME,
Relations.ICON_IMAGE,
Relations.ICON_EMOJI,
Relations.ICON_OPTION,
Relations.TYPE,
Relations.LAYOUT,
Relations.IS_ARCHIVED,

View file

@ -1,7 +1,5 @@
package com.anytypeio.anytype.presentation.spaces
import javax.inject.Inject
interface SpaceGradientProvider {
fun get(id: Double): Gradient
@ -11,7 +9,7 @@ interface SpaceGradientProvider {
*/
fun randomId(): Int
class Impl @Inject constructor(): SpaceGradientProvider {
object Default : SpaceGradientProvider {
override fun get(id: Double): Gradient {
return gradients[id] ?: Gradient("#F6EB7D", "#CBD2FA")

View file

@ -14,6 +14,7 @@ import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.`object`.GetObject
import com.anytypeio.anytype.presentation.search.ObjectSearchConstants
import com.anytypeio.anytype.presentation.spaces.SpaceGradientProvider
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.combine
@ -29,6 +30,7 @@ class DataViewListWidgetContainer(
private val getObject: GetObject,
private val storage: StorelessSubscriptionContainer,
private val urlBuilder: UrlBuilder,
private val gradientProvider: SpaceGradientProvider,
private val activeView: Flow<Id?>,
private val isWidgetCollapsed: Flow<Boolean>,
isSessionActive: Flow<Boolean>
@ -76,7 +78,10 @@ class DataViewListWidgetContainer(
elements = objects.map { obj ->
WidgetView.SetOfObjects.Element(
obj = obj,
objectIcon = obj.widgetElementIcon(urlBuilder)
objectIcon = obj.widgetElementIcon(
builder = urlBuilder,
gradientProvider = gradientProvider
)
)
},
isExpanded = true,

View file

@ -16,6 +16,7 @@ import com.anytypeio.anytype.domain.objects.ObjectWatcher
import com.anytypeio.anytype.presentation.search.ObjectSearchConstants
import com.anytypeio.anytype.presentation.search.ObjectSearchConstants.collectionsSorts
import com.anytypeio.anytype.presentation.search.Subscriptions
import com.anytypeio.anytype.presentation.spaces.SpaceGradientProvider
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.emptyFlow
@ -30,6 +31,7 @@ class ListWidgetContainer(
private val subscription: Id,
private val storage: StorelessSubscriptionContainer,
private val urlBuilder: UrlBuilder,
private val spaceGradientProvider: SpaceGradientProvider,
private val isWidgetCollapsed: Flow<Boolean>,
private val objectWatcher: ObjectWatcher,
isSessionActive: Flow<Boolean>
@ -98,7 +100,10 @@ class ListWidgetContainer(
elements = objects.map { obj ->
WidgetView.ListOfObjects.Element(
obj = obj,
objectIcon = obj.widgetElementIcon(urlBuilder)
objectIcon = obj.widgetElementIcon(
builder = urlBuilder,
gradientProvider = spaceGradientProvider
)
)
},
isExpanded = true,

View file

@ -10,6 +10,7 @@ import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.objects.ObjectWatcher
import com.anytypeio.anytype.presentation.search.ObjectSearchConstants
import com.anytypeio.anytype.presentation.spaces.SpaceGradientProvider
import com.anytypeio.anytype.presentation.widgets.WidgetConfig.isValidObject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
@ -27,6 +28,7 @@ class TreeWidgetContainer(
private val config: Config,
private val container: StorelessSubscriptionContainer,
private val urlBuilder: UrlBuilder,
private val spaceGradientProvider: SpaceGradientProvider,
private val expandedBranches: Flow<List<TreePath>>,
private val isWidgetCollapsed: Flow<Boolean>,
private val objectWatcher: ObjectWatcher,
@ -217,7 +219,10 @@ class TreeWidgetContainer(
expanded = expanded,
currentLinkPath = currentLinkPath
),
objectIcon = obj.widgetElementIcon(urlBuilder),
objectIcon = obj.widgetElementIcon(
builder = urlBuilder,
gradientProvider = spaceGradientProvider
),
indent = level,
obj = obj,
path = path + link

View file

@ -6,6 +6,7 @@ import com.anytypeio.anytype.core_models.ObjectWrapper
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.presentation.editor.model.Indent
import com.anytypeio.anytype.presentation.objects.ObjectIcon
import com.anytypeio.anytype.presentation.spaces.SpaceGradientProvider
sealed class WidgetView {
@ -116,9 +117,15 @@ fun ObjectWrapper.Basic.getWidgetObjectName(): String? {
}
}
fun ObjectWrapper.Basic.widgetElementIcon(builder: UrlBuilder) : ObjectIcon {
fun ObjectWrapper.Basic.widgetElementIcon(
builder: UrlBuilder,
gradientProvider: SpaceGradientProvider
) : ObjectIcon {
val img = iconImage
val emoji = iconEmoji
val option = iconOption?.let { code ->
gradientProvider.get(code)
}
return when (layout) {
ObjectType.Layout.BASIC -> when {
!img.isNullOrBlank() -> ObjectIcon.Basic.Image(hash = builder.thumbnail(img))
@ -130,10 +137,17 @@ fun ObjectWrapper.Basic.widgetElementIcon(builder: UrlBuilder) : ObjectIcon {
!emoji.isNullOrBlank() -> ObjectIcon.Basic.Emoji(unicode = emoji)
else -> ObjectIcon.None
}
ObjectType.Layout.PROFILE -> if (!img.isNullOrBlank()) {
ObjectIcon.Profile.Image(hash = builder.thumbnail(img))
} else {
ObjectIcon.Profile.Avatar(name = name.orEmpty())
ObjectType.Layout.PROFILE -> {
if (!img.isNullOrBlank()) {
ObjectIcon.Profile.Image(hash = builder.thumbnail(img))
} else if (option != null) {
ObjectIcon.Profile.Gradient(
from = option.from,
to = option.to
)
} else {
ObjectIcon.Profile.Avatar(name = name.orEmpty())
}
}
ObjectType.Layout.SET, ObjectType.Layout.COLLECTION -> if (!img.isNullOrBlank()) {
ObjectIcon.Basic.Image(hash = builder.thumbnail(img))

View file

@ -13,6 +13,7 @@ import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.objects.ObjectWatcher
import com.anytypeio.anytype.presentation.objects.ObjectIcon
import com.anytypeio.anytype.presentation.spaces.SpaceGradientProvider
import com.anytypeio.anytype.presentation.util.DefaultCoroutineTestRule
import com.anytypeio.anytype.presentation.widgets.TreePath
import com.anytypeio.anytype.presentation.widgets.TreeWidgetContainer
@ -42,6 +43,9 @@ class TreeWidgetContainerTest {
@Mock
lateinit var gateway: Gateway
@Mock
lateinit var spaceGradientProvider: SpaceGradientProvider
@Mock
lateinit var storelessSubscriptionContainer: StorelessSubscriptionContainer
@ -97,7 +101,8 @@ class TreeWidgetContainerTest {
workspace = workspace,
isSessionActive = flowOf(true),
config = config,
objectWatcher = objectWatcher
objectWatcher = objectWatcher,
spaceGradientProvider = spaceGradientProvider
)
stubObjectSearch(
@ -167,7 +172,8 @@ class TreeWidgetContainerTest {
workspace = workspace,
isSessionActive = flowOf(true),
config = config,
objectWatcher = objectWatcher
objectWatcher = objectWatcher,
spaceGradientProvider = spaceGradientProvider
)
stubObjectSearch(
@ -252,7 +258,8 @@ class TreeWidgetContainerTest {
workspace = workspace,
isSessionActive = flowOf(true),
config = config,
objectWatcher = objectWatcher
objectWatcher = objectWatcher,
spaceGradientProvider = spaceGradientProvider
)
stubObjectSearch(
@ -392,7 +399,8 @@ class TreeWidgetContainerTest {
workspace = workspace,
isSessionActive = flowOf(true),
config = config,
objectWatcher = objectWatcher
objectWatcher = objectWatcher,
spaceGradientProvider = spaceGradientProvider
)
stubObjectSearch(
@ -449,7 +457,8 @@ class TreeWidgetContainerTest {
workspace = workspace,
isSessionActive = flowOf(true),
config = config,
objectWatcher = objectWatcher
objectWatcher = objectWatcher,
spaceGradientProvider = spaceGradientProvider
)
stubObjectSearch(

View file

@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
@ -24,6 +25,7 @@ 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.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.graphics.toColorInt
@ -256,4 +258,25 @@ fun ProfileOption(
Arrow()
}
}
}
@Composable
fun GradientComposeView(
modifier: Modifier,
from: String,
to: String,
size: Dp
) {
val gradient = Brush.radialGradient(
colors = listOf(
Color(from.toColorInt()),
Color(to.toColorInt())
)
)
Box(
modifier = modifier
.size(size)
.clip(CircleShape)
.background(gradient)
)
}