diff --git a/core-models/src/main/java/com/anytypeio/anytype/core_models/WidgetSession.kt b/core-models/src/main/java/com/anytypeio/anytype/core_models/WidgetSession.kt index 742820f432..9ab596f9f0 100644 --- a/core-models/src/main/java/com/anytypeio/anytype/core_models/WidgetSession.kt +++ b/core-models/src/main/java/com/anytypeio/anytype/core_models/WidgetSession.kt @@ -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 + val collapsed: List = emptyList(), + val widgetsToActiveViews: Map = emptyMap() ) \ No newline at end of file diff --git a/persistence/build.gradle b/persistence/build.gradle index 41798545d8..38b76af88b 100644 --- a/persistence/build.gradle +++ b/persistence/build.gradle @@ -13,6 +13,7 @@ dependencies { implementation libs.coroutinesAndroid implementation libs.gson implementation libs.lifecycleLiveData + implementation libs.kotlinxSerializationJson implementation libs.room implementation libs.roomKtx diff --git a/persistence/src/main/java/com/anytypeio/anytype/persistence/common/Converters.kt b/persistence/src/main/java/com/anytypeio/anytype/persistence/common/Converters.kt new file mode 100644 index 0000000000..68e0582c6d --- /dev/null +++ b/persistence/src/main/java/com/anytypeio/anytype/persistence/common/Converters.kt @@ -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.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 = try { + if (this.isNotEmpty()) { + Json.decodeFromString(this) + } else { + emptyMap() + } +} catch (e: Exception) { + Timber.e(e, "Error while mapping from json string") + emptyMap() +} \ No newline at end of file diff --git a/persistence/src/main/java/com/anytypeio/anytype/persistence/repo/DefaultUserSettingsCache.kt b/persistence/src/main/java/com/anytypeio/anytype/persistence/repo/DefaultUserSettingsCache.kt index abf7bfdd0b..9156c6ff73 100644 --- a/persistence/src/main/java/com/anytypeio/anytype/persistence/repo/DefaultUserSettingsCache.kt +++ b/persistence/src/main/java/com/anytypeio/anytype/persistence/repo/DefaultUserSettingsCache.kt @@ -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" } } \ No newline at end of file diff --git a/persistence/src/test/java/com/anytypeio/anytype/AccountDaoTest.kt b/persistence/src/test/java/com/anytypeio/anytype/persistence/AccountDaoTest.kt similarity index 98% rename from persistence/src/test/java/com/anytypeio/anytype/AccountDaoTest.kt rename to persistence/src/test/java/com/anytypeio/anytype/persistence/AccountDaoTest.kt index 7dda07c08e..1f8983e366 100644 --- a/persistence/src/test/java/com/anytypeio/anytype/AccountDaoTest.kt +++ b/persistence/src/test/java/com/anytypeio/anytype/persistence/AccountDaoTest.kt @@ -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) diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/home/HomeScreenViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/home/HomeScreenViewModel.kt index f3537878a1..cc8e5aba45 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/home/HomeScreenViewModel.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/home/HomeScreenViewModel.kt @@ -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) } diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/widgets/WidgetActiveViewStateHolder.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/widgets/WidgetActiveViewStateHolder.kt index ca0ec672d7..bdee874f68 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/widgets/WidgetActiveViewStateHolder.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/widgets/WidgetActiveViewStateHolder.kt @@ -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 class Impl @Inject constructor() : WidgetActiveViewStateHolder { private val widgetToActiveView = MutableStateFlow(mapOf()) + override fun init(map: WidgetToActiveView) { + widgetToActiveView.value = map + } + override fun onChangeCurrentWidgetView(widget: Id, view: Id) { widgetToActiveView.value = widgetToActiveView.value + mapOf(widget to view) } diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/widgets/WidgetView.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/widgets/WidgetView.kt index 9675022171..eec11cdc16 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/widgets/WidgetView.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/widgets/WidgetView.kt @@ -149,4 +149,18 @@ fun ObjectWrapper.Basic.widgetElementIcon(builder: UrlBuilder) : ObjectIcon { } else -> ObjectIcon.None } +} + +fun List.getActiveTabViews() : Map { + return filterIsInstance().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() } \ No newline at end of file