From 0a0c28910f5d0eff8b0896baa2bcc061be0473e3 Mon Sep 17 00:00:00 2001 From: rainxchzed Date: Fri, 20 Mar 2026 23:27:39 +0500 Subject: [PATCH 1/3] feat: implement "Liquid Glass" toggle and rename theme repository - Rename `ThemesRepository` to `TweaksRepository` to reflect its expanded scope of managing various app behaviors and settings. - Implement a new "Liquid Glass Effect" setting in the Appearance section, allowing users to toggle the smooth glass-like UI effects. - Update `ProfileViewModel`, `HomeViewModel`, `SearchViewModel`, `AppsViewModel`, and `DetailsViewModel` to observe and react to the `isLiquidGlassEnabled` state. - Conditionally apply `Modifier.liquefiable` and liquid frost backgrounds across multiple UI components (TopBars, BottomNavigation, FilterChips, and content sections) based on the user's preference. - Refactor dependency injection and repository usages to support the new `TweaksRepository` interface. - Add localized strings for the Liquid Glass option title and description. - Adjust layout and padding in the Profile screen settings sections. --- .../rainxch/githubstore/app/GithubStoreApp.kt | 4 +- .../kotlin/zed/rainxch/githubstore/Main.kt | 1 + .../zed/rainxch/githubstore/MainState.kt | 1 + .../zed/rainxch/githubstore/MainViewModel.kt | 18 +- .../githubstore/app/di/SharedModules.kt | 2 +- .../app/navigation/AppNavigation.kt | 6 +- .../app/navigation/BottomNavigation.kt | 45 +++-- .../core/data/di/PlatformModule.android.kt | 159 ++++++++------- .../core/data/services/AutoUpdateWorker.kt | 8 +- .../core/data/services/UpdateCheckWorker.kt | 14 +- .../shizuku/ShizukuInstallerWrapper.kt | 6 +- .../zed/rainxch/core/data/di/SharedModule.kt | 10 +- .../repository/InstalledAppsRepositoryImpl.kt | 6 +- ...ositoryImpl.kt => TweaksRepositoryImpl.kt} | 23 ++- ...hemesRepository.kt => TweaksRepository.kt} | 25 ++- .../composeResources/values/strings.xml | 3 + .../zed/rainxch/apps/data/di/SharedModule.kt | 2 +- .../data/repository/AppsRepositoryImpl.kt | 8 +- .../zed/rainxch/apps/presentation/AppsRoot.kt | 26 ++- .../rainxch/apps/presentation/AppsState.kt | 1 + .../apps/presentation/AppsViewModel.kt | 15 ++ .../details/presentation/DetailsRoot.kt | 40 +++- .../details/presentation/DetailsState.kt | 5 +- .../details/presentation/DetailsViewModel.kt | 13 ++ .../components/SmartInstallButton.kt | 188 ++++++++++-------- .../presentation/components/sections/About.kt | 25 ++- .../components/sections/Header.kt | 37 +++- .../presentation/components/sections/Logs.kt | 17 +- .../presentation/components/sections/Owner.kt | 35 +++- .../presentation/components/sections/Stats.kt | 29 ++- .../components/sections/WhatsNew.kt | 38 +++- .../zed/rainxch/home/presentation/HomeRoot.kt | 23 ++- .../rainxch/home/presentation/HomeState.kt | 1 + .../home/presentation/HomeViewModel.kt | 13 ++ .../components/HomeFilterChips.kt | 46 +++-- .../profile/presentation/ProfileAction.kt | 25 ++- .../profile/presentation/ProfileRoot.kt | 35 +--- .../profile/presentation/ProfileState.kt | 1 + .../profile/presentation/ProfileViewModel.kt | 121 ++++++----- .../components/sections/Appearance.kt | 13 +- .../components/sections/SettingsSection.kt | 27 +++ .../rainxch/search/presentation/SearchRoot.kt | 24 ++- .../search/presentation/SearchState.kt | 1 + .../search/presentation/SearchViewModel.kt | 21 +- 44 files changed, 784 insertions(+), 377 deletions(-) rename core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/{ThemesRepositoryImpl.kt => TweaksRepositoryImpl.kt} (87%) rename core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/repository/{ThemesRepository.kt => TweaksRepository.kt} (87%) diff --git a/composeApp/src/androidMain/kotlin/zed/rainxch/githubstore/app/GithubStoreApp.kt b/composeApp/src/androidMain/kotlin/zed/rainxch/githubstore/app/GithubStoreApp.kt index bdaa982d..d20510df 100644 --- a/composeApp/src/androidMain/kotlin/zed/rainxch/githubstore/app/GithubStoreApp.kt +++ b/composeApp/src/androidMain/kotlin/zed/rainxch/githubstore/app/GithubStoreApp.kt @@ -17,7 +17,7 @@ import zed.rainxch.core.data.services.UpdateScheduler import zed.rainxch.core.domain.model.InstallSource import zed.rainxch.core.domain.model.InstalledApp import zed.rainxch.core.domain.repository.InstalledAppsRepository -import zed.rainxch.core.domain.repository.ThemesRepository +import zed.rainxch.core.domain.repository.TweaksRepository import zed.rainxch.core.domain.system.PackageMonitor import zed.rainxch.githubstore.app.di.initKoin @@ -83,7 +83,7 @@ class GithubStoreApp : Application() { private fun scheduleBackgroundUpdateChecks() { appScope.launch { try { - val intervalHours = get().getUpdateCheckInterval().first() + val intervalHours = get().getUpdateCheckInterval().first() UpdateScheduler.schedule( context = this@GithubStoreApp, intervalHours = intervalHours, diff --git a/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/Main.kt b/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/Main.kt index d9cb817b..ff98da3d 100644 --- a/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/Main.kt +++ b/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/Main.kt @@ -102,6 +102,7 @@ fun App(deepLinkUri: String? = null) { AppNavigation( navController = navController, + isLiquidGlassEnabled = state.isLiquidGlassEnabled, ) } } diff --git a/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/MainState.kt b/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/MainState.kt index aa5dd430..2a0ac885 100644 --- a/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/MainState.kt +++ b/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/MainState.kt @@ -13,4 +13,5 @@ data class MainState( val isAmoledTheme: Boolean = false, val isDarkTheme: Boolean? = null, val currentFontTheme: FontTheme = FontTheme.CUSTOM, + val isLiquidGlassEnabled: Boolean = true, ) diff --git a/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/MainViewModel.kt b/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/MainViewModel.kt index 343258e7..8cb187a7 100644 --- a/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/MainViewModel.kt +++ b/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/MainViewModel.kt @@ -10,11 +10,11 @@ import kotlinx.coroutines.launch import zed.rainxch.core.domain.repository.AuthenticationState import zed.rainxch.core.domain.repository.InstalledAppsRepository import zed.rainxch.core.domain.repository.RateLimitRepository -import zed.rainxch.core.domain.repository.ThemesRepository +import zed.rainxch.core.domain.repository.TweaksRepository import zed.rainxch.core.domain.use_cases.SyncInstalledAppsUseCase class MainViewModel( - private val themesRepository: ThemesRepository, + private val tweaksRepository: TweaksRepository, private val installedAppsRepository: InstalledAppsRepository, private val authenticationState: AuthenticationState, private val rateLimitRepository: RateLimitRepository, @@ -37,7 +37,7 @@ class MainViewModel( } viewModelScope.launch { - themesRepository + tweaksRepository .getThemeColor() .collect { theme -> _state.update { @@ -46,7 +46,7 @@ class MainViewModel( } } viewModelScope.launch { - themesRepository + tweaksRepository .getAmoledTheme() .collect { isAmoled -> _state.update { @@ -55,7 +55,7 @@ class MainViewModel( } } viewModelScope.launch { - themesRepository + tweaksRepository .getIsDarkTheme() .collect { isDarkTheme -> _state.update { @@ -65,7 +65,7 @@ class MainViewModel( } viewModelScope.launch { - themesRepository + tweaksRepository .getFontTheme() .collect { fontTheme -> _state.update { @@ -74,6 +74,12 @@ class MainViewModel( } } + viewModelScope.launch { + tweaksRepository.getLiquidGlassEnabled().collect { enabled -> + _state.update { it.copy(isLiquidGlassEnabled = enabled) } + } + } + viewModelScope.launch { rateLimitRepository.rateLimitState.collect { rateLimitInfo -> _state.update { currentState -> diff --git a/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/di/SharedModules.kt b/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/di/SharedModules.kt index b25bcb36..8cdcb9c7 100644 --- a/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/di/SharedModules.kt +++ b/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/di/SharedModules.kt @@ -9,7 +9,7 @@ val mainModule: Module = module { viewModel { MainViewModel( - themesRepository = get(), + tweaksRepository = get(), installedAppsRepository = get(), rateLimitRepository = get(), syncUseCase = get(), diff --git a/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/navigation/AppNavigation.kt b/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/navigation/AppNavigation.kt index 1e0e0743..0f435ee1 100644 --- a/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/navigation/AppNavigation.kt +++ b/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/navigation/AppNavigation.kt @@ -40,7 +40,10 @@ import zed.rainxch.search.presentation.SearchRoot import zed.rainxch.starred.presentation.StarredReposRoot @Composable -fun AppNavigation(navController: NavHostController) { +fun AppNavigation( + navController: NavHostController, + isLiquidGlassEnabled: Boolean = true, +) { val liquidState = rememberLiquidState() var bottomNavigationHeight by remember { mutableStateOf(0.dp) } val density = LocalDensity.current @@ -294,6 +297,7 @@ fun AppNavigation(navController: NavHostController) { } }, isUpdateAvailable = appsState.apps.any { it.installedApp.isUpdateAvailable }, + isLiquidGlassEnabled = isLiquidGlassEnabled, modifier = Modifier .align(Alignment.BottomCenter) diff --git a/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/navigation/BottomNavigation.kt b/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/navigation/BottomNavigation.kt index e4442c25..d044b935 100644 --- a/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/navigation/BottomNavigation.kt +++ b/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/navigation/BottomNavigation.kt @@ -8,6 +8,7 @@ import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.spring import androidx.compose.animation.core.tween import androidx.compose.foundation.background +import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.collectIsPressedAsState @@ -65,6 +66,7 @@ fun BottomNavigation( currentScreen: GithubStoreGraph, onNavigate: (GithubStoreGraph) -> Unit, isUpdateAvailable: Boolean, + isLiquidGlassEnabled: Boolean = true, modifier: Modifier = Modifier, ) { val liquidState = LocalBottomNavigationLiquid.current @@ -129,31 +131,36 @@ fun BottomNavigation( modifier = modifier, contentAlignment = Alignment.Center, ) { + val useLiquid = isLiquidGlassEnabled && isLiquidFrostAvailable() + Box( modifier = Modifier .clip(CircleShape) - .background( - if (isLiquidFrostAvailable()) { - MaterialTheme.colorScheme.surfaceContainerHighest.copy( - alpha = if (isDarkTheme) .25f else .15f, - ) - } else { - MaterialTheme.colorScheme.surfaceContainerHighest - }, - ).then( - if (isLiquidFrostAvailable()) { - Modifier.liquid(liquidState) { - this.shape = CircleShape - this.frost = if (isDarkTheme) 12.dp else 10.dp - this.curve = if (isDarkTheme) .35f else .45f - this.refraction = if (isDarkTheme) .08f else .12f - this.dispersion = if (isDarkTheme) .18f else .25f - this.saturation = if (isDarkTheme) .40f else .55f - this.contrast = if (isDarkTheme) 1.8f else 1.6f - } + .then( + if (useLiquid) { + Modifier + .background( + MaterialTheme.colorScheme.surfaceContainerHighest.copy( + alpha = if (isDarkTheme) .25f else .15f, + ), + ).liquid(liquidState) { + this.shape = CircleShape + this.frost = if (isDarkTheme) 12.dp else 10.dp + this.curve = if (isDarkTheme) .35f else .45f + this.refraction = if (isDarkTheme) .08f else .12f + this.dispersion = if (isDarkTheme) .18f else .25f + this.saturation = if (isDarkTheme) .40f else .55f + this.contrast = if (isDarkTheme) 1.8f else 1.6f + } } else { Modifier + .background(MaterialTheme.colorScheme.surfaceContainer) + .border( + width = 1.dp, + color = MaterialTheme.colorScheme.outlineVariant, + shape = CircleShape, + ) }, ).pointerInput(Unit) { }, ) { diff --git a/core/data/src/androidMain/kotlin/zed/rainxch/core/data/di/PlatformModule.android.kt b/core/data/src/androidMain/kotlin/zed/rainxch/core/data/di/PlatformModule.android.kt index ba00fa8f..4b33328b 100644 --- a/core/data/src/androidMain/kotlin/zed/rainxch/core/data/di/PlatformModule.android.kt +++ b/core/data/src/androidMain/kotlin/zed/rainxch/core/data/di/PlatformModule.android.kt @@ -8,12 +8,13 @@ import org.koin.dsl.module import zed.rainxch.core.data.local.data_store.createDataStore import zed.rainxch.core.data.local.db.AppDatabase import zed.rainxch.core.data.local.db.initDatabase -import zed.rainxch.core.data.services.AndroidInstallerInfoExtractor import zed.rainxch.core.data.services.AndroidDownloader import zed.rainxch.core.data.services.AndroidFileLocationsProvider import zed.rainxch.core.data.services.AndroidInstaller +import zed.rainxch.core.data.services.AndroidInstallerInfoExtractor import zed.rainxch.core.data.services.AndroidLocalizationManager import zed.rainxch.core.data.services.AndroidPackageMonitor +import zed.rainxch.core.data.services.AndroidUpdateScheduleManager import zed.rainxch.core.data.services.FileLocationsProvider import zed.rainxch.core.data.services.LocalizationManager import zed.rainxch.core.data.services.shizuku.AndroidInstallerStatusProvider @@ -24,7 +25,6 @@ import zed.rainxch.core.data.utils.AndroidBrowserHelper import zed.rainxch.core.data.utils.AndroidClipboardHelper import zed.rainxch.core.data.utils.AndroidShareManager import zed.rainxch.core.domain.network.Downloader -import zed.rainxch.core.data.services.AndroidUpdateScheduleManager import zed.rainxch.core.domain.system.Installer import zed.rainxch.core.domain.system.InstallerStatusProvider import zed.rainxch.core.domain.system.PackageMonitor @@ -34,100 +34,99 @@ import zed.rainxch.core.domain.utils.BrowserHelper import zed.rainxch.core.domain.utils.ClipboardHelper import zed.rainxch.core.domain.utils.ShareManager -actual val corePlatformModule = module { - // Core +actual val corePlatformModule = + module { + // Core - single { - AndroidDownloader( - files = get(), - ) - } - - // AndroidInstaller — registered by class so the wrapper can inject it - single { - AndroidInstaller( - context = get(), - installerInfoExtractor = AndroidInstallerInfoExtractor(androidContext()) - ) - } + single { + AndroidDownloader( + files = get(), + ) + } - // ShizukuServiceManager — manages Shizuku lifecycle, permissions, service binding - single { - ShizukuServiceManager( - context = androidContext() - ).also { it.initialize() } - } + // AndroidInstaller — registered by class so the wrapper can inject it + single { + AndroidInstaller( + context = get(), + installerInfoExtractor = AndroidInstallerInfoExtractor(androidContext()), + ) + } - // Installer — the ShizukuInstallerWrapper is the public Installer singleton. - // It delegates to AndroidInstaller by default, intercepting with Shizuku when enabled. - single { - ShizukuInstallerWrapper( - androidInstaller = get(), - shizukuServiceManager = get(), - themesRepository = get() - ).also { wrapper -> - wrapper.observeInstallerPreference(get()) + // ShizukuServiceManager — manages Shizuku lifecycle, permissions, service binding + single { + ShizukuServiceManager( + context = androidContext(), + ).also { it.initialize() } } - } - // InstallerStatusProvider — exposes Shizuku availability to the UI layer - single { - AndroidInstallerStatusProvider( - shizukuServiceManager = get(), - scope = get() - ) - } + // Installer — the ShizukuInstallerWrapper is the public Installer singleton. + // It delegates to AndroidInstaller by default, intercepting with Shizuku when enabled. + single { + ShizukuInstallerWrapper( + androidInstaller = get(), + shizukuServiceManager = get(), + tweaksRepository = get(), + ).also { wrapper -> + wrapper.observeInstallerPreference(get()) + } + } - single { - AndroidFileLocationsProvider(context = get()) - } + // InstallerStatusProvider — exposes Shizuku availability to the UI layer + single { + AndroidInstallerStatusProvider( + shizukuServiceManager = get(), + scope = get(), + ) + } - single { - AndroidPackageMonitor(androidContext()) - } + single { + AndroidFileLocationsProvider(context = get()) + } - single { - AndroidLocalizationManager() - } + single { + AndroidPackageMonitor(androidContext()) + } - // Locals + single { + AndroidLocalizationManager() + } - single { - initDatabase(androidContext()) - } + // Locals - single> { - createDataStore(androidContext()) - } + single { + initDatabase(androidContext()) + } + single> { + createDataStore(androidContext()) + } - // Utils + // Utils - single { - AndroidBrowserHelper(androidContext()) - } + single { + AndroidBrowserHelper(androidContext()) + } - single { - AndroidClipboardHelper(androidContext()) - } + single { + AndroidClipboardHelper(androidContext()) + } - single { - AndroidAppLauncher( - context = androidContext(), - logger = get() - ) - } + single { + AndroidAppLauncher( + context = androidContext(), + logger = get(), + ) + } - single { - AndroidShareManager( - context = androidContext() - ) - } + single { + AndroidShareManager( + context = androidContext(), + ) + } - single { - AndroidUpdateScheduleManager( - context = androidContext() - ) + single { + AndroidUpdateScheduleManager( + context = androidContext(), + ) + } } - -} diff --git a/core/data/src/androidMain/kotlin/zed/rainxch/core/data/services/AutoUpdateWorker.kt b/core/data/src/androidMain/kotlin/zed/rainxch/core/data/services/AutoUpdateWorker.kt index 8bd28415..eae4abe8 100644 --- a/core/data/src/androidMain/kotlin/zed/rainxch/core/data/services/AutoUpdateWorker.kt +++ b/core/data/src/androidMain/kotlin/zed/rainxch/core/data/services/AutoUpdateWorker.kt @@ -24,7 +24,7 @@ import zed.rainxch.core.domain.model.InstalledApp import zed.rainxch.core.domain.model.InstallerType import zed.rainxch.core.domain.network.Downloader import zed.rainxch.core.domain.repository.InstalledAppsRepository -import zed.rainxch.core.domain.repository.ThemesRepository +import zed.rainxch.core.domain.repository.TweaksRepository import zed.rainxch.core.domain.system.Installer /** @@ -43,15 +43,15 @@ class AutoUpdateWorker( private val installedAppsRepository: InstalledAppsRepository by inject() private val installer: Installer by inject() private val downloader: Downloader by inject() - private val themesRepository: ThemesRepository by inject() + private val tweaksRepository: TweaksRepository by inject() private val shizukuServiceManager: ShizukuServiceManager by inject() override suspend fun doWork(): Result { return try { Logger.i { "AutoUpdateWorker: Starting auto-update" } - val autoUpdateEnabled = themesRepository.getAutoUpdateEnabled().first() - val installerType = themesRepository.getInstallerType().first() + val autoUpdateEnabled = tweaksRepository.getAutoUpdateEnabled().first() + val installerType = tweaksRepository.getInstallerType().first() shizukuServiceManager.refreshStatus() val shizukuReady = shizukuServiceManager.status.value == ShizukuStatus.READY diff --git a/core/data/src/androidMain/kotlin/zed/rainxch/core/data/services/UpdateCheckWorker.kt b/core/data/src/androidMain/kotlin/zed/rainxch/core/data/services/UpdateCheckWorker.kt index 1f907060..ab3496e3 100644 --- a/core/data/src/androidMain/kotlin/zed/rainxch/core/data/services/UpdateCheckWorker.kt +++ b/core/data/src/androidMain/kotlin/zed/rainxch/core/data/services/UpdateCheckWorker.kt @@ -18,9 +18,9 @@ import co.touchlab.kermit.Logger import kotlinx.coroutines.flow.first import org.koin.core.component.KoinComponent import org.koin.core.component.inject -import zed.rainxch.core.domain.repository.InstalledAppsRepository -import zed.rainxch.core.domain.repository.ThemesRepository import zed.rainxch.core.domain.model.InstallerType +import zed.rainxch.core.domain.repository.InstalledAppsRepository +import zed.rainxch.core.domain.repository.TweaksRepository import zed.rainxch.core.domain.use_cases.SyncInstalledAppsUseCase /** @@ -39,7 +39,7 @@ class UpdateCheckWorker( KoinComponent { private val installedAppsRepository: InstalledAppsRepository by inject() private val syncInstalledAppsUseCase: SyncInstalledAppsUseCase by inject() - private val themesRepository: ThemesRepository by inject() + private val tweaksRepository: TweaksRepository by inject() override suspend fun doWork(): Result = try { @@ -61,11 +61,13 @@ class UpdateCheckWorker( if (appsWithUpdates.isNotEmpty()) { // Check if auto-update via Shizuku is enabled - val autoUpdateEnabled = themesRepository.getAutoUpdateEnabled().first() - val installerType = themesRepository.getInstallerType().first() + val autoUpdateEnabled = tweaksRepository.getAutoUpdateEnabled().first() + val installerType = tweaksRepository.getInstallerType().first() if (autoUpdateEnabled && installerType == InstallerType.SHIZUKU) { - Logger.i { "UpdateCheckWorker: Auto-update enabled with Shizuku, scheduling AutoUpdateWorker for ${appsWithUpdates.size} apps" } + Logger.i { + "UpdateCheckWorker: Auto-update enabled with Shizuku, scheduling AutoUpdateWorker for ${appsWithUpdates.size} apps" + } UpdateScheduler.scheduleAutoUpdate(applicationContext) } else { // Show notification for manual update diff --git a/core/data/src/androidMain/kotlin/zed/rainxch/core/data/services/shizuku/ShizukuInstallerWrapper.kt b/core/data/src/androidMain/kotlin/zed/rainxch/core/data/services/shizuku/ShizukuInstallerWrapper.kt index 6ae4b068..3a8ca1a9 100644 --- a/core/data/src/androidMain/kotlin/zed/rainxch/core/data/services/shizuku/ShizukuInstallerWrapper.kt +++ b/core/data/src/androidMain/kotlin/zed/rainxch/core/data/services/shizuku/ShizukuInstallerWrapper.kt @@ -10,7 +10,7 @@ import zed.rainxch.core.data.services.shizuku.model.ShizukuStatus import zed.rainxch.core.domain.model.GithubAsset import zed.rainxch.core.domain.model.InstallerType import zed.rainxch.core.domain.model.SystemArchitecture -import zed.rainxch.core.domain.repository.ThemesRepository +import zed.rainxch.core.domain.repository.TweaksRepository import zed.rainxch.core.domain.system.Installer import zed.rainxch.core.domain.system.InstallerInfoExtractor @@ -27,7 +27,7 @@ import zed.rainxch.core.domain.system.InstallerInfoExtractor class ShizukuInstallerWrapper( private val androidInstaller: Installer, private val shizukuServiceManager: ShizukuServiceManager, - private val themesRepository: ThemesRepository, + private val tweaksRepository: TweaksRepository, ) : Installer { companion object { private const val TAG = "ShizukuInstaller" @@ -46,7 +46,7 @@ class ShizukuInstallerWrapper( */ fun observeInstallerPreference(scope: CoroutineScope) { scope.launch { - themesRepository.getInstallerType().collect { type -> + tweaksRepository.getInstallerType().collect { type -> cachedInstallerType = type Logger.d(TAG) { "Installer type changed to: $type" } } diff --git a/core/data/src/commonMain/kotlin/zed/rainxch/core/data/di/SharedModule.kt b/core/data/src/commonMain/kotlin/zed/rainxch/core/data/di/SharedModule.kt index ec7c5f45..c008ab88 100644 --- a/core/data/src/commonMain/kotlin/zed/rainxch/core/data/di/SharedModule.kt +++ b/core/data/src/commonMain/kotlin/zed/rainxch/core/data/di/SharedModule.kt @@ -27,7 +27,7 @@ import zed.rainxch.core.data.repository.InstalledAppsRepositoryImpl import zed.rainxch.core.data.repository.ProxyRepositoryImpl import zed.rainxch.core.data.repository.RateLimitRepositoryImpl import zed.rainxch.core.data.repository.StarredRepositoryImpl -import zed.rainxch.core.data.repository.ThemesRepositoryImpl +import zed.rainxch.core.data.repository.TweaksRepositoryImpl import zed.rainxch.core.domain.getPlatform import zed.rainxch.core.domain.logging.GitHubStoreLogger import zed.rainxch.core.domain.model.Platform @@ -38,7 +38,7 @@ import zed.rainxch.core.domain.repository.InstalledAppsRepository import zed.rainxch.core.domain.repository.ProxyRepository import zed.rainxch.core.domain.repository.RateLimitRepository import zed.rainxch.core.domain.repository.StarredRepository -import zed.rainxch.core.domain.repository.ThemesRepository +import zed.rainxch.core.domain.repository.TweaksRepository import zed.rainxch.core.domain.use_cases.SyncInstalledAppsUseCase val coreModule = @@ -75,7 +75,7 @@ val coreModule = historyDao = get(), installer = get(), httpClient = get(), - themesRepository = get(), + tweaksRepository = get(), ) } @@ -88,8 +88,8 @@ val coreModule = ) } - single { - ThemesRepositoryImpl( + single { + TweaksRepositoryImpl( preferences = get(), ) } diff --git a/core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/InstalledAppsRepositoryImpl.kt b/core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/InstalledAppsRepositoryImpl.kt index 3628947c..6c6531b4 100644 --- a/core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/InstalledAppsRepositoryImpl.kt +++ b/core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/InstalledAppsRepositoryImpl.kt @@ -23,7 +23,7 @@ import zed.rainxch.core.domain.model.GithubRelease import zed.rainxch.core.domain.model.InstallSource import zed.rainxch.core.domain.model.InstalledApp import zed.rainxch.core.domain.repository.InstalledAppsRepository -import zed.rainxch.core.domain.repository.ThemesRepository +import zed.rainxch.core.domain.repository.TweaksRepository import zed.rainxch.core.domain.system.Installer class InstalledAppsRepositoryImpl( @@ -32,7 +32,7 @@ class InstalledAppsRepositoryImpl( private val historyDao: UpdateHistoryDao, private val installer: Installer, private val httpClient: HttpClient, - private val themesRepository: ThemesRepository, + private val tweaksRepository: TweaksRepository, ) : InstalledAppsRepository { override suspend fun executeInTransaction(block: suspend () -> R): R = database.useWriterConnection { transactor -> @@ -78,7 +78,7 @@ class InstalledAppsRepositoryImpl( repo: String, ): GithubRelease? { return try { - val includePreReleases = themesRepository.getIncludePreReleases().first() + val includePreReleases = tweaksRepository.getIncludePreReleases().first() val releases = httpClient diff --git a/core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/ThemesRepositoryImpl.kt b/core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/TweaksRepositoryImpl.kt similarity index 87% rename from core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/ThemesRepositoryImpl.kt rename to core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/TweaksRepositoryImpl.kt index 89ae3202..0cfc3fa4 100644 --- a/core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/ThemesRepositoryImpl.kt +++ b/core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/TweaksRepositoryImpl.kt @@ -11,11 +11,11 @@ import kotlinx.coroutines.flow.map import zed.rainxch.core.domain.model.AppTheme import zed.rainxch.core.domain.model.FontTheme import zed.rainxch.core.domain.model.InstallerType -import zed.rainxch.core.domain.repository.ThemesRepository +import zed.rainxch.core.domain.repository.TweaksRepository -class ThemesRepositoryImpl( +class TweaksRepositoryImpl( private val preferences: DataStore, -) : ThemesRepository { +) : TweaksRepository { private val THEME_KEY = stringPreferencesKey("app_theme") private val AMOLED_KEY = booleanPreferencesKey("amoled_theme") private val IS_DARK_THEME_KEY = booleanPreferencesKey("is_dark_theme") @@ -25,6 +25,7 @@ class ThemesRepositoryImpl( private val AUTO_UPDATE_KEY = booleanPreferencesKey("auto_update_enabled") private val UPDATE_CHECK_INTERVAL_KEY = longPreferencesKey("update_check_interval_hours") private val INCLUDE_PRE_RELEASES_KEY = booleanPreferencesKey("include_pre_releases") + private val LIQUID_GLASS_ENABLED_KEY = booleanPreferencesKey("liquid_glass_enabled") override fun getThemeColor(): Flow = preferences.data.map { prefs -> @@ -87,12 +88,11 @@ class ThemesRepositoryImpl( } } - override fun getInstallerType(): Flow { - return preferences.data.map { prefs -> + override fun getInstallerType(): Flow = + preferences.data.map { prefs -> val name = prefs[INSTALLER_TYPE_KEY] InstallerType.fromName(name) } - } override suspend fun setInstallerType(type: InstallerType) { preferences.edit { prefs -> @@ -133,6 +133,17 @@ class ThemesRepositoryImpl( } } + override fun getLiquidGlassEnabled(): Flow = + preferences.data.map { prefs -> + prefs[LIQUID_GLASS_ENABLED_KEY] ?: true + } + + override suspend fun setLiquidGlassEnabled(enabled: Boolean) { + preferences.edit { prefs -> + prefs[LIQUID_GLASS_ENABLED_KEY] = enabled + } + } + companion object { const val DEFAULT_UPDATE_CHECK_INTERVAL_HOURS = 6L } diff --git a/core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/repository/ThemesRepository.kt b/core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/repository/TweaksRepository.kt similarity index 87% rename from core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/repository/ThemesRepository.kt rename to core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/repository/TweaksRepository.kt index f307ca04..d7e5f5ce 100644 --- a/core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/repository/ThemesRepository.kt +++ b/core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/repository/TweaksRepository.kt @@ -5,23 +5,44 @@ import zed.rainxch.core.domain.model.AppTheme import zed.rainxch.core.domain.model.FontTheme import zed.rainxch.core.domain.model.InstallerType -interface ThemesRepository { +interface TweaksRepository { fun getThemeColor(): Flow + suspend fun setThemeColor(theme: AppTheme) + fun getIsDarkTheme(): Flow + suspend fun setDarkTheme(isDarkTheme: Boolean?) + fun getAmoledTheme(): Flow + suspend fun setAmoledTheme(enabled: Boolean) + fun getFontTheme(): Flow + suspend fun setFontTheme(fontTheme: FontTheme) + fun getAutoDetectClipboardLinks(): Flow + suspend fun setAutoDetectClipboardLinks(enabled: Boolean) + fun getInstallerType(): Flow + suspend fun setInstallerType(type: InstallerType) + fun getAutoUpdateEnabled(): Flow + suspend fun setAutoUpdateEnabled(enabled: Boolean) + fun getUpdateCheckInterval(): Flow + suspend fun setUpdateCheckInterval(hours: Long) + fun getIncludePreReleases(): Flow + suspend fun setIncludePreReleases(enabled: Boolean) -} \ No newline at end of file + + fun getLiquidGlassEnabled(): Flow + + suspend fun setLiquidGlassEnabled(enabled: Boolean) +} diff --git a/core/presentation/src/commonMain/composeResources/values/strings.xml b/core/presentation/src/commonMain/composeResources/values/strings.xml index 818ffd1b..9c3b2964 100644 --- a/core/presentation/src/commonMain/composeResources/values/strings.xml +++ b/core/presentation/src/commonMain/composeResources/values/strings.xml @@ -583,4 +583,7 @@ , %1$d failed Package mismatch: the APK is %1$s, but the installed app is %2$s. Update blocked. Signing key mismatch: the update was signed by a different developer. Update blocked. + + Liquid Glass Effect + Enhance the interface with a smooth glass-like appearance \ No newline at end of file diff --git a/feature/apps/data/src/commonMain/kotlin/zed/rainxch/apps/data/di/SharedModule.kt b/feature/apps/data/src/commonMain/kotlin/zed/rainxch/apps/data/di/SharedModule.kt index ffe42d76..ccf7fea2 100644 --- a/feature/apps/data/src/commonMain/kotlin/zed/rainxch/apps/data/di/SharedModule.kt +++ b/feature/apps/data/src/commonMain/kotlin/zed/rainxch/apps/data/di/SharedModule.kt @@ -13,7 +13,7 @@ val appsModule = logger = get(), httpClient = get(), packageMonitor = get(), - themesRepository = get(), + tweaksRepository = get(), ) } } diff --git a/feature/apps/data/src/commonMain/kotlin/zed/rainxch/apps/data/repository/AppsRepositoryImpl.kt b/feature/apps/data/src/commonMain/kotlin/zed/rainxch/apps/data/repository/AppsRepositoryImpl.kt index c59efe61..17358454 100644 --- a/feature/apps/data/src/commonMain/kotlin/zed/rainxch/apps/data/repository/AppsRepositoryImpl.kt +++ b/feature/apps/data/src/commonMain/kotlin/zed/rainxch/apps/data/repository/AppsRepositoryImpl.kt @@ -24,7 +24,7 @@ import zed.rainxch.core.domain.model.InstallSource import zed.rainxch.core.domain.model.InstalledApp import zed.rainxch.core.domain.model.RateLimitException import zed.rainxch.core.domain.repository.InstalledAppsRepository -import zed.rainxch.core.domain.repository.ThemesRepository +import zed.rainxch.core.domain.repository.TweaksRepository import zed.rainxch.core.domain.system.PackageMonitor import zed.rainxch.core.domain.utils.AppLauncher import kotlin.time.Clock @@ -35,7 +35,7 @@ class AppsRepositoryImpl( private val logger: GitHubStoreLogger, private val httpClient: HttpClient, private val packageMonitor: PackageMonitor, - private val themesRepository: ThemesRepository, + private val tweaksRepository: TweaksRepository, ) : AppsRepository { private val json = Json { ignoreUnknownKeys = true } @@ -64,7 +64,7 @@ class AppsRepositoryImpl( repo: String, ): GithubRelease? = try { - val includePreReleases = themesRepository.getIncludePreReleases().first() + val includePreReleases = tweaksRepository.getIncludePreReleases().first() val releases = httpClient @@ -110,7 +110,7 @@ class AppsRepositoryImpl( } }.getOrThrow() - val includePreReleases = themesRepository.getIncludePreReleases().first() + val includePreReleases = tweaksRepository.getIncludePreReleases().first() val latestTag = try { val releases = diff --git a/feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/AppsRoot.kt b/feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/AppsRoot.kt index 4b65818a..22eb8a6e 100644 --- a/feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/AppsRoot.kt +++ b/feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/AppsRoot.kt @@ -140,9 +140,11 @@ fun AppsRoot( } } - is AppsEvent.AppLinkedSuccessfully -> { /* handled by ShowSuccess */ } + is AppsEvent.AppLinkedSuccessfully -> { // handled by ShowSuccess + } - is AppsEvent.ImportComplete -> { /* handled by ShowSuccess */ } + is AppsEvent.ImportComplete -> { // handled by ShowSuccess + } } } @@ -247,7 +249,15 @@ fun AppsScreen( modifier = Modifier.padding(bottomNavHeight + 16.dp), ) }, - modifier = Modifier.liquefiable(liquidState), + modifier = + Modifier + .then( + if (state.isLiquidGlassEnabled) { + Modifier.liquefiable(liquidState) + } else { + Modifier + }, + ), ) { innerPadding -> // Link app bottom sheet @@ -427,7 +437,15 @@ fun AppsScreen( onCancelClick = { onAction(AppsAction.OnCancelUpdate(appItem.installedApp.packageName)) }, onUninstallClick = { onAction(AppsAction.OnUninstallApp(appItem.installedApp)) }, onRepoClick = { onAction(AppsAction.OnNavigateToRepo(appItem.installedApp.repoId)) }, - modifier = Modifier.liquefiable(liquidState), + modifier = + Modifier + .then( + if (state.isLiquidGlassEnabled) { + Modifier.liquefiable(liquidState) + } else { + Modifier + }, + ), ) } diff --git a/feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/AppsState.kt b/feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/AppsState.kt index c0e955ac..d551fece 100644 --- a/feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/AppsState.kt +++ b/feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/AppsState.kt @@ -24,6 +24,7 @@ data class AppsState( val isCheckingForUpdates: Boolean = false, val lastCheckedTimestamp: Long? = null, val isRefreshing: Boolean = false, + val isLiquidGlassEnabled: Boolean = true, // Link app to repo val showLinkSheet: Boolean = false, val linkStep: LinkStep = LinkStep.PickApp, diff --git a/feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/AppsViewModel.kt b/feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/AppsViewModel.kt index 2d5f3349..d1755141 100644 --- a/feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/AppsViewModel.kt +++ b/feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/AppsViewModel.kt @@ -31,6 +31,7 @@ import zed.rainxch.core.domain.model.InstalledApp import zed.rainxch.core.domain.model.RateLimitException import zed.rainxch.core.domain.network.Downloader import zed.rainxch.core.domain.repository.InstalledAppsRepository +import zed.rainxch.core.domain.repository.TweaksRepository import zed.rainxch.core.domain.system.Installer import zed.rainxch.core.domain.use_cases.SyncInstalledAppsUseCase import zed.rainxch.core.domain.utils.ShareManager @@ -45,6 +46,7 @@ class AppsViewModel( private val syncInstalledAppsUseCase: SyncInstalledAppsUseCase, private val logger: GitHubStoreLogger, private val shareManager: ShareManager, + private val tweaksRepository: TweaksRepository, ) : ViewModel() { companion object { private const val UPDATE_CHECK_COOLDOWN_MS = 30 * 60 * 1000L @@ -61,6 +63,7 @@ class AppsViewModel( .onStart { if (!hasLoadedInitialData) { loadApps() + observeLiquidGlassEnabled() hasLoadedInitialData = true } }.stateIn( @@ -69,6 +72,18 @@ class AppsViewModel( initialValue = AppsState(), ) + private fun observeLiquidGlassEnabled() { + viewModelScope.launch { + tweaksRepository.getLiquidGlassEnabled().collect { enabled -> + _state.update { + it.copy( + isLiquidGlassEnabled = enabled, + ) + } + } + } + } + private val _events = Channel() val events = _events.receiveAsFlow() diff --git a/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/DetailsRoot.kt b/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/DetailsRoot.kt index 1a25abeb..0973060c 100644 --- a/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/DetailsRoot.kt +++ b/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/DetailsRoot.kt @@ -76,13 +76,11 @@ import zed.rainxch.githubstore.core.presentation.res.Res import zed.rainxch.githubstore.core.presentation.res.add_to_favourites import zed.rainxch.githubstore.core.presentation.res.cancel import zed.rainxch.githubstore.core.presentation.res.confirm_uninstall_message -import zed.rainxch.githubstore.core.presentation.res.install_anyway -import zed.rainxch.githubstore.core.presentation.res.signing_key_changed_message -import zed.rainxch.githubstore.core.presentation.res.signing_key_changed_title import zed.rainxch.githubstore.core.presentation.res.confirm_uninstall_title import zed.rainxch.githubstore.core.presentation.res.dismiss import zed.rainxch.githubstore.core.presentation.res.downgrade_requires_uninstall import zed.rainxch.githubstore.core.presentation.res.downgrade_warning_message +import zed.rainxch.githubstore.core.presentation.res.install_anyway import zed.rainxch.githubstore.core.presentation.res.install_permission_blocked_message import zed.rainxch.githubstore.core.presentation.res.install_permission_unavailable import zed.rainxch.githubstore.core.presentation.res.navigate_back @@ -92,6 +90,8 @@ import zed.rainxch.githubstore.core.presentation.res.remove_from_favourites import zed.rainxch.githubstore.core.presentation.res.repository_not_starred import zed.rainxch.githubstore.core.presentation.res.repository_starred import zed.rainxch.githubstore.core.presentation.res.share_repository +import zed.rainxch.githubstore.core.presentation.res.signing_key_changed_message +import zed.rainxch.githubstore.core.presentation.res.signing_key_changed_title import zed.rainxch.githubstore.core.presentation.res.star_from_github import zed.rainxch.githubstore.core.presentation.res.uninstall import zed.rainxch.githubstore.core.presentation.res.uninstall_first @@ -342,7 +342,14 @@ fun DetailsScreen( ) }, containerColor = MaterialTheme.colorScheme.background, - modifier = Modifier.liquefiable(liquidTopbarState), + modifier = + Modifier.then( + if (state.isLiquidGlassEnabled) { + Modifier.liquefiable(liquidTopbarState) + } else { + Modifier + }, + ), ) { innerPadding -> LanguagePicker( @@ -404,8 +411,13 @@ fun DetailsScreen( .fillMaxHeight() .widthIn(max = 680.dp) .fillMaxWidth() - .liquefiable(liquidTopbarState) - .padding(innerPadding), + .then( + if (state.isLiquidGlassEnabled) { + Modifier.liquefiable(liquidTopbarState) + } else { + Modifier + }, + ).padding(innerPadding), contentPadding = PaddingValues(16.dp), verticalArrangement = Arrangement.spacedBy(24.dp), ) { @@ -415,7 +427,10 @@ fun DetailsScreen( ) state.stats?.let { stats -> - stats(repoStats = stats) + stats( + isLiquidGlassEnabled = state.isLiquidGlassEnabled, + repoStats = stats, + ) } if (state.isComingFromUpdate) { @@ -423,6 +438,7 @@ fun DetailsScreen( whatsNew( release = release, isExpanded = state.isWhatsNewExpanded, + isLiquidGlassEnabled = state.isLiquidGlassEnabled, onToggleExpanded = { onAction(DetailsAction.ToggleWhatsNewExpanded) }, collapsedHeight = collapsedSectionHeight, translationState = state.whatsNewTranslation, @@ -443,6 +459,7 @@ fun DetailsScreen( readmeMarkdown = state.readmeMarkdown, readmeLanguage = state.readmeLanguage, isExpanded = state.isAboutExpanded, + isLiquidGlassEnabled = state.isLiquidGlassEnabled, onToggleExpanded = { onAction(DetailsAction.ToggleAboutExpanded) }, collapsedHeight = collapsedSectionHeight, translationState = state.aboutTranslation, @@ -463,6 +480,7 @@ fun DetailsScreen( readmeMarkdown = state.readmeMarkdown, readmeLanguage = state.readmeLanguage, isExpanded = state.isAboutExpanded, + isLiquidGlassEnabled = state.isLiquidGlassEnabled, onToggleExpanded = { onAction(DetailsAction.ToggleAboutExpanded) }, collapsedHeight = collapsedSectionHeight, translationState = state.aboutTranslation, @@ -482,6 +500,7 @@ fun DetailsScreen( whatsNew( release = release, isExpanded = state.isWhatsNewExpanded, + isLiquidGlassEnabled = state.isLiquidGlassEnabled, onToggleExpanded = { onAction(DetailsAction.ToggleWhatsNewExpanded) }, collapsedHeight = collapsedSectionHeight, translationState = state.whatsNewTranslation, @@ -506,6 +525,7 @@ fun DetailsScreen( state.userProfile?.let { userProfile -> author( + isLiquidGlassEnabled = state.isLiquidGlassEnabled, author = userProfile, onAction = onAction, ) @@ -670,12 +690,10 @@ private fun DetailsTopbar( 1f to MaterialTheme.colorScheme.surface.copy(alpha = 0.85f), ), ).then( - if (isLiquidFrostAvailable()) { + if (state.isLiquidGlassEnabled && isLiquidFrostAvailable()) { Modifier.liquid(liquidTopbarState) { this.shape = CutCornerShape(0.dp) - if (isLiquidFrostAvailable()) { - this.frost = 5.dp - } + this.frost = 5.dp this.curve = .25f this.refraction = .05f this.dispersion = .1f diff --git a/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/DetailsState.kt b/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/DetailsState.kt index 05b40e79..b7464657 100644 --- a/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/DetailsState.kt +++ b/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/DetailsState.kt @@ -8,12 +8,12 @@ import zed.rainxch.core.domain.model.InstalledApp import zed.rainxch.core.domain.model.SystemArchitecture import zed.rainxch.details.domain.model.ReleaseCategory import zed.rainxch.details.domain.model.RepoStats +import zed.rainxch.details.presentation.model.AttestationStatus import zed.rainxch.details.presentation.model.DowngradeWarning -import zed.rainxch.details.presentation.model.SigningKeyWarning import zed.rainxch.details.presentation.model.DownloadStage import zed.rainxch.details.presentation.model.InstallLogItem +import zed.rainxch.details.presentation.model.SigningKeyWarning import zed.rainxch.details.presentation.model.TranslationState -import zed.rainxch.details.presentation.model.AttestationStatus import zed.rainxch.details.presentation.model.TranslationTarget data class DetailsState( @@ -54,6 +54,7 @@ data class DetailsState( val isTrackingApp: Boolean = false, val isAboutExpanded: Boolean = false, val isWhatsNewExpanded: Boolean = false, + val isLiquidGlassEnabled: Boolean = true, val aboutTranslation: TranslationState = TranslationState(), val whatsNewTranslation: TranslationState = TranslationState(), val isLanguagePickerVisible: Boolean = false, diff --git a/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/DetailsViewModel.kt b/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/DetailsViewModel.kt index 812ccd57..fc3a5f9d 100644 --- a/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/DetailsViewModel.kt +++ b/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/DetailsViewModel.kt @@ -34,6 +34,7 @@ import zed.rainxch.core.domain.network.Downloader import zed.rainxch.core.domain.repository.FavouritesRepository import zed.rainxch.core.domain.repository.InstalledAppsRepository import zed.rainxch.core.domain.repository.StarredRepository +import zed.rainxch.core.domain.repository.TweaksRepository import zed.rainxch.core.domain.system.Installer import zed.rainxch.core.domain.system.PackageMonitor import zed.rainxch.core.domain.use_cases.SyncInstalledAppsUseCase @@ -88,6 +89,7 @@ class DetailsViewModel( private val translationRepository: TranslationRepository, private val logger: GitHubStoreLogger, private val isComingFromUpdate: Boolean, + private val tweaksRepository: TweaksRepository, ) : ViewModel() { private var hasLoadedInitialData = false private var currentDownloadJob: Job? = null @@ -103,6 +105,7 @@ class DetailsViewModel( .onStart { if (!hasLoadedInitialData) { loadInitial() + observeLiquidGlassEnabled() hasLoadedInitialData = true } @@ -117,6 +120,16 @@ class DetailsViewModel( private val rateLimited = AtomicBoolean(false) + private fun observeLiquidGlassEnabled() { + viewModelScope.launch { + tweaksRepository.getLiquidGlassEnabled().collect { enabled -> + _state.update { + it.copy(isLiquidGlassEnabled = enabled) + } + } + } + } + private fun recomputeAssetsForRelease(release: GithubRelease?): Pair, GithubAsset?> { val installable = release diff --git a/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/SmartInstallButton.kt b/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/SmartInstallButton.kt index 74a101c4..2bf29551 100644 --- a/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/SmartInstallButton.kt +++ b/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/SmartInstallButton.kt @@ -11,8 +11,8 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.CircleShape @@ -58,7 +58,6 @@ import zed.rainxch.githubstore.core.presentation.res.Res import zed.rainxch.githubstore.core.presentation.res.architecture_compatible import zed.rainxch.githubstore.core.presentation.res.cancel_download import zed.rainxch.githubstore.core.presentation.res.checking_attestation -import zed.rainxch.githubstore.core.presentation.res.verified_build import zed.rainxch.githubstore.core.presentation.res.downloading import zed.rainxch.githubstore.core.presentation.res.install_latest import zed.rainxch.githubstore.core.presentation.res.install_version @@ -69,6 +68,7 @@ import zed.rainxch.githubstore.core.presentation.res.show_install_options import zed.rainxch.githubstore.core.presentation.res.uninstall import zed.rainxch.githubstore.core.presentation.res.update_to_version import zed.rainxch.githubstore.core.presentation.res.updating +import zed.rainxch.githubstore.core.presentation.res.verified_build import zed.rainxch.githubstore.core.presentation.res.verifying @OptIn(ExperimentalMaterial3ExpressiveApi::class) @@ -76,6 +76,7 @@ import zed.rainxch.githubstore.core.presentation.res.verifying fun SmartInstallButton( isDownloading: Boolean, isInstalling: Boolean, + isLiquidGlassEnabled: Boolean, progress: Int?, primaryAsset: GithubAsset?, onAction: (DetailsAction) -> Unit, @@ -111,96 +112,108 @@ fun SmartInstallButton( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(4.dp), ) { - // Uninstall button - ElevatedCard( - onClick = { onAction(DetailsAction.OnRequestUninstall) }, - modifier = - Modifier - .weight(1f) - .height(52.dp) - .liquefiable(liquidState), - colors = - CardDefaults.elevatedCardColors( - containerColor = MaterialTheme.colorScheme.errorContainer, - ), - shape = - RoundedCornerShape( - topStart = 24.dp, - bottomStart = 24.dp, - topEnd = 6.dp, - bottomEnd = 6.dp, - ), - ) { - Box( - modifier = Modifier.fillMaxSize(), - contentAlignment = Alignment.Center, + // Uninstall button + ElevatedCard( + onClick = { onAction(DetailsAction.OnRequestUninstall) }, + modifier = + Modifier + .weight(1f) + .height(52.dp) + .then( + if (isLiquidGlassEnabled) { + Modifier.liquefiable(liquidState) + } else { + Modifier + }, + ), + colors = + CardDefaults.elevatedCardColors( + containerColor = MaterialTheme.colorScheme.errorContainer, + ), + shape = + RoundedCornerShape( + topStart = 24.dp, + bottomStart = 24.dp, + topEnd = 6.dp, + bottomEnd = 6.dp, + ), ) { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(6.dp), + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center, ) { - Icon( - imageVector = Icons.Default.Delete, - contentDescription = null, - modifier = Modifier.size(18.dp), - tint = MaterialTheme.colorScheme.onErrorContainer, - ) - Text( - text = stringResource(Res.string.uninstall), - color = MaterialTheme.colorScheme.onErrorContainer, - fontWeight = FontWeight.Bold, - style = MaterialTheme.typography.titleMedium, - ) + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(6.dp), + ) { + Icon( + imageVector = Icons.Default.Delete, + contentDescription = null, + modifier = Modifier.size(18.dp), + tint = MaterialTheme.colorScheme.onErrorContainer, + ) + Text( + text = stringResource(Res.string.uninstall), + color = MaterialTheme.colorScheme.onErrorContainer, + fontWeight = FontWeight.Bold, + style = MaterialTheme.typography.titleMedium, + ) + } } } - } - // Open button - ElevatedCard( - modifier = - Modifier - .weight(1f) - .height(52.dp) - .liquefiable(liquidState), - colors = - CardDefaults.elevatedCardColors( - containerColor = MaterialTheme.colorScheme.primary, - ), - shape = - RoundedCornerShape( - topStart = 6.dp, - bottomStart = 6.dp, - topEnd = 24.dp, - bottomEnd = 24.dp, - ), - onClick = { - onAction(DetailsAction.OpenApp) - }, - ) { - Box( - modifier = Modifier.fillMaxSize(), - contentAlignment = Alignment.Center, + // Open button + ElevatedCard( + modifier = + Modifier + .weight(1f) + .height(52.dp) + .then( + if (isLiquidGlassEnabled) { + Modifier.liquefiable(liquidState) + } else { + Modifier + }, + ), + colors = + CardDefaults.elevatedCardColors( + containerColor = MaterialTheme.colorScheme.primary, + ), + shape = + RoundedCornerShape( + topStart = 6.dp, + bottomStart = 6.dp, + topEnd = 24.dp, + bottomEnd = 24.dp, + ), + onClick = { + onAction(DetailsAction.OpenApp) + }, ) { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(6.dp), + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center, ) { - Icon( - imageVector = Icons.AutoMirrored.Filled.OpenInNew, - contentDescription = null, - modifier = Modifier.size(18.dp), - tint = MaterialTheme.colorScheme.onPrimary, - ) - Text( - text = stringResource(Res.string.open_app), - color = MaterialTheme.colorScheme.onPrimary, - fontWeight = FontWeight.Bold, - style = MaterialTheme.typography.titleMedium, - ) + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(6.dp), + ) { + Icon( + imageVector = Icons.AutoMirrored.Filled.OpenInNew, + contentDescription = null, + modifier = Modifier.size(18.dp), + tint = MaterialTheme.colorScheme.onPrimary, + ) + Text( + text = stringResource(Res.string.open_app), + color = MaterialTheme.colorScheme.onPrimary, + fontWeight = FontWeight.Bold, + style = MaterialTheme.typography.titleMedium, + ) + } } } } - } AttestationBadge(attestationStatus = state.attestationStatus) } @@ -265,7 +278,13 @@ fun SmartInstallButton( } } }, - ).liquefiable(liquidState), + ).then( + if (isLiquidGlassEnabled) { + Modifier.liquefiable(liquidState) + } else { + Modifier + }, + ), colors = CardDefaults.elevatedCardColors( containerColor = buttonColor, @@ -573,6 +592,7 @@ private fun AttestationBadge(attestationStatus: AttestationStatus) { color = MaterialTheme.colorScheme.onSurfaceVariant, ) } + AttestationStatus.VERIFIED -> { Icon( imageVector = Icons.Filled.VerifiedUser, @@ -588,6 +608,7 @@ private fun AttestationBadge(attestationStatus: AttestationStatus) { fontWeight = FontWeight.SemiBold, ) } + else -> {} } } @@ -629,6 +650,7 @@ fun SmartInstallButtonDownloadingPreview() { ), ), onAction = {}, + isLiquidGlassEnabled = true, state = DetailsState( isDownloading = true, diff --git a/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/About.kt b/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/About.kt index 7df015be..32a3b31f 100644 --- a/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/About.kt +++ b/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/About.kt @@ -53,6 +53,7 @@ fun LazyListScope.about( readmeMarkdown: String, readmeLanguage: String?, isExpanded: Boolean, + isLiquidGlassEnabled: Boolean, onToggleExpanded: () -> Unit, collapsedHeight: Dp, translationState: TranslationState, @@ -84,7 +85,13 @@ fun LazyListScope.about( style = MaterialTheme.typography.titleLarge, color = MaterialTheme.colorScheme.onBackground, fontWeight = FontWeight.Bold, - modifier = Modifier.liquefiable(liquidState), + modifier = Modifier.then( + if (isLiquidGlassEnabled) { + Modifier.liquefiable(liquidState) + } else { + Modifier + }, + ), ) readmeLanguage?.let { @@ -92,7 +99,13 @@ fun LazyListScope.about( text = it, style = MaterialTheme.typography.labelSmall, color = MaterialTheme.colorScheme.outline, - modifier = Modifier.liquefiable(liquidState), + modifier = Modifier.then( + if (isLiquidGlassEnabled) { + Modifier.liquefiable(liquidState) + } else { + Modifier + }, + ), ) } } @@ -131,7 +144,13 @@ fun LazyListScope.about( modifier = Modifier .fillMaxWidth() - .liquefiable(liquidState) + .then( + if (isLiquidGlassEnabled) { + Modifier.liquefiable(liquidState) + } else { + Modifier + }, + ) .animateContentSize(), ) } diff --git a/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/Header.kt b/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/Header.kt index 0cd92106..7c4b69b6 100644 --- a/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/Header.kt +++ b/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/Header.kt @@ -55,7 +55,14 @@ fun LazyListScope.header( installedApp = state.installedApp, downloadStage = state.downloadStage, downloadProgress = state.downloadProgressPercent, - modifier = Modifier.liquefiable(liquidState), + modifier = + Modifier.then( + if (state.isLiquidGlassEnabled) { + Modifier.liquefiable(liquidState) + } else { + Modifier + }, + ), ) } } @@ -106,6 +113,7 @@ fun LazyListScope.header( SmartInstallButton( isDownloading = state.isDownloading, isInstalling = state.isInstalling, + isLiquidGlassEnabled = state.isLiquidGlassEnabled, progress = state.downloadProgressPercent, primaryAsset = state.primaryAsset, state = state, @@ -144,7 +152,14 @@ fun LazyListScope.header( modifier = Modifier.size(24.dp), ) }, - modifier = Modifier.liquefiable(liquidState), + modifier = + Modifier.then( + if (state.isLiquidGlassEnabled) { + Modifier.liquefiable(liquidState) + } else { + Modifier + }, + ), ) Spacer(Modifier.height(8.dp)) @@ -174,7 +189,14 @@ fun LazyListScope.header( modifier = Modifier.size(24.dp), ) }, - modifier = Modifier.liquefiable(liquidState), + modifier = + Modifier.then( + if (state.isLiquidGlassEnabled) { + Modifier.liquefiable(liquidState) + } else { + Modifier + }, + ), ) Spacer(Modifier.height(8.dp)) @@ -204,7 +226,14 @@ fun LazyListScope.header( modifier = Modifier.size(24.dp), ) }, - modifier = Modifier.liquefiable(liquidState), + modifier = + Modifier.then( + if (state.isLiquidGlassEnabled) { + Modifier.liquefiable(liquidState) + } else { + Modifier + }, + ), ) } } diff --git a/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/Logs.kt b/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/Logs.kt index 7e53ef4f..c4f372ef 100644 --- a/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/Logs.kt +++ b/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/Logs.kt @@ -31,7 +31,13 @@ fun LazyListScope.logs(state: DetailsState) { modifier = Modifier .padding(vertical = 8.dp) - .liquefiable(liquidState), + .then( + if (state.isLiquidGlassEnabled) { + Modifier.liquefiable(liquidState) + } else { + Modifier + }, + ), fontWeight = FontWeight.Bold, ) } @@ -51,7 +57,14 @@ fun LazyListScope.logs(state: DetailsState) { } else { MaterialTheme.colorScheme.outline }, - modifier = Modifier.liquefiable(liquidState), + modifier = + Modifier.then( + if (state.isLiquidGlassEnabled) { + Modifier.liquefiable(liquidState) + } else { + Modifier + }, + ), ) } } diff --git a/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/Owner.kt b/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/Owner.kt index 3aac5da5..824965eb 100644 --- a/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/Owner.kt +++ b/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/Owner.kt @@ -42,6 +42,7 @@ import zed.rainxch.githubstore.core.presentation.res.* @OptIn(ExperimentalMaterial3ExpressiveApi::class) fun LazyListScope.author( + isLiquidGlassEnabled: Boolean, author: GithubUserProfile?, onAction: (DetailsAction) -> Unit, ) { @@ -59,7 +60,13 @@ fun LazyListScope.author( modifier = Modifier .padding(bottom = 12.dp) - .liquefiable(liquidState), + .then( + if (isLiquidGlassEnabled) { + Modifier.liquefiable(liquidState) + } else { + Modifier + }, + ), fontWeight = FontWeight.Bold, ) @@ -90,7 +97,13 @@ fun LazyListScope.author( Modifier .size(80.dp) .clip(CircleShape) - .liquefiable(liquidState), + .then( + if (isLiquidGlassEnabled) { + Modifier.liquefiable(liquidState) + } else { + Modifier + }, + ), loading = { Box( modifier = Modifier.fillMaxSize(), @@ -110,7 +123,14 @@ fun LazyListScope.author( text = it, style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurface, - modifier = Modifier.liquefiable(liquidState), + modifier = + Modifier.then( + if (isLiquidGlassEnabled) { + Modifier.liquefiable(liquidState) + } else { + Modifier + }, + ), ) } @@ -122,7 +142,14 @@ fun LazyListScope.author( maxLines = 2, softWrap = false, overflow = TextOverflow.Ellipsis, - modifier = Modifier.liquefiable(liquidState), + modifier = + Modifier.then( + if (isLiquidGlassEnabled) { + Modifier.liquefiable(liquidState) + } else { + Modifier + }, + ), ) } diff --git a/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/Stats.kt b/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/Stats.kt index 3e53632e..8ff7218e 100644 --- a/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/Stats.kt +++ b/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/Stats.kt @@ -15,7 +15,10 @@ import zed.rainxch.details.presentation.components.StatItem import zed.rainxch.details.presentation.utils.LocalTopbarLiquidState import zed.rainxch.githubstore.core.presentation.res.* -fun LazyListScope.stats(repoStats: RepoStats) { +fun LazyListScope.stats( + isLiquidGlassEnabled: Boolean, + repoStats: RepoStats, +) { item { val liquidState = LocalTopbarLiquidState.current @@ -31,7 +34,13 @@ fun LazyListScope.stats(repoStats: RepoStats) { modifier = Modifier .weight(1.5f) - .liquefiable(liquidState), + .then( + if (isLiquidGlassEnabled) { + Modifier.liquefiable(liquidState) + } else { + Modifier + }, + ), ) StatItem( @@ -40,7 +49,13 @@ fun LazyListScope.stats(repoStats: RepoStats) { modifier = Modifier .weight(2f) - .liquefiable(liquidState), + .then( + if (isLiquidGlassEnabled) { + Modifier.liquefiable(liquidState) + } else { + Modifier + }, + ), ) StatItem( @@ -49,7 +64,13 @@ fun LazyListScope.stats(repoStats: RepoStats) { modifier = Modifier .weight(1f) - .liquefiable(liquidState), + .then( + if (isLiquidGlassEnabled) { + Modifier.liquefiable(liquidState) + } else { + Modifier + }, + ), ) } } diff --git a/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/WhatsNew.kt b/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/WhatsNew.kt index 32d2e571..78b4747e 100644 --- a/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/WhatsNew.kt +++ b/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/WhatsNew.kt @@ -53,6 +53,7 @@ import zed.rainxch.githubstore.core.presentation.res.* fun LazyListScope.whatsNew( release: GithubRelease, isExpanded: Boolean, + isLiquidGlassEnabled: Boolean, onToggleExpanded: () -> Unit, collapsedHeight: Dp, translationState: TranslationState, @@ -79,7 +80,14 @@ fun LazyListScope.whatsNew( text = stringResource(Res.string.whats_new), style = MaterialTheme.typography.titleLarge, color = MaterialTheme.colorScheme.onBackground, - modifier = Modifier.liquefiable(liquidState), + modifier = + Modifier.then( + if (isLiquidGlassEnabled) { + Modifier.liquefiable(liquidState) + } else { + Modifier + }, + ), fontWeight = FontWeight.Bold, ) @@ -115,14 +123,28 @@ fun LazyListScope.whatsNew( release.tagName, style = MaterialTheme.typography.labelLarge, color = MaterialTheme.colorScheme.primary, - modifier = Modifier.liquefiable(liquidState), + modifier = + Modifier.then( + if (isLiquidGlassEnabled) { + Modifier.liquefiable(liquidState) + } else { + Modifier + }, + ), ) Text( release.publishedAt.take(10), style = MaterialTheme.typography.labelMedium, color = MaterialTheme.colorScheme.onSurfaceVariant, - modifier = Modifier.liquefiable(liquidState), + modifier = + Modifier.then( + if (isLiquidGlassEnabled) { + Modifier.liquefiable(liquidState) + } else { + Modifier + }, + ), ) } } @@ -139,6 +161,7 @@ fun LazyListScope.whatsNew( release = release, collapsedHeight = collapsedHeight, isExpanded = isExpanded, + isLiquidGlassEnabled = isLiquidGlassEnabled, liquidState = liquidState, onToggleExpanded = onToggleExpanded, ) @@ -151,6 +174,7 @@ private fun ExpandableMarkdownContent( release: GithubRelease, collapsedHeight: Dp, isExpanded: Boolean, + isLiquidGlassEnabled: Boolean, liquidState: LiquidState, onToggleExpanded: () -> Unit, ) { @@ -203,7 +227,13 @@ private fun ExpandableMarkdownContent( modifier = Modifier .fillMaxWidth() - .liquefiable(liquidState) + .then( + if (isLiquidGlassEnabled) { + Modifier.liquefiable(liquidState) + } else { + Modifier + }, + ) .onGloballyPositioned { coordinates -> val measured = coordinates.size.height.toFloat() if (measured > contentHeightPx) { diff --git a/feature/home/presentation/src/commonMain/kotlin/zed/rainxch/home/presentation/HomeRoot.kt b/feature/home/presentation/src/commonMain/kotlin/zed/rainxch/home/presentation/HomeRoot.kt index d343625a..b54e52d8 100644 --- a/feature/home/presentation/src/commonMain/kotlin/zed/rainxch/home/presentation/HomeRoot.kt +++ b/feature/home/presentation/src/commonMain/kotlin/zed/rainxch/home/presentation/HomeRoot.kt @@ -206,8 +206,15 @@ fun HomeScreen( .fillMaxSize() .padding(innerPadding) .padding(horizontal = 8.dp) - .liquefiable(liquidState) - .liquefiable(homeTopbarLiquidState), + .then( + if (state.isLiquidGlassEnabled) { + Modifier + .liquefiable(liquidState) + .liquefiable(homeTopbarLiquidState) + } else { + Modifier + }, + ), ) { FilterChips(state, onAction) @@ -265,8 +272,15 @@ private fun MainState( modifier = Modifier .animateItem() - .liquefiable(bottomNavLiquidState) - .liquefiable(homeTopBarLiquidState), + .then( + if (state.isLiquidGlassEnabled) { + Modifier + .liquefiable(bottomNavLiquidState) + .liquefiable(homeTopBarLiquidState) + } else { + Modifier + }, + ), ) } @@ -384,6 +398,7 @@ private fun FilterChips( onCategorySelected = { category -> onAction(HomeAction.SwitchCategory(category)) }, + isLiquidGlassEnabled = state.isLiquidGlassEnabled, ) } diff --git a/feature/home/presentation/src/commonMain/kotlin/zed/rainxch/home/presentation/HomeState.kt b/feature/home/presentation/src/commonMain/kotlin/zed/rainxch/home/presentation/HomeState.kt index fac58ccd..324a5d5f 100644 --- a/feature/home/presentation/src/commonMain/kotlin/zed/rainxch/home/presentation/HomeState.kt +++ b/feature/home/presentation/src/commonMain/kotlin/zed/rainxch/home/presentation/HomeState.kt @@ -19,4 +19,5 @@ data class HomeState( val isUpdateAvailable: Boolean = false, val currentPlatform: DiscoveryPlatform = DiscoveryPlatform.All, val isPlatformPopupVisible: Boolean = false, + val isLiquidGlassEnabled: Boolean = false, ) diff --git a/feature/home/presentation/src/commonMain/kotlin/zed/rainxch/home/presentation/HomeViewModel.kt b/feature/home/presentation/src/commonMain/kotlin/zed/rainxch/home/presentation/HomeViewModel.kt index 11036fb6..58149c31 100644 --- a/feature/home/presentation/src/commonMain/kotlin/zed/rainxch/home/presentation/HomeViewModel.kt +++ b/feature/home/presentation/src/commonMain/kotlin/zed/rainxch/home/presentation/HomeViewModel.kt @@ -23,6 +23,7 @@ import zed.rainxch.core.domain.repository.FavouritesRepository import zed.rainxch.core.domain.repository.InstalledAppsRepository import zed.rainxch.core.domain.repository.StarredRepository import zed.rainxch.core.domain.use_cases.SyncInstalledAppsUseCase +import zed.rainxch.core.domain.repository.TweaksRepository import zed.rainxch.core.domain.utils.ShareManager import zed.rainxch.core.presentation.model.DiscoveryRepositoryUi import zed.rainxch.core.presentation.utils.toUi @@ -40,6 +41,7 @@ class HomeViewModel( private val starredRepository: StarredRepository, private val logger: GitHubStoreLogger, private val shareManager: ShareManager, + private val tweaksRepository: TweaksRepository, ) : ViewModel() { private var hasLoadedInitialData = false private var currentJob: Job? = null @@ -58,6 +60,7 @@ class HomeViewModel( observeInstalledApps() observeFavourites() observeStarredRepos() + observeLiquidGlassEnabled() hasLoadedInitialData = true } @@ -352,6 +355,16 @@ class HomeViewModel( } } + private fun observeLiquidGlassEnabled() { + viewModelScope.launch { + tweaksRepository.getLiquidGlassEnabled().collect { enabled -> + _state.update { + it.copy(isLiquidGlassEnabled = enabled) + } + } + } + } + private fun observeFavourites() { viewModelScope.launch { favouritesRepository.getAllFavorites().collect { favourites -> diff --git a/feature/home/presentation/src/commonMain/kotlin/zed/rainxch/home/presentation/components/HomeFilterChips.kt b/feature/home/presentation/src/commonMain/kotlin/zed/rainxch/home/presentation/components/HomeFilterChips.kt index e8755b28..a2468a84 100644 --- a/feature/home/presentation/src/commonMain/kotlin/zed/rainxch/home/presentation/components/HomeFilterChips.kt +++ b/feature/home/presentation/src/commonMain/kotlin/zed/rainxch/home/presentation/components/HomeFilterChips.kt @@ -7,6 +7,7 @@ import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.spring import androidx.compose.animation.core.tween import androidx.compose.foundation.background +import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.collectIsPressedAsState @@ -58,6 +59,7 @@ fun LiquidGlassCategoryChips( categories: List, selectedCategory: HomeCategory, onCategorySelected: (HomeCategory) -> Unit, + isLiquidGlassEnabled: Boolean = true, modifier: Modifier = Modifier, ) { val liquidState = LocalHomeTopBarLiquid.current @@ -118,31 +120,39 @@ fun LiquidGlassCategoryChips( val borderColor = if (isDarkTheme) Color.White.copy(alpha = .10f) else Color.Transparent val containerShape = RoundedCornerShape(20.dp) + val useLiquid = isLiquidGlassEnabled && isLiquidFrostAvailable() Box( modifier = modifier .fillMaxWidth() .clip(containerShape) - .background( - if (isDarkTheme) { - MaterialTheme.colorScheme.surfaceContainerHighest.copy(alpha = .30f) - } else { - MaterialTheme.colorScheme.primaryContainer.copy(alpha = .45f) - }, - ).then( - if (isLiquidFrostAvailable()) { - Modifier.liquid(liquidState) { - this.shape = containerShape - this.frost = if (isDarkTheme) 14.dp else 12.dp - this.curve = if (isDarkTheme) .30f else .40f - this.refraction = if (isDarkTheme) .06f else .10f - this.dispersion = if (isDarkTheme) .15f else .22f - this.saturation = if (isDarkTheme) .35f else .50f - this.contrast = if (isDarkTheme) 1.7f else 1.5f - } + .then( + if (useLiquid) { + Modifier + .background( + if (isDarkTheme) { + MaterialTheme.colorScheme.surfaceContainerHighest.copy(alpha = .30f) + } else { + MaterialTheme.colorScheme.primaryContainer.copy(alpha = .45f) + }, + ).liquid(liquidState) { + this.shape = containerShape + this.frost = if (isDarkTheme) 14.dp else 12.dp + this.curve = if (isDarkTheme) .30f else .40f + this.refraction = if (isDarkTheme) .06f else .10f + this.dispersion = if (isDarkTheme) .15f else .22f + this.saturation = if (isDarkTheme) .35f else .50f + this.contrast = if (isDarkTheme) 1.7f else 1.5f + } } else { - Modifier.background(MaterialTheme.colorScheme.surfaceContainerHighest) + Modifier + .background(MaterialTheme.colorScheme.surfaceContainer) + .border( + width = 1.dp, + color = MaterialTheme.colorScheme.outlineVariant, + shape = containerShape, + ) }, ), ) { diff --git a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileAction.kt b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileAction.kt index e160afdd..57e77905 100644 --- a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileAction.kt +++ b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileAction.kt @@ -66,12 +66,29 @@ sealed interface ProfileAction { data object OnProxyPasswordVisibilityToggle : ProfileAction + data class OnLiquidGlassEnabledChange( + val enabled: Boolean, + ) : ProfileAction + data object OnProxySave : ProfileAction - data class OnInstallerTypeSelected(val type: InstallerType) : ProfileAction + + data class OnInstallerTypeSelected( + val type: InstallerType, + ) : ProfileAction + data object OnRequestShizukuPermission : ProfileAction - data class OnAutoUpdateToggled(val enabled: Boolean) : ProfileAction - data class OnUpdateCheckIntervalChanged(val hours: Long) : ProfileAction - data class OnIncludePreReleasesToggled(val enabled: Boolean) : ProfileAction + + data class OnAutoUpdateToggled( + val enabled: Boolean, + ) : ProfileAction + + data class OnUpdateCheckIntervalChanged( + val hours: Long, + ) : ProfileAction + + data class OnIncludePreReleasesToggled( + val enabled: Boolean, + ) : ProfileAction data class OnAutoDetectClipboardToggled( val enabled: Boolean, diff --git a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileRoot.kt b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileRoot.kt index c9e0b68e..f28e73e4 100644 --- a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileRoot.kt +++ b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileRoot.kt @@ -34,10 +34,7 @@ import zed.rainxch.core.presentation.utils.ObserveAsEvents import zed.rainxch.githubstore.core.presentation.res.* import zed.rainxch.profile.presentation.components.LogoutDialog import zed.rainxch.profile.presentation.components.sections.about -import zed.rainxch.profile.presentation.components.sections.installationSection -import zed.rainxch.profile.presentation.components.sections.updatesSection import zed.rainxch.profile.presentation.components.sections.logout -import zed.rainxch.profile.presentation.components.sections.networkSection import zed.rainxch.profile.presentation.components.sections.othersSection import zed.rainxch.profile.presentation.components.sections.profile import zed.rainxch.profile.presentation.components.sections.settings @@ -166,7 +163,14 @@ fun ProfileScreen( TopAppBar() }, containerColor = MaterialTheme.colorScheme.background, - modifier = Modifier.liquefiable(liquidState), + modifier = + Modifier.then( + if (state.isLiquidGlassEnabled) { + Modifier.liquefiable(liquidState) + } else { + Modifier + }, + ), ) { innerPadding -> LazyColumn( modifier = @@ -189,29 +193,6 @@ fun ProfileScreen( onAction = onAction, ) - item { - Spacer(Modifier.height(32.dp)) - } - - networkSection( - state = state, - onAction = onAction, - ) - - item { - Spacer(Modifier.height(12.dp)) - } - - installationSection( - state = state, - onAction = onAction - ) - - updatesSection( - state = state, - onAction = onAction - ) - item { Spacer(Modifier.height(16.dp)) } diff --git a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileState.kt b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileState.kt index 0c0d8afa..d462c615 100644 --- a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileState.kt +++ b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileState.kt @@ -29,4 +29,5 @@ data class ProfileState( val autoUpdateEnabled: Boolean = false, val updateCheckIntervalHours: Long = 6L, val includePreReleases: Boolean = false, + val isLiquidGlassEnabled: Boolean = true, ) diff --git a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileViewModel.kt b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileViewModel.kt index 75f452b4..ec82ca42 100644 --- a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileViewModel.kt +++ b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileViewModel.kt @@ -14,7 +14,7 @@ import kotlinx.coroutines.launch import org.jetbrains.compose.resources.getString import zed.rainxch.core.domain.model.ProxyConfig import zed.rainxch.core.domain.repository.ProxyRepository -import zed.rainxch.core.domain.repository.ThemesRepository +import zed.rainxch.core.domain.repository.TweaksRepository import zed.rainxch.core.domain.system.InstallerStatusProvider import zed.rainxch.core.domain.system.UpdateScheduleManager import zed.rainxch.core.domain.utils.BrowserHelper @@ -27,7 +27,7 @@ import zed.rainxch.profile.presentation.model.ProxyType class ProfileViewModel( private val browserHelper: BrowserHelper, - private val themesRepository: ThemesRepository, + private val tweaksRepository: TweaksRepository, private val profileRepository: ProfileRepository, private val installerStatusProvider: InstallerStatusProvider, private val proxyRepository: ProxyRepository, @@ -38,28 +38,32 @@ class ProfileViewModel( private var hasLoadedInitialData = false private val _state = MutableStateFlow(ProfileState()) - val state = _state - .onStart { - if (!hasLoadedInitialData) { - loadCurrentTheme() - collectIsUserLoggedIn() - loadUserProfile() - loadVersionName() - loadProxyConfig() - observeCacheSize() - loadInstallerPreference() - observeShizukuStatus() - loadAutoUpdatePreference() - loadUpdateCheckInterval() - loadIncludePreReleases() - - hasLoadedInitialData = true - } - }.stateIn( - scope = viewModelScope, - started = SharingStarted.WhileSubscribed(5_000L), - initialValue = ProfileState(), - ) + val state = + _state + .onStart { + if (!hasLoadedInitialData) { + loadCurrentTheme() + loadUserProfile() + loadVersionName() + loadProxyConfig() + loadInstallerPreference() + loadAutoUpdatePreference() + loadUpdateCheckInterval() + loadIncludePreReleases() + loadLiquidGlassEnabled() + + observeLoggedInStatus() + + observeCacheSize() + observeShizukuStatus() + + hasLoadedInitialData = true + } + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5_000L), + initialValue = ProfileState(), + ) private val _events = Channel() val events = _events.receiveAsFlow() @@ -100,7 +104,7 @@ class ProfileViewModel( } } - private fun collectIsUserLoggedIn() { + private fun observeLoggedInStatus() { viewModelScope.launch { profileRepository.isUserLoggedIn .collect { isLoggedIn -> @@ -127,7 +131,7 @@ class ProfileViewModel( private fun loadCurrentTheme() { viewModelScope.launch { - themesRepository.getThemeColor().collect { theme -> + tweaksRepository.getThemeColor().collect { theme -> _state.update { it.copy(selectedThemeColor = theme) } @@ -135,7 +139,7 @@ class ProfileViewModel( } viewModelScope.launch { - themesRepository.getAmoledTheme().collect { isAmoled -> + tweaksRepository.getAmoledTheme().collect { isAmoled -> _state.update { it.copy(isAmoledThemeEnabled = isAmoled) } @@ -143,7 +147,7 @@ class ProfileViewModel( } viewModelScope.launch { - themesRepository.getIsDarkTheme().collect { isDarkTheme -> + tweaksRepository.getIsDarkTheme().collect { isDarkTheme -> _state.update { it.copy(isDarkTheme = isDarkTheme) } @@ -151,7 +155,7 @@ class ProfileViewModel( } viewModelScope.launch { - themesRepository.getFontTheme().collect { fontTheme -> + tweaksRepository.getFontTheme().collect { fontTheme -> _state.update { it.copy(selectedFontTheme = fontTheme) } @@ -159,7 +163,7 @@ class ProfileViewModel( } viewModelScope.launch { - themesRepository.getAutoDetectClipboardLinks().collect { enabled -> + tweaksRepository.getAutoDetectClipboardLinks().collect { enabled -> _state.update { it.copy(autoDetectClipboardLinks = enabled) } @@ -205,7 +209,7 @@ class ProfileViewModel( private fun loadInstallerPreference() { viewModelScope.launch { - themesRepository.getInstallerType().collect { type -> + tweaksRepository.getInstallerType().collect { type -> _state.update { it.copy(installerType = type) } @@ -225,7 +229,7 @@ class ProfileViewModel( private fun loadAutoUpdatePreference() { viewModelScope.launch { - themesRepository.getAutoUpdateEnabled().collect { enabled -> + tweaksRepository.getAutoUpdateEnabled().collect { enabled -> _state.update { it.copy(autoUpdateEnabled = enabled) } @@ -235,7 +239,7 @@ class ProfileViewModel( private fun loadUpdateCheckInterval() { viewModelScope.launch { - themesRepository.getUpdateCheckInterval().collect { hours -> + tweaksRepository.getUpdateCheckInterval().collect { hours -> _state.update { it.copy(updateCheckIntervalHours = hours) } @@ -243,9 +247,19 @@ class ProfileViewModel( } } + private fun loadLiquidGlassEnabled() { + viewModelScope.launch { + tweaksRepository.getLiquidGlassEnabled().collect { enabled -> + _state.update { + it.copy(isLiquidGlassEnabled = enabled) + } + } + } + } + private fun loadIncludePreReleases() { viewModelScope.launch { - themesRepository.getIncludePreReleases().collect { enabled -> + tweaksRepository.getIncludePreReleases().collect { enabled -> _state.update { it.copy(includePreReleases = enabled) } @@ -280,13 +294,13 @@ class ProfileViewModel( is ProfileAction.OnThemeColorSelected -> { viewModelScope.launch { - themesRepository.setThemeColor(action.themeColor) + tweaksRepository.setThemeColor(action.themeColor) } } is ProfileAction.OnAmoledThemeToggled -> { viewModelScope.launch { - themesRepository.setAmoledTheme(action.enabled) + tweaksRepository.setAmoledTheme(action.enabled) } } @@ -317,45 +331,50 @@ class ProfileViewModel( ProfileAction.OnLogoutDismiss -> { _state.update { it.copy( - isLogoutDialogVisible = false + isLogoutDialogVisible = false, ) } } + is ProfileAction.OnLiquidGlassEnabledChange -> { + viewModelScope.launch { + tweaksRepository.setLiquidGlassEnabled(action.enabled) + } + } + ProfileAction.OnNavigateBackClick -> { - /* Handed in composable */ + // Handed in composable } ProfileAction.OnLoginClick -> { - /* Handed in composable */ + // Handed in composable } ProfileAction.OnFavouriteReposClick -> { - /* Handed in composable */ + // Handed in composable } ProfileAction.OnStarredReposClick -> { - /* Handed in composable */ + // Handed in composable } - is ProfileAction.OnRepositoriesClick -> { - /* Handed in composable */ + // Handed in composable } ProfileAction.OnSponsorClick -> { - /* Handed in composable */ + // Handed in composable } is ProfileAction.OnFontThemeSelected -> { viewModelScope.launch { - themesRepository.setFontTheme(action.fontTheme) + tweaksRepository.setFontTheme(action.fontTheme) } } is ProfileAction.OnDarkThemeChange -> { viewModelScope.launch { - themesRepository.setDarkTheme(action.isDarkTheme) + tweaksRepository.setDarkTheme(action.isDarkTheme) } } @@ -406,13 +425,13 @@ class ProfileViewModel( is ProfileAction.OnAutoDetectClipboardToggled -> { viewModelScope.launch { - themesRepository.setAutoDetectClipboardLinks(action.enabled) + tweaksRepository.setAutoDetectClipboardLinks(action.enabled) } } is ProfileAction.OnInstallerTypeSelected -> { viewModelScope.launch { - themesRepository.setInstallerType(action.type) + tweaksRepository.setInstallerType(action.type) } } @@ -422,20 +441,20 @@ class ProfileViewModel( is ProfileAction.OnAutoUpdateToggled -> { viewModelScope.launch { - themesRepository.setAutoUpdateEnabled(action.enabled) + tweaksRepository.setAutoUpdateEnabled(action.enabled) } } is ProfileAction.OnUpdateCheckIntervalChanged -> { viewModelScope.launch { - themesRepository.setUpdateCheckInterval(action.hours) + tweaksRepository.setUpdateCheckInterval(action.hours) updateScheduleManager.reschedule(action.hours) } } is ProfileAction.OnIncludePreReleasesToggled -> { viewModelScope.launch { - themesRepository.setIncludePreReleases(action.enabled) + tweaksRepository.setIncludePreReleases(action.enabled) } } diff --git a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/Appearance.kt b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/Appearance.kt index 84f73c59..2ba42de6 100644 --- a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/Appearance.kt +++ b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/Appearance.kt @@ -32,8 +32,6 @@ import androidx.compose.material.icons.filled.Colorize import androidx.compose.material.icons.filled.DarkMode import androidx.compose.material.icons.filled.Done import androidx.compose.material.icons.filled.LightMode -import androidx.compose.material3.CardDefaults -import androidx.compose.material3.ElevatedCard import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.Icon import androidx.compose.material3.MaterialShapes @@ -128,6 +126,17 @@ fun LazyListScope.appearanceSection( VerticalSpacer(8.dp) + ToggleSettingCard( + title = stringResource(Res.string.liquid_glass_option_title), + description = stringResource(Res.string.liquid_glass_option_description), + checked = state.isLiquidGlassEnabled, + onCheckedChange = { enabled -> + onAction(ProfileAction.OnLiquidGlassEnabledChange(enabled)) + }, + ) + + VerticalSpacer(8.dp) + ToggleSettingCard( title = stringResource(Res.string.auto_detect_clipboard_links), description = stringResource(Res.string.auto_detect_clipboard_description), diff --git a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/SettingsSection.kt b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/SettingsSection.kt index e39d399c..94bd7d2c 100644 --- a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/SettingsSection.kt +++ b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/SettingsSection.kt @@ -1,6 +1,10 @@ package zed.rainxch.profile.presentation.components.sections +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height import androidx.compose.foundation.lazy.LazyListScope +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp import zed.rainxch.profile.presentation.ProfileAction import zed.rainxch.profile.presentation.ProfileState @@ -12,4 +16,27 @@ fun LazyListScope.settings( state = state, onAction = onAction, ) + + item { + Spacer(Modifier.height(32.dp)) + } + + networkSection( + state = state, + onAction = onAction, + ) + + item { + Spacer(Modifier.height(12.dp)) + } + + installationSection( + state = state, + onAction = onAction, + ) + + updatesSection( + state = state, + onAction = onAction, + ) } diff --git a/feature/search/presentation/src/commonMain/kotlin/zed/rainxch/search/presentation/SearchRoot.kt b/feature/search/presentation/src/commonMain/kotlin/zed/rainxch/search/presentation/SearchRoot.kt index 390baa41..1a16d880 100644 --- a/feature/search/presentation/src/commonMain/kotlin/zed/rainxch/search/presentation/SearchRoot.kt +++ b/feature/search/presentation/src/commonMain/kotlin/zed/rainxch/search/presentation/SearchRoot.kt @@ -294,7 +294,13 @@ fun SearchScreen( } }, containerColor = MaterialTheme.colorScheme.background, - modifier = Modifier.liquefiable(liquidState), + modifier = Modifier.then( + if (state.isLiquidGlassEnabled) { + Modifier.liquefiable(liquidState) + } else { + Modifier + }, + ), ) { innerPadding -> Column( modifier = @@ -519,7 +525,13 @@ fun SearchScreen( modifier = Modifier .fillMaxSize() - .liquefiable(liquidState), + .then( + if (state.isLiquidGlassEnabled) { + Modifier.liquefiable(liquidState) + } else { + Modifier + }, + ), ) { items( items = state.repositories, @@ -539,7 +551,13 @@ fun SearchScreen( modifier = Modifier .animateItem() - .liquefiable(liquidState), + .then( + if (state.isLiquidGlassEnabled) { + Modifier.liquefiable(liquidState) + } else { + Modifier + }, + ), ) } diff --git a/feature/search/presentation/src/commonMain/kotlin/zed/rainxch/search/presentation/SearchState.kt b/feature/search/presentation/src/commonMain/kotlin/zed/rainxch/search/presentation/SearchState.kt index 3526cc66..efa529c9 100644 --- a/feature/search/presentation/src/commonMain/kotlin/zed/rainxch/search/presentation/SearchState.kt +++ b/feature/search/presentation/src/commonMain/kotlin/zed/rainxch/search/presentation/SearchState.kt @@ -17,6 +17,7 @@ data class SearchState( val selectedSortOrder: SortOrderUi = SortOrderUi.Descending, val selectedLanguage: ProgrammingLanguageUi = ProgrammingLanguageUi.All, val isLoading: Boolean = false, + val isLiquidGlassEnabled: Boolean = true, val isLoadingMore: Boolean = false, val errorMessage: String? = null, val hasMorePages: Boolean = true, diff --git a/feature/search/presentation/src/commonMain/kotlin/zed/rainxch/search/presentation/SearchViewModel.kt b/feature/search/presentation/src/commonMain/kotlin/zed/rainxch/search/presentation/SearchViewModel.kt index 37ae0043..45c11ef9 100644 --- a/feature/search/presentation/src/commonMain/kotlin/zed/rainxch/search/presentation/SearchViewModel.kt +++ b/feature/search/presentation/src/commonMain/kotlin/zed/rainxch/search/presentation/SearchViewModel.kt @@ -23,7 +23,7 @@ import zed.rainxch.core.domain.model.RateLimitException import zed.rainxch.core.domain.repository.FavouritesRepository import zed.rainxch.core.domain.repository.InstalledAppsRepository import zed.rainxch.core.domain.repository.StarredRepository -import zed.rainxch.core.domain.repository.ThemesRepository +import zed.rainxch.core.domain.repository.TweaksRepository import zed.rainxch.core.domain.use_cases.SyncInstalledAppsUseCase import zed.rainxch.core.domain.utils.ClipboardHelper import zed.rainxch.core.domain.utils.ShareManager @@ -50,7 +50,7 @@ class SearchViewModel( private val shareManager: ShareManager, private val platform: Platform, private val clipboardHelper: ClipboardHelper, - private val themesRepository: ThemesRepository, + private val tweaksRepository: TweaksRepository, ) : ViewModel() { private var hasLoadedInitialData = false private var currentSearchJob: Job? = null @@ -72,6 +72,7 @@ class SearchViewModel( observeInstalledApps() observeFavouriteApps() observeStarredRepos() + observeLiquidGlassEnabled() observeClipboardSetting() checkClipboardForLinks() @@ -83,6 +84,18 @@ class SearchViewModel( initialValue = SearchState(), ) + private fun observeLiquidGlassEnabled() { + viewModelScope.launch { + tweaksRepository.getLiquidGlassEnabled().collect { enabled -> + _state.update { + it.copy( + isLiquidGlassEnabled = enabled, + ) + } + } + } + } + private val _events = Channel() val events = _events.receiveAsFlow() @@ -101,7 +114,7 @@ class SearchViewModel( private fun observeClipboardSetting() { viewModelScope.launch { - themesRepository.getAutoDetectClipboardLinks().collect { enabled -> + tweaksRepository.getAutoDetectClipboardLinks().collect { enabled -> _state.update { current -> current.copy( autoDetectClipboardEnabled = enabled, @@ -116,7 +129,7 @@ class SearchViewModel( private fun checkClipboardForLinks() { viewModelScope.launch { - val enabled = themesRepository.getAutoDetectClipboardLinks().first() + val enabled = tweaksRepository.getAutoDetectClipboardLinks().first() if (!enabled) return@launch try { From ccb9788550eea53a04d503c8dbe0b2a94bee60ea Mon Sep 17 00:00:00 2001 From: rainxchzed Date: Fri, 20 Mar 2026 23:35:37 +0500 Subject: [PATCH 2/3] locale: add translations for Liquid Glass effect across multiple languages - Add localized strings for "Liquid Glass" effect title and description. - Provide translations for Bengali, Hindi, Italian, Spanish, Arabic, French, Polish, Russian, Japanese, Korean, Turkish, and Chinese (Simplified). - Include descriptions for enhancing the interface with a smooth glass-like appearance. --- .../src/commonMain/composeResources/values-ar/strings-ar.xml | 2 ++ .../src/commonMain/composeResources/values-bn/strings-bn.xml | 2 ++ .../src/commonMain/composeResources/values-es/strings-es.xml | 2 ++ .../src/commonMain/composeResources/values-fr/strings-fr.xml | 2 ++ .../src/commonMain/composeResources/values-hi/strings-hi.xml | 2 ++ .../src/commonMain/composeResources/values-it/strings-it.xml | 2 ++ .../src/commonMain/composeResources/values-ja/strings-ja.xml | 2 ++ .../src/commonMain/composeResources/values-ko/strings-ko.xml | 2 ++ .../src/commonMain/composeResources/values-pl/strings-pl.xml | 2 ++ .../src/commonMain/composeResources/values-ru/strings-ru.xml | 2 ++ .../src/commonMain/composeResources/values-tr/strings-tr.xml | 2 ++ .../composeResources/values-zh-rCN/strings-zh-rCN.xml | 2 ++ 12 files changed, 24 insertions(+) diff --git a/core/presentation/src/commonMain/composeResources/values-ar/strings-ar.xml b/core/presentation/src/commonMain/composeResources/values-ar/strings-ar.xml index e0748684..4a561172 100644 --- a/core/presentation/src/commonMain/composeResources/values-ar/strings-ar.xml +++ b/core/presentation/src/commonMain/composeResources/values-ar/strings-ar.xml @@ -583,4 +583,6 @@ اختر اللغة عدم تطابق الحزمة: ملف APK هو %1$s، لكن التطبيق المثبت هو %2$s. تم حظر التحديث. عدم تطابق مفتاح التوقيع: تم توقيع التحديث بواسطة مطور مختلف. تم حظر التحديث. + تأثير الزجاج السائل + تحسين الواجهة بمظهر زجاجي ناعم diff --git a/core/presentation/src/commonMain/composeResources/values-bn/strings-bn.xml b/core/presentation/src/commonMain/composeResources/values-bn/strings-bn.xml index 865f262b..aa745340 100644 --- a/core/presentation/src/commonMain/composeResources/values-bn/strings-bn.xml +++ b/core/presentation/src/commonMain/composeResources/values-bn/strings-bn.xml @@ -582,4 +582,6 @@ ভাষা নির্বাচন করুন প্যাকেজ অমিল: APK হলো %1$s, কিন্তু ইনস্টল করা অ্যাপ হলো %2$s। আপডেট ব্লক করা হয়েছে। সাইনিং কী অমিল: আপডেটটি একজন ভিন্ন ডেভেলপার দ্বারা সাইন করা হয়েছে। আপডেট ব্লক করা হয়েছে। + লিকুইড গ্লাস ইফেক্ট + একটি মসৃণ কাচের মতো চেহারা দিয়ে ইন্টারফেস উন্নত করুন diff --git a/core/presentation/src/commonMain/composeResources/values-es/strings-es.xml b/core/presentation/src/commonMain/composeResources/values-es/strings-es.xml index 8e541a1c..44d95c36 100644 --- a/core/presentation/src/commonMain/composeResources/values-es/strings-es.xml +++ b/core/presentation/src/commonMain/composeResources/values-es/strings-es.xml @@ -543,4 +543,6 @@ Seleccionar idioma Paquete no coincide: el APK es %1$s, pero la aplicación instalada es %2$s. Actualización bloqueada. Clave de firma no coincide: la actualización fue firmada por un desarrollador diferente. Actualización bloqueada. + Efecto de cristal líquido + Mejora la interfaz con una apariencia suave tipo cristal \ No newline at end of file diff --git a/core/presentation/src/commonMain/composeResources/values-fr/strings-fr.xml b/core/presentation/src/commonMain/composeResources/values-fr/strings-fr.xml index 8f284a2d..2fb12319 100644 --- a/core/presentation/src/commonMain/composeResources/values-fr/strings-fr.xml +++ b/core/presentation/src/commonMain/composeResources/values-fr/strings-fr.xml @@ -544,4 +544,6 @@ Sélectionner la langue Incompatibilité de paquet : l\'APK est %1$s, mais l\'application installée est %2$s. Mise à jour bloquée. Incompatibilité de clé de signature : la mise à jour a été signée par un développeur différent. Mise à jour bloquée. + Effet verre liquide + Améliorez l\'interface avec une apparence vitreuse et fluide \ No newline at end of file diff --git a/core/presentation/src/commonMain/composeResources/values-hi/strings-hi.xml b/core/presentation/src/commonMain/composeResources/values-hi/strings-hi.xml index 84d48e92..d18c2342 100644 --- a/core/presentation/src/commonMain/composeResources/values-hi/strings-hi.xml +++ b/core/presentation/src/commonMain/composeResources/values-hi/strings-hi.xml @@ -582,4 +582,6 @@ भाषा चुनें पैकेज मेल नहीं खाता: APK %1$s है, लेकिन इंस्टॉल किया गया ऐप %2$s है। अपडेट ब्लॉक किया गया। साइनिंग कुंजी मेल नहीं खाती: अपडेट किसी अन्य डेवलपर द्वारा साइन किया गया था। अपडेट ब्लॉक किया गया। + लिक्विड ग्लास इफ़ेक्ट + एक चिकने काँच जैसे रूप से इंटरफ़ेस को बेहतर बनाएं diff --git a/core/presentation/src/commonMain/composeResources/values-it/strings-it.xml b/core/presentation/src/commonMain/composeResources/values-it/strings-it.xml index 2f89829b..3f5a02c4 100644 --- a/core/presentation/src/commonMain/composeResources/values-it/strings-it.xml +++ b/core/presentation/src/commonMain/composeResources/values-it/strings-it.xml @@ -582,4 +582,6 @@ Seleziona lingua Pacchetto non corrispondente: l\'APK è %1$s, ma l\'app installata è %2$s. Aggiornamento bloccato. Chiave di firma non corrispondente: l\'aggiornamento è stato firmato da uno sviluppatore diverso. Aggiornamento bloccato. + Effetto vetro liquido + Migliora l\'interfaccia con un aspetto liscio simile al vetro \ No newline at end of file diff --git a/core/presentation/src/commonMain/composeResources/values-ja/strings-ja.xml b/core/presentation/src/commonMain/composeResources/values-ja/strings-ja.xml index f17d5f9e..5772689d 100644 --- a/core/presentation/src/commonMain/composeResources/values-ja/strings-ja.xml +++ b/core/presentation/src/commonMain/composeResources/values-ja/strings-ja.xml @@ -544,4 +544,6 @@ 言語を選択 パッケージの不一致: APKは%1$sですが、インストール済みアプリは%2$sです。更新がブロックされました。 署名キーの不一致: 更新は別の開発者によって署名されています。更新がブロックされました。 + リキッドグラスエフェクト + 滑らかなガラスのような外観でインターフェースを向上させます \ No newline at end of file diff --git a/core/presentation/src/commonMain/composeResources/values-ko/strings-ko.xml b/core/presentation/src/commonMain/composeResources/values-ko/strings-ko.xml index 3d682fd3..704cf4d5 100644 --- a/core/presentation/src/commonMain/composeResources/values-ko/strings-ko.xml +++ b/core/presentation/src/commonMain/composeResources/values-ko/strings-ko.xml @@ -579,4 +579,6 @@ 언어 선택 패키지 불일치: APK는 %1$s이지만 설치된 앱은 %2$s입니다. 업데이트가 차단되었습니다. 서명 키 불일치: 업데이트가 다른 개발자에 의해 서명되었습니다. 업데이트가 차단되었습니다. + 리퀴드 글라스 효과 + 매끄러운 유리 같은 외관으로 인터페이스를 향상시킵니다 \ No newline at end of file diff --git a/core/presentation/src/commonMain/composeResources/values-pl/strings-pl.xml b/core/presentation/src/commonMain/composeResources/values-pl/strings-pl.xml index 4e6f288a..db869a7d 100644 --- a/core/presentation/src/commonMain/composeResources/values-pl/strings-pl.xml +++ b/core/presentation/src/commonMain/composeResources/values-pl/strings-pl.xml @@ -546,4 +546,6 @@ Wybierz język Niezgodność pakietu: APK to %1$s, ale zainstalowana aplikacja to %2$s. Aktualizacja zablokowana. Niezgodność klucza podpisu: aktualizacja została podpisana przez innego programistę. Aktualizacja zablokowana. + Efekt płynnego szkła + Ulepsz interfejs o gładki, szklany wygląd \ No newline at end of file diff --git a/core/presentation/src/commonMain/composeResources/values-ru/strings-ru.xml b/core/presentation/src/commonMain/composeResources/values-ru/strings-ru.xml index 5fd684cd..e658a621 100644 --- a/core/presentation/src/commonMain/composeResources/values-ru/strings-ru.xml +++ b/core/presentation/src/commonMain/composeResources/values-ru/strings-ru.xml @@ -546,4 +546,6 @@ Выбрать язык Несоответствие пакета: APK — %1$s, но установленное приложение — %2$s. Обновление заблокировано. Несоответствие ключа подписи: обновление подписано другим разработчиком. Обновление заблокировано. + Эффект жидкого стекла + Улучшите интерфейс с помощью гладкого стеклянного оформления \ No newline at end of file diff --git a/core/presentation/src/commonMain/composeResources/values-tr/strings-tr.xml b/core/presentation/src/commonMain/composeResources/values-tr/strings-tr.xml index 601bfab0..68d7fccf 100644 --- a/core/presentation/src/commonMain/composeResources/values-tr/strings-tr.xml +++ b/core/presentation/src/commonMain/composeResources/values-tr/strings-tr.xml @@ -580,4 +580,6 @@ Dil seçin Paket uyumsuzluğu: APK %1$s, ancak yüklü uygulama %2$s. Güncelleme engellendi. İmza anahtarı uyumsuzluğu: güncelleme farklı bir geliştirici tarafından imzalanmış. Güncelleme engellendi. + Sıvı Cam Efekti + Arayüzü pürüzsüz cam benzeri bir görünümle geliştirin diff --git a/core/presentation/src/commonMain/composeResources/values-zh-rCN/strings-zh-rCN.xml b/core/presentation/src/commonMain/composeResources/values-zh-rCN/strings-zh-rCN.xml index f1dc983a..91f789a8 100644 --- a/core/presentation/src/commonMain/composeResources/values-zh-rCN/strings-zh-rCN.xml +++ b/core/presentation/src/commonMain/composeResources/values-zh-rCN/strings-zh-rCN.xml @@ -545,4 +545,6 @@ 选择语言 包名不匹配:APK 为 %1$s,但已安装的应用为 %2$s。更新已阻止。 签名密钥不匹配:更新由不同的开发者签名。更新已阻止。 + 液态玻璃效果 + 以流畅的玻璃质感提升界面外观 \ No newline at end of file From 6e24d9daa3d99e94ca1864a74028cf3f2b813669 Mon Sep 17 00:00:00 2001 From: rainxchzed Date: Fri, 20 Mar 2026 23:37:51 +0500 Subject: [PATCH 3/3] feat: enable liquid glass effect by default in HomeState - Update the default value of `isLiquidGlassEnabled` to `true` in `HomeState`. --- .../kotlin/zed/rainxch/home/presentation/HomeState.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/feature/home/presentation/src/commonMain/kotlin/zed/rainxch/home/presentation/HomeState.kt b/feature/home/presentation/src/commonMain/kotlin/zed/rainxch/home/presentation/HomeState.kt index 324a5d5f..fefc0378 100644 --- a/feature/home/presentation/src/commonMain/kotlin/zed/rainxch/home/presentation/HomeState.kt +++ b/feature/home/presentation/src/commonMain/kotlin/zed/rainxch/home/presentation/HomeState.kt @@ -19,5 +19,5 @@ data class HomeState( val isUpdateAvailable: Boolean = false, val currentPlatform: DiscoveryPlatform = DiscoveryPlatform.All, val isPlatformPopupVisible: Boolean = false, - val isLiquidGlassEnabled: Boolean = false, + val isLiquidGlassEnabled: Boolean = true, )