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

DROID-1179 Widgets | Enhancement | Save selected view in widget with set between app launches (#3154)

This commit is contained in:
Evgenii Kozlov 2023-05-04 19:01:10 +02:00 committed by uburoiubu
parent 8efc6b4c8a
commit c58149f102
No known key found for this signature in database
GPG key ID: C8FB80E0A595FBB6
8 changed files with 84 additions and 19 deletions

View file

@ -2,7 +2,9 @@ package com.anytypeio.anytype.core_models
/**
* @property [collapsed] list of collapsed widgets
* @property [widgetsToActiveViews] maps specific widget to the selected view inside this widget
*/
data class WidgetSession(
val collapsed: List<Id>
val collapsed: List<Id> = emptyList(),
val widgetsToActiveViews: Map<Id, Id> = emptyMap()
)

View file

@ -13,6 +13,7 @@ dependencies {
implementation libs.coroutinesAndroid
implementation libs.gson
implementation libs.lifecycleLiveData
implementation libs.kotlinxSerializationJson
implementation libs.room
implementation libs.roomKtx

View file

@ -0,0 +1,31 @@
package com.anytypeio.anytype.persistence.common
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import timber.log.Timber
typealias JsonString = String
fun Map<String, String>.toJsonString(): JsonString = try {
JsonObject(
content = mapValues { entry ->
JsonPrimitive(entry.value)
}
).toString()
} catch (e: Exception) {
Timber.e(e, "Error while mapping to json string")
""
}
fun JsonString.toStringMap(): Map<String, String> = try {
if (this.isNotEmpty()) {
Json.decodeFromString(this)
} else {
emptyMap()
}
} catch (e: Exception) {
Timber.e(e, "Error while mapping from json string")
emptyMap()
}

View file

@ -5,6 +5,8 @@ import com.anytypeio.anytype.core_models.ThemeMode
import com.anytypeio.anytype.core_models.Wallpaper
import com.anytypeio.anytype.core_models.WidgetSession
import com.anytypeio.anytype.data.auth.repo.UserSettingsCache
import com.anytypeio.anytype.persistence.common.toJsonString
import com.anytypeio.anytype.persistence.common.toStringMap
class DefaultUserSettingsCache(private val prefs: SharedPreferences) : UserSettingsCache {
@ -57,7 +59,7 @@ class DefaultUserSettingsCache(private val prefs: SharedPreferences) : UserSetti
val type = prefs.getInt(WALLPAPER_TYPE_KEY, -1)
if (type != -1) {
val value = prefs.getString(WALLPAPER_VALUE_KEY, null)
if (value != null && value.isNotEmpty()) {
if (!value.isNullOrEmpty()) {
return when (type) {
WALLPAPER_TYPE_COLOR -> {
Wallpaper.Color(value)
@ -68,6 +70,7 @@ class DefaultUserSettingsCache(private val prefs: SharedPreferences) : UserSetti
WALLPAPER_TYPE_IMAGE -> {
Wallpaper.Image(value)
}
else -> {
Wallpaper.Default
}
@ -122,23 +125,26 @@ class DefaultUserSettingsCache(private val prefs: SharedPreferences) : UserSetti
}
}
override suspend fun getWidgetSession(): WidgetSession {
return if (prefs.contains(COLLAPSED_WIDGETS_KEY)) {
WidgetSession(
collapsed = prefs.getStringSet(COLLAPSED_WIDGETS_KEY, emptySet())
.orEmpty()
.toList()
)
} else
WidgetSession(
collapsed = emptyList()
)
}
override suspend fun getWidgetSession(): WidgetSession = WidgetSession(
collapsed = if (prefs.contains(COLLAPSED_WIDGETS_KEY)) {
prefs.getStringSet(COLLAPSED_WIDGETS_KEY, emptySet())
.orEmpty()
.toList()
} else {
emptyList()
},
widgetsToActiveViews = if (prefs.contains(ACTIVE_WIDGETS_VIEWS_KEY)) {
prefs.getString(ACTIVE_WIDGETS_VIEWS_KEY, "")!!.toStringMap()
} else {
emptyMap()
}
)
override suspend fun saveWidgetSession(session: WidgetSession) {
prefs
.edit()
.putStringSet(COLLAPSED_WIDGETS_KEY, session.collapsed.toSet())
.putString(ACTIVE_WIDGETS_VIEWS_KEY, session.widgetsToActiveViews.toJsonString())
.apply()
}
@ -167,5 +173,6 @@ class DefaultUserSettingsCache(private val prefs: SharedPreferences) : UserSetti
const val THEME_TYPE_NIGHT = 3
const val COLLAPSED_WIDGETS_KEY = "prefs.user_settings.collapsed-widgets"
const val ACTIVE_WIDGETS_VIEWS_KEY = "prefs.user_settings.active-widget-views"
}
}

View file

@ -1,4 +1,4 @@
package com.anytypeio.anytype
package com.anytypeio.anytype.persistence
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.room.Room
@ -6,6 +6,8 @@ import androidx.test.platform.app.InstrumentationRegistry
import com.anytypeio.anytype.persistence.db.AnytypeDatabase
import com.anytypeio.anytype.persistence.model.AccountTable
import com.anytypeio.anytype.test_utils.MockDataFactory
import kotlin.test.assertEquals
import kotlin.test.assertTrue
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import org.junit.After
@ -14,8 +16,6 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import kotlin.test.assertEquals
import kotlin.test.assertTrue
@RunWith(RobolectricTestRunner::class)
@Config(manifest = Config.NONE)

View file

@ -66,6 +66,7 @@ import com.anytypeio.anytype.presentation.widgets.WidgetDispatchEvent
import com.anytypeio.anytype.presentation.widgets.WidgetSessionStateHolder
import com.anytypeio.anytype.presentation.widgets.WidgetView
import com.anytypeio.anytype.presentation.widgets.collection.Subscription
import com.anytypeio.anytype.presentation.widgets.getActiveTabViews
import com.anytypeio.anytype.presentation.widgets.parseWidgets
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableSharedFlow
@ -305,7 +306,10 @@ class HomeScreenViewModel(
viewModelScope.launch {
saveWidgetSession.execute(
SaveWidgetSession.Params(
WidgetSession(collapsed = collapsedWidgetStateHolder.get())
WidgetSession(
collapsed = collapsedWidgetStateHolder.get(),
widgetsToActiveViews = views.value.getActiveTabViews()
)
)
)
val subscriptions = widgets.value.orEmpty().map { widget ->
@ -718,8 +722,9 @@ class HomeScreenViewModel(
val session = withContext(appCoroutineDispatchers.io) {
getWidgetSession.execute(Unit).getOrNull()
}
if (session != null && session.collapsed.isNotEmpty()) {
if (session != null) {
collapsedWidgetStateHolder.set(session.collapsed)
widgetActiveViewStateHolder.init(session.widgetsToActiveViews)
}
proceedWithOpeningWidgetObject(widgetObject = configStorage.get().widgets)
}

View file

@ -8,12 +8,17 @@ import kotlinx.coroutines.flow.mapLatest
interface WidgetActiveViewStateHolder {
fun init(map: WidgetToActiveView)
fun onChangeCurrentWidgetView(widget: Id, view: Id)
fun observeCurrentWidgetView(widget: Id): Flow<Id?>
class Impl @Inject constructor() : WidgetActiveViewStateHolder {
private val widgetToActiveView = MutableStateFlow<WidgetToActiveView>(mapOf())
override fun init(map: WidgetToActiveView) {
widgetToActiveView.value = map
}
override fun onChangeCurrentWidgetView(widget: Id, view: Id) {
widgetToActiveView.value = widgetToActiveView.value + mapOf(widget to view)
}

View file

@ -149,4 +149,18 @@ fun ObjectWrapper.Basic.widgetElementIcon(builder: UrlBuilder) : ObjectIcon {
}
else -> ObjectIcon.None
}
}
fun List<WidgetView>.getActiveTabViews() : Map<Id, Id> {
return filterIsInstance<WidgetView.SetOfObjects>().mapNotNull { widget ->
if (widget.tabs.isNotEmpty()) {
val selected = widget.tabs.firstOrNull { it.isSelected }
if (selected != null)
widget.id to selected.id
else
null
} else {
null
}
}.toMap()
}