From e05b3e3173620ce013941d3cc379dc8bb627cf3e Mon Sep 17 00:00:00 2001 From: Evgenii Kozlov Date: Thu, 30 Mar 2023 12:08:09 +0200 Subject: [PATCH] DROID-1118 Widgets | Enhancement | Persist widget session (#3060) --- .../anytype/di/feature/MainEntryDI.kt | 8 +- .../di/feature/auth/DeletedAccountDI.kt | 12 ++- .../anytype/di/feature/home/HomescreenDI.kt | 32 -------- .../di/feature/settings/AccountAndDataDI.kt | 9 ++- .../di/feature/settings/LogoutWarningDI.kt | 9 ++- .../anytype/core_models/WidgetSession.kt | 8 ++ .../data/auth/repo/UserSettingsCache.kt | 6 +- .../auth/repo/UserSettingsDataRepository.kt | 11 ++- .../anytype/domain/auth/interactor/Logout.kt | 8 +- .../domain/config/UserSettingsRepository.kt | 4 + .../anytype/domain/widgets/DeleteWidget.kt | 3 +- .../domain/widgets/ManageWidgetSession.kt | 25 ++++++ .../anytype/domain/widgets/UpdateWidget.kt | 3 +- .../repo/DefaultUserSettingsCache.kt | 31 ++++++++ .../presentation/home/HomeScreenViewModel.kt | 38 ++++++++- .../widgets/CollapsedWidgetStateHolder.kt | 16 ++++ .../auth/DeleteAccountViewModelTest.kt | 13 +++- .../home/HomeScreenViewModelTest.kt | 77 +++++++++++++++++-- 18 files changed, 248 insertions(+), 65 deletions(-) create mode 100644 core-models/src/main/java/com/anytypeio/anytype/core_models/WidgetSession.kt create mode 100644 domain/src/main/java/com/anytypeio/anytype/domain/widgets/ManageWidgetSession.kt diff --git a/app/src/main/java/com/anytypeio/anytype/di/feature/MainEntryDI.kt b/app/src/main/java/com/anytypeio/anytype/di/feature/MainEntryDI.kt index 1d9628f0a1..27dda55ba8 100644 --- a/app/src/main/java/com/anytypeio/anytype/di/feature/MainEntryDI.kt +++ b/app/src/main/java/com/anytypeio/anytype/di/feature/MainEntryDI.kt @@ -126,11 +126,13 @@ object MainEntryModule { fun provideLogoutUseCase( repo: AuthRepository, provider: ConfigStorage, + user: UserSettingsRepository, dispatchers: AppCoroutineDispatchers ): Logout = Logout( - repo, - provider, - dispatchers + repo = repo, + user = user, + config = provider, + dispatchers = dispatchers ) @JvmStatic diff --git a/app/src/main/java/com/anytypeio/anytype/di/feature/auth/DeletedAccountDI.kt b/app/src/main/java/com/anytypeio/anytype/di/feature/auth/DeletedAccountDI.kt index 03c8a07c57..de28219116 100644 --- a/app/src/main/java/com/anytypeio/anytype/di/feature/auth/DeletedAccountDI.kt +++ b/app/src/main/java/com/anytypeio/anytype/di/feature/auth/DeletedAccountDI.kt @@ -10,6 +10,7 @@ import com.anytypeio.anytype.domain.auth.interactor.Logout import com.anytypeio.anytype.domain.auth.repo.AuthRepository import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers import com.anytypeio.anytype.domain.config.ConfigStorage +import com.anytypeio.anytype.domain.config.UserSettingsRepository import com.anytypeio.anytype.domain.misc.AppActionManager import com.anytypeio.anytype.domain.search.RelationsSubscriptionManager import com.anytypeio.anytype.ext.DefaultDateHelper @@ -58,11 +59,13 @@ object DeletedAccountModule { fun provideLogoutUseCase( repo: AuthRepository, provider: ConfigStorage, - dispatchers: AppCoroutineDispatchers + dispatchers: AppCoroutineDispatchers, + user: UserSettingsRepository ): Logout = Logout( - repo, - provider, - dispatchers + repo = repo, + config = provider, + user = user, + dispatchers = dispatchers ) @JvmStatic @@ -89,4 +92,5 @@ interface DeletedAccountDependencies : ComponentDependencies { fun dispatchers(): AppCoroutineDispatchers fun configStorage(): ConfigStorage fun authRepository(): AuthRepository + fun userSettingsRepository(): UserSettingsRepository } \ No newline at end of file diff --git a/app/src/main/java/com/anytypeio/anytype/di/feature/home/HomescreenDI.kt b/app/src/main/java/com/anytypeio/anytype/di/feature/home/HomescreenDI.kt index c11642e0bb..69947255e8 100644 --- a/app/src/main/java/com/anytypeio/anytype/di/feature/home/HomescreenDI.kt +++ b/app/src/main/java/com/anytypeio/anytype/di/feature/home/HomescreenDI.kt @@ -27,9 +27,6 @@ import com.anytypeio.anytype.domain.page.CloseBlock import com.anytypeio.anytype.domain.page.CreateObject import com.anytypeio.anytype.domain.search.SubscriptionEventChannel import com.anytypeio.anytype.domain.templates.GetTemplates -import com.anytypeio.anytype.domain.widgets.CreateWidget -import com.anytypeio.anytype.domain.widgets.DeleteWidget -import com.anytypeio.anytype.domain.widgets.UpdateWidget import com.anytypeio.anytype.domain.workspace.WorkspaceManager import com.anytypeio.anytype.presentation.home.HomeScreenViewModel import com.anytypeio.anytype.presentation.home.Unsubscriber @@ -93,35 +90,6 @@ object HomeScreenModule { dispatchers = dispatchers ) - @JvmStatic - @Provides - @PerScreen - fun createWidget( - repo: BlockRepository - ): CreateWidget = CreateWidget( - repo = repo, - ) - - @JvmStatic - @Provides - @PerScreen - fun deleteWidget( - repo: BlockRepository, - dispatchers: AppCoroutineDispatchers - ): DeleteWidget = DeleteWidget( - repo = repo, - dispatchers = dispatchers - ) - - @JvmStatic - @Provides - @PerScreen - fun updateWidget( - repo: BlockRepository, - ): UpdateWidget = UpdateWidget( - repo = repo - ) - @JvmStatic @Provides @PerScreen diff --git a/app/src/main/java/com/anytypeio/anytype/di/feature/settings/AccountAndDataDI.kt b/app/src/main/java/com/anytypeio/anytype/di/feature/settings/AccountAndDataDI.kt index e799096095..e066cedcc4 100644 --- a/app/src/main/java/com/anytypeio/anytype/di/feature/settings/AccountAndDataDI.kt +++ b/app/src/main/java/com/anytypeio/anytype/di/feature/settings/AccountAndDataDI.kt @@ -9,6 +9,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.DebugSync import com.anytypeio.anytype.domain.device.ClearFileCache import com.anytypeio.anytype.presentation.util.downloader.UriFileProvider @@ -87,11 +88,13 @@ object AccountAndDataModule { fun provideLogoutUseCase( repo: AuthRepository, provider: ConfigStorage, + user: UserSettingsRepository, dispatchers: AppCoroutineDispatchers ): Logout = Logout( - repo, - provider, - dispatchers + repo = repo, + config = provider, + user = user, + dispatchers = dispatchers ) @JvmStatic diff --git a/app/src/main/java/com/anytypeio/anytype/di/feature/settings/LogoutWarningDI.kt b/app/src/main/java/com/anytypeio/anytype/di/feature/settings/LogoutWarningDI.kt index bc69011448..8ab59e32d9 100644 --- a/app/src/main/java/com/anytypeio/anytype/di/feature/settings/LogoutWarningDI.kt +++ b/app/src/main/java/com/anytypeio/anytype/di/feature/settings/LogoutWarningDI.kt @@ -6,6 +6,7 @@ import com.anytypeio.anytype.domain.auth.interactor.Logout import com.anytypeio.anytype.domain.auth.repo.AuthRepository import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers import com.anytypeio.anytype.domain.config.ConfigStorage +import com.anytypeio.anytype.domain.config.UserSettingsRepository import com.anytypeio.anytype.domain.misc.AppActionManager import com.anytypeio.anytype.domain.search.RelationsSubscriptionManager import com.anytypeio.anytype.ui.settings.LogoutWarningFragment @@ -51,10 +52,12 @@ object LogoutWarningModule { fun provideLogoutUseCase( repo: AuthRepository, provider: ConfigStorage, + user: UserSettingsRepository, dispatchers: AppCoroutineDispatchers ): Logout = Logout( - repo, - provider, - dispatchers + repo = repo, + config = provider, + user = user, + dispatchers = dispatchers ) } \ No newline at end of file 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 new file mode 100644 index 0000000000..742820f432 --- /dev/null +++ b/core-models/src/main/java/com/anytypeio/anytype/core_models/WidgetSession.kt @@ -0,0 +1,8 @@ +package com.anytypeio.anytype.core_models + +/** + * @property [collapsed] list of collapsed widgets + */ +data class WidgetSession( + val collapsed: List +) \ No newline at end of file diff --git a/data/src/main/java/com/anytypeio/anytype/data/auth/repo/UserSettingsCache.kt b/data/src/main/java/com/anytypeio/anytype/data/auth/repo/UserSettingsCache.kt index d4681ffedd..938a3b3a83 100644 --- a/data/src/main/java/com/anytypeio/anytype/data/auth/repo/UserSettingsCache.kt +++ b/data/src/main/java/com/anytypeio/anytype/data/auth/repo/UserSettingsCache.kt @@ -1,7 +1,8 @@ package com.anytypeio.anytype.data.auth.repo -import com.anytypeio.anytype.core_models.Wallpaper import com.anytypeio.anytype.core_models.ThemeMode +import com.anytypeio.anytype.core_models.Wallpaper +import com.anytypeio.anytype.core_models.WidgetSession interface UserSettingsCache { suspend fun setDefaultObjectType(type: String, name: String) @@ -10,4 +11,7 @@ interface UserSettingsCache { suspend fun getWallpaper() : Wallpaper suspend fun setThemeMode(mode: ThemeMode) suspend fun getThemeMode(): ThemeMode + suspend fun getWidgetSession() : WidgetSession + suspend fun saveWidgetSession(session: WidgetSession) + suspend fun clear() } \ No newline at end of file diff --git a/data/src/main/java/com/anytypeio/anytype/data/auth/repo/UserSettingsDataRepository.kt b/data/src/main/java/com/anytypeio/anytype/data/auth/repo/UserSettingsDataRepository.kt index d85424c1f2..c98f6219bc 100644 --- a/data/src/main/java/com/anytypeio/anytype/data/auth/repo/UserSettingsDataRepository.kt +++ b/data/src/main/java/com/anytypeio/anytype/data/auth/repo/UserSettingsDataRepository.kt @@ -1,7 +1,8 @@ package com.anytypeio.anytype.data.auth.repo -import com.anytypeio.anytype.core_models.Wallpaper 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.domain.config.UserSettingsRepository class UserSettingsDataRepository(private val cache: UserSettingsCache) : UserSettingsRepository { @@ -22,4 +23,12 @@ class UserSettingsDataRepository(private val cache: UserSettingsCache) : UserSet } override suspend fun getThemeMode(): ThemeMode = cache.getThemeMode() + + override suspend fun getWidgetSession(): WidgetSession = cache.getWidgetSession() + + override suspend fun saveWidgetSession(session: WidgetSession) = cache.saveWidgetSession( + session = session + ) + + override suspend fun clear() = cache.clear() } \ No newline at end of file diff --git a/domain/src/main/java/com/anytypeio/anytype/domain/auth/interactor/Logout.kt b/domain/src/main/java/com/anytypeio/anytype/domain/auth/interactor/Logout.kt index b212d37865..e4ef3657d9 100644 --- a/domain/src/main/java/com/anytypeio/anytype/domain/auth/interactor/Logout.kt +++ b/domain/src/main/java/com/anytypeio/anytype/domain/auth/interactor/Logout.kt @@ -4,20 +4,22 @@ import com.anytypeio.anytype.domain.auth.repo.AuthRepository import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers import com.anytypeio.anytype.domain.base.Interactor import com.anytypeio.anytype.domain.config.ConfigStorage -import kotlinx.coroutines.Dispatchers +import com.anytypeio.anytype.domain.config.UserSettingsRepository /** * Use case for logging out. */ class Logout( private val repo: AuthRepository, - private val provider: ConfigStorage, + private val config: ConfigStorage, + private val user: UserSettingsRepository, dispatchers: AppCoroutineDispatchers, ) : Interactor(context = dispatchers.io) { override suspend fun run(params: Params) { repo.logout(params.clearLocalRepositoryData) - provider.clear() + user.clear() + config.clear() } class Params( diff --git a/domain/src/main/java/com/anytypeio/anytype/domain/config/UserSettingsRepository.kt b/domain/src/main/java/com/anytypeio/anytype/domain/config/UserSettingsRepository.kt index d6a463acfa..ca1662d81b 100644 --- a/domain/src/main/java/com/anytypeio/anytype/domain/config/UserSettingsRepository.kt +++ b/domain/src/main/java/com/anytypeio/anytype/domain/config/UserSettingsRepository.kt @@ -2,6 +2,7 @@ package com.anytypeio.anytype.domain.config import com.anytypeio.anytype.core_models.ThemeMode import com.anytypeio.anytype.core_models.Wallpaper +import com.anytypeio.anytype.core_models.WidgetSession interface UserSettingsRepository { suspend fun setWallpaper(wallpaper: Wallpaper) @@ -10,4 +11,7 @@ interface UserSettingsRepository { suspend fun getDefaultObjectType(): Pair suspend fun setThemeMode(mode: ThemeMode) suspend fun getThemeMode(): ThemeMode + suspend fun getWidgetSession() : WidgetSession + suspend fun saveWidgetSession(session: WidgetSession) + suspend fun clear() } \ No newline at end of file diff --git a/domain/src/main/java/com/anytypeio/anytype/domain/widgets/DeleteWidget.kt b/domain/src/main/java/com/anytypeio/anytype/domain/widgets/DeleteWidget.kt index 165eb74f4b..d107233077 100644 --- a/domain/src/main/java/com/anytypeio/anytype/domain/widgets/DeleteWidget.kt +++ b/domain/src/main/java/com/anytypeio/anytype/domain/widgets/DeleteWidget.kt @@ -6,8 +6,9 @@ import com.anytypeio.anytype.core_models.Payload import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers import com.anytypeio.anytype.domain.base.ResultInteractor import com.anytypeio.anytype.domain.block.repo.BlockRepository +import javax.inject.Inject -class DeleteWidget( +class DeleteWidget @Inject constructor( private val repo: BlockRepository, dispatchers: AppCoroutineDispatchers ) : ResultInteractor(dispatchers.io) { diff --git a/domain/src/main/java/com/anytypeio/anytype/domain/widgets/ManageWidgetSession.kt b/domain/src/main/java/com/anytypeio/anytype/domain/widgets/ManageWidgetSession.kt new file mode 100644 index 0000000000..5df2661f0d --- /dev/null +++ b/domain/src/main/java/com/anytypeio/anytype/domain/widgets/ManageWidgetSession.kt @@ -0,0 +1,25 @@ +package com.anytypeio.anytype.domain.widgets + +import com.anytypeio.anytype.core_models.WidgetSession +import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers +import com.anytypeio.anytype.domain.base.ResultInteractor +import com.anytypeio.anytype.domain.config.UserSettingsRepository +import javax.inject.Inject + +class GetWidgetSession @Inject constructor( + private val repo: UserSettingsRepository, + dispatchers: AppCoroutineDispatchers +) : ResultInteractor(dispatchers.io) { + override suspend fun doWork(params: Unit): WidgetSession = repo.getWidgetSession() +} + +class SaveWidgetSession @Inject constructor( + private val repo: UserSettingsRepository, + dispatchers: AppCoroutineDispatchers +) : ResultInteractor(dispatchers.io) { + override suspend fun doWork(params: Params) { + repo.saveWidgetSession(params.session) + } + + data class Params(val session: WidgetSession) +} \ No newline at end of file diff --git a/domain/src/main/java/com/anytypeio/anytype/domain/widgets/UpdateWidget.kt b/domain/src/main/java/com/anytypeio/anytype/domain/widgets/UpdateWidget.kt index e489b75efe..95ea294726 100644 --- a/domain/src/main/java/com/anytypeio/anytype/domain/widgets/UpdateWidget.kt +++ b/domain/src/main/java/com/anytypeio/anytype/domain/widgets/UpdateWidget.kt @@ -5,11 +5,12 @@ import com.anytypeio.anytype.core_models.Id import com.anytypeio.anytype.core_models.Payload import com.anytypeio.anytype.domain.base.ResultatInteractor import com.anytypeio.anytype.domain.block.repo.BlockRepository +import javax.inject.Inject /** * Use-case for updating widget block contained in widgets. */ -class UpdateWidget( +class UpdateWidget @Inject constructor( private val repo: BlockRepository ) : ResultatInteractor() { 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 fee5ffbef3..abf7bfdd0b 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 @@ -3,6 +3,7 @@ package com.anytypeio.anytype.persistence.repo import android.content.SharedPreferences 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 class DefaultUserSettingsCache(private val prefs: SharedPreferences) : UserSettingsCache { @@ -121,6 +122,34 @@ 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 saveWidgetSession(session: WidgetSession) { + prefs + .edit() + .putStringSet(COLLAPSED_WIDGETS_KEY, session.collapsed.toSet()) + .apply() + } + + override suspend fun clear() { + prefs.edit() + .remove(DEFAULT_OBJECT_TYPE_ID_KEY) + .remove(DEFAULT_OBJECT_TYPE_NAME_KEY) + .remove(COLLAPSED_WIDGETS_KEY) + .apply() + } + companion object { const val DEFAULT_OBJECT_TYPE_ID_KEY = "prefs.user_settings.default_object_type.id" const val DEFAULT_OBJECT_TYPE_NAME_KEY = "prefs.user_settings.default_object_type.name" @@ -136,5 +165,7 @@ class DefaultUserSettingsCache(private val prefs: SharedPreferences) : UserSetti const val THEME_TYPE_SYSTEM = 1 const val THEME_TYPE_LIGHT = 2 const val THEME_TYPE_NIGHT = 3 + + const val COLLAPSED_WIDGETS_KEY = "prefs.user_settings.collapsed-widgets" } } \ No newline at end of file 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 306dceaaaf..1c194f317d 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 @@ -16,6 +16,7 @@ import com.anytypeio.anytype.core_models.Payload import com.anytypeio.anytype.core_models.Position import com.anytypeio.anytype.core_models.Relations import com.anytypeio.anytype.core_models.WidgetLayout +import com.anytypeio.anytype.core_models.WidgetSession import com.anytypeio.anytype.core_models.ext.process import com.anytypeio.anytype.core_utils.ext.letNotNull import com.anytypeio.anytype.core_utils.ext.replace @@ -38,6 +39,8 @@ import com.anytypeio.anytype.domain.page.CloseBlock import com.anytypeio.anytype.domain.page.CreateObject import com.anytypeio.anytype.domain.widgets.CreateWidget import com.anytypeio.anytype.domain.widgets.DeleteWidget +import com.anytypeio.anytype.domain.widgets.GetWidgetSession +import com.anytypeio.anytype.domain.widgets.SaveWidgetSession import com.anytypeio.anytype.domain.widgets.UpdateWidget import com.anytypeio.anytype.presentation.extension.sendAnalyticsObjectCreateEvent import com.anytypeio.anytype.presentation.home.Command.ChangeWidgetType.Companion.UNDEFINED_LAYOUT_CODE @@ -74,6 +77,7 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.scan import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import timber.log.Timber class HomeScreenViewModel( @@ -99,7 +103,9 @@ class HomeScreenViewModel( private val unsubscriber: Unsubscriber, private val getDefaultPageType: GetDefaultPageType, private val appActionManager: AppActionManager, - private val analytics: Analytics + private val analytics: Analytics, + private val getWidgetSession: GetWidgetSession, + private val saveWidgetSession: SaveWidgetSession ) : NavigationViewModel(), Reducer, WidgetActiveViewStateHolder by widgetActiveViewStateHolder, @@ -111,6 +117,8 @@ class HomeScreenViewModel( val commands = MutableSharedFlow() val mode = MutableStateFlow(InteractionMode.Default) + private var isWidgetSessionRestored = false + private val isEmptyingBinInProgress = MutableStateFlow(false) private val objectViewState = MutableStateFlow(ObjectViewState.Idle) @@ -300,6 +308,11 @@ class HomeScreenViewModel( private fun proceedWithClosingWidgetObject(widgetObject: Id) { viewModelScope.launch { + saveWidgetSession.execute( + SaveWidgetSession.Params( + WidgetSession(collapsed = collapsedWidgetStateHolder.get()) + ) + ) val subscriptions = widgets.value.map { widget -> if (widget.source is Widget.Source.Bundled) widget.source.id @@ -697,6 +710,7 @@ class HomeScreenViewModel( } if (deletedWidgets.isNotEmpty()) { viewModelScope.launch { + collapsedWidgetStateHolder.onWidgetDeleted(deletedWidgets) unsubscriber.unsubscribe(deletedWidgets) } } @@ -704,7 +718,19 @@ class HomeScreenViewModel( fun onStart() { Timber.d("onStart") - proceedWithOpeningWidgetObject(widgetObject = configStorage.get().widgets) + if (!isWidgetSessionRestored) { + viewModelScope.launch { + val session = withContext(appCoroutineDispatchers.io) { + getWidgetSession.execute(Unit).getOrNull() + } + if (session != null && session.collapsed.isNotEmpty()) { + collapsedWidgetStateHolder.set(session.collapsed) + } + proceedWithOpeningWidgetObject(widgetObject = configStorage.get().widgets) + } + } else { + proceedWithOpeningWidgetObject(widgetObject = configStorage.get().widgets) + } } fun onStop() { @@ -836,7 +862,9 @@ class HomeScreenViewModel( private val unsubscriber: Unsubscriber, private val getDefaultPageType: GetDefaultPageType, private val appActionManager: AppActionManager, - private val analytics: Analytics + private val analytics: Analytics, + private val getWidgetSession: GetWidgetSession, + private val saveWidgetSession: SaveWidgetSession ) : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") override fun create(modelClass: Class): T = HomeScreenViewModel( @@ -862,7 +890,9 @@ class HomeScreenViewModel( unsubscriber = unsubscriber, getDefaultPageType = getDefaultPageType, appActionManager = appActionManager, - analytics = analytics + analytics = analytics, + getWidgetSession = getWidgetSession, + saveWidgetSession = saveWidgetSession ) as T } diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/widgets/CollapsedWidgetStateHolder.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/widgets/CollapsedWidgetStateHolder.kt index ba97a7b9d4..26cb8600b6 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/widgets/CollapsedWidgetStateHolder.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/widgets/CollapsedWidgetStateHolder.kt @@ -10,6 +10,9 @@ interface CollapsedWidgetStateHolder { fun onToggleCollapsedWidgetState(widget: Id) fun isCollapsed(widget: Id): Flow + fun set(collapsed: List) + fun get() : List + fun onWidgetDeleted(widgets: List) class Impl @Inject constructor(): CollapsedWidgetStateHolder { private val collapsedWidgets = MutableStateFlow>(emptyList()) @@ -29,5 +32,18 @@ interface CollapsedWidgetStateHolder { override fun isCollapsed(widget: Id) = collapsedWidgets.map { collapsed -> collapsed.contains(widget) } + + override fun set(collapsed: List) { + collapsedWidgets.value = collapsed + } + + override fun get(): List = collapsedWidgets.value + + override fun onWidgetDeleted(widgets: List) { + val curr = collapsedWidgets.value + if (curr.isNotEmpty()) { + collapsedWidgets.value = curr - widgets + } + } } } \ No newline at end of file diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/auth/DeleteAccountViewModelTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/auth/DeleteAccountViewModelTest.kt index a73e728b07..39aa7040fc 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/auth/DeleteAccountViewModelTest.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/auth/DeleteAccountViewModelTest.kt @@ -8,10 +8,13 @@ import com.anytypeio.anytype.domain.auth.interactor.Logout import com.anytypeio.anytype.domain.auth.repo.AuthRepository import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers import com.anytypeio.anytype.domain.config.ConfigStorage +import com.anytypeio.anytype.domain.config.UserSettingsRepository import com.anytypeio.anytype.domain.misc.AppActionManager import com.anytypeio.anytype.domain.search.RelationsSubscriptionManager import com.anytypeio.anytype.presentation.auth.account.DeletedAccountViewModel import com.anytypeio.anytype.presentation.util.CoroutinesTestRule +import java.time.Duration +import kotlin.test.assertEquals import kotlinx.coroutines.ExperimentalCoroutinesApi import org.junit.Before import org.junit.Rule @@ -21,8 +24,6 @@ import org.mockito.MockitoAnnotations import org.mockito.kotlin.times import org.mockito.kotlin.verifyBlocking import org.mockito.kotlin.verifyNoInteractions -import java.time.Duration -import kotlin.test.assertEquals @ExperimentalCoroutinesApi class DeleteAccountViewModelTest { @@ -42,6 +43,9 @@ class DeleteAccountViewModelTest { @Mock lateinit var repo: AuthRepository + @Mock + lateinit var userSettingsRepository: UserSettingsRepository + @Mock lateinit var helper: DateHelper @@ -71,8 +75,9 @@ class DeleteAccountViewModelTest { ) logout = Logout( repo = repo, - provider = configStorage, - dispatchers = testDispatchers + config = configStorage, + dispatchers = testDispatchers, + user = userSettingsRepository ) vm = DeletedAccountViewModel( restoreAccount = restoreAccount, diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/home/HomeScreenViewModelTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/home/HomeScreenViewModelTest.kt index 812ea9bbe7..4348db2549 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/home/HomeScreenViewModelTest.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/home/HomeScreenViewModelTest.kt @@ -10,6 +10,7 @@ import com.anytypeio.anytype.core_models.ObjectType import com.anytypeio.anytype.core_models.ObjectView import com.anytypeio.anytype.core_models.ObjectWrapper import com.anytypeio.anytype.core_models.Payload +import com.anytypeio.anytype.core_models.Relations import com.anytypeio.anytype.core_models.SmartBlockType import com.anytypeio.anytype.core_models.StubConfig import com.anytypeio.anytype.core_models.StubLinkToObjectBlock @@ -17,6 +18,7 @@ import com.anytypeio.anytype.core_models.StubObject import com.anytypeio.anytype.core_models.StubObjectView import com.anytypeio.anytype.core_models.StubSmartBlock import com.anytypeio.anytype.core_models.StubWidgetBlock +import com.anytypeio.anytype.core_models.WidgetSession import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers import com.anytypeio.anytype.domain.base.Resultat import com.anytypeio.anytype.domain.bin.EmptyBin @@ -36,6 +38,8 @@ import com.anytypeio.anytype.domain.page.CloseBlock import com.anytypeio.anytype.domain.page.CreateObject import com.anytypeio.anytype.domain.widgets.CreateWidget import com.anytypeio.anytype.domain.widgets.DeleteWidget +import com.anytypeio.anytype.domain.widgets.GetWidgetSession +import com.anytypeio.anytype.domain.widgets.SaveWidgetSession import com.anytypeio.anytype.domain.widgets.UpdateWidget import com.anytypeio.anytype.presentation.objects.ObjectIcon import com.anytypeio.anytype.presentation.search.Subscriptions @@ -136,6 +140,12 @@ class HomeScreenViewModelTest { @Mock lateinit var analytics: Analytics + @Mock + lateinit var saveWidgetSession: SaveWidgetSession + + @Mock + lateinit var getWidgetSession: GetWidgetSession + private val objectPayloadDispatcher = Dispatcher.Default() private val widgetEventDispatcher = Dispatcher.Default() @@ -184,6 +194,7 @@ class HomeScreenViewModelTest { stubInterceptEvents(events) stubOpenObject(givenObjectView) stubCollapsedWidgetState(any()) + stubGetWidgetSession() val vm = buildViewModel() @@ -204,6 +215,26 @@ class HomeScreenViewModelTest { verify(openObject, times(1)).stream(OpenObject.Params(WIDGET_OBJECT_ID, false)) } + private fun stubGetWidgetSession( + session: WidgetSession = WidgetSession(emptyList()) + ) { + getWidgetSession.stub { + onBlocking { + execute(any()) + } doReturn Resultat.Success(session) + } + } + + private fun stubSaveWidgetSession( + session: WidgetSession = WidgetSession(emptyList()) + ) { + saveWidgetSession.stub { + onBlocking { + execute(SaveWidgetSession.Params(session)) + } doReturn Resultat.Success(Unit) + } + } + @Test fun `should emit only bin and actions when home screen has no associated widgets except the default ones`() = runTest { @@ -229,6 +260,7 @@ class HomeScreenViewModelTest { stubInterceptEvents(events = emptyFlow()) stubOpenObject(givenObjectView) stubCollapsedWidgetState(any()) + stubGetWidgetSession() val vm = buildViewModel() @@ -306,6 +338,7 @@ class HomeScreenViewModelTest { ) stubCollapsedWidgetState(any()) stubWidgetActiveView(widgetBlock) + stubGetWidgetSession() val vm = buildViewModel() @@ -412,6 +445,7 @@ class HomeScreenViewModelTest { stubCollapsedWidgetState(any()) stubWidgetActiveView(widgetBlock) + stubGetWidgetSession() val vm = buildViewModel() @@ -590,6 +624,7 @@ class HomeScreenViewModelTest { ) stubCollapsedWidgetState(any()) + stubGetWidgetSession() stubWidgetActiveView(favoriteWidgetBlock) val vm = buildViewModel() @@ -744,7 +779,7 @@ class HomeScreenViewModelTest { targets = emptyList() ) stubCollapsedWidgetState(any()) - + stubGetWidgetSession() val vm = buildViewModel() @@ -836,6 +871,30 @@ class HomeScreenViewModelTest { targets = emptyList() ) stubCollapsedWidgetState(any()) + stubGetWidgetSession() + stubSaveWidgetSession() + + getDefaultPageType.stub { + onBlocking { + execute(any()) + } doReturn Resultat.Success( + GetDefaultPageType.Response( + null, null + ) + ) + } + + storelessSubscriptionContainer.stub { + onBlocking { + subscribe( + StoreSearchByIdsParams( + subscription = HomeScreenViewModel.HOME_SCREEN_SPACE_OBJECT_SUBSCRIPTION, + targets = listOf(config.workspace), + keys = listOf(Relations.ID, Relations.ICON_EMOJI, Relations.ICON_IMAGE) + ) + ) + } doReturn emptyFlow() + } val givenPayload = Payload( context = WIDGET_OBJECT_ID, @@ -869,6 +928,8 @@ class HomeScreenViewModelTest { vm.onStart() + advanceUntilIdle() + vm.onDropDownMenuAction( widget = widgetBlock.id, DropDownMenuAction.RemoveWidget @@ -951,6 +1012,7 @@ class HomeScreenViewModelTest { targets = emptyList() ) stubCollapsedWidgetState(any()) + stubGetWidgetSession() val vm = buildViewModel() @@ -1019,6 +1081,7 @@ class HomeScreenViewModelTest { targets = emptyList() ) stubCollapsedWidgetState(any()) + stubGetWidgetSession() stubCloseObject() val vm = buildViewModel() @@ -1148,6 +1211,7 @@ class HomeScreenViewModelTest { ) stubCollapsedWidgetState(any()) + stubGetWidgetSession() stubWidgetActiveView(favoriteWidgetBlock) val vm = buildViewModel() @@ -1282,8 +1346,8 @@ class HomeScreenViewModelTest { ) stubCollapsedWidgetState(any()) + stubGetWidgetSession() stubWidgetActiveView(favoriteWidgetBlock) - stubCloseObject() val vm = buildViewModel() @@ -1486,6 +1550,7 @@ class HomeScreenViewModelTest { ) stubCollapsedWidgetState(any()) + stubGetWidgetSession() stubWidgetActiveView(favoriteWidgetBlock) stubCloseObject() @@ -1699,7 +1764,7 @@ class HomeScreenViewModelTest { targets = emptyList() ) stubCollapsedWidgetState(any()) - + stubGetWidgetSession() val vm = buildViewModel() @@ -1768,7 +1833,7 @@ class HomeScreenViewModelTest { targets = emptyList() ) stubCollapsedWidgetState(any()) - + stubGetWidgetSession() val vm = buildViewModel() @@ -1888,7 +1953,9 @@ class HomeScreenViewModelTest { unsubscriber = unsubscriber, getDefaultPageType = getDefaultPageType, appActionManager = appActionManager, - analytics = analytics + analytics = analytics, + getWidgetSession = getWidgetSession, + saveWidgetSession = saveWidgetSession ) companion object {