Skip to content

Commit e3a3a88

Browse files
committed
feat: persist and enhance discovery platform selection
- Implement persistent storage for the selected discovery platform in `TweaksRepository`. - Add `observeDiscoveryPlatform` to `HomeViewModel` to reactively update the UI state when the platform preference changes. - Rename `HomeAction.SwitchFilterPlatform` to `SwitchDiscoveryPlatform` and ensure the selection is saved to preferences. - Enhance the `PlatformsPopup` UI with `AnimatedVisibility` for smoother transitions and updated Material3 styling. - Refactor `TweaksRepositoryImpl` to move preference keys to a companion object and include new logic for handling `DiscoveryPlatform`. - Add a helper method `fromName` to `DiscoveryPlatform` for easier deserialization from stored preferences.
1 parent f1b9197 commit e3a3a88

6 files changed

Lines changed: 83 additions & 40 deletions

File tree

core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/TweaksRepositoryImpl.kt

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,14 @@ import androidx.datastore.preferences.core.stringPreferencesKey
99
import kotlinx.coroutines.flow.Flow
1010
import kotlinx.coroutines.flow.map
1111
import zed.rainxch.core.domain.model.AppTheme
12+
import zed.rainxch.core.domain.model.DiscoveryPlatform
1213
import zed.rainxch.core.domain.model.FontTheme
1314
import zed.rainxch.core.domain.model.InstallerType
1415
import zed.rainxch.core.domain.repository.TweaksRepository
1516

1617
class TweaksRepositoryImpl(
1718
private val preferences: DataStore<Preferences>,
1819
) : TweaksRepository {
19-
private val THEME_KEY = stringPreferencesKey("app_theme")
20-
private val AMOLED_KEY = booleanPreferencesKey("amoled_theme")
21-
private val IS_DARK_THEME_KEY = booleanPreferencesKey("is_dark_theme")
22-
private val FONT_KEY = stringPreferencesKey("font_theme")
23-
private val AUTO_DETECT_CLIPBOARD_KEY = booleanPreferencesKey("auto_detect_clipboard_links")
24-
private val INSTALLER_TYPE_KEY = stringPreferencesKey("installer_type")
25-
private val AUTO_UPDATE_KEY = booleanPreferencesKey("auto_update_enabled")
26-
private val UPDATE_CHECK_INTERVAL_KEY = longPreferencesKey("update_check_interval_hours")
27-
private val INCLUDE_PRE_RELEASES_KEY = booleanPreferencesKey("include_pre_releases")
28-
private val LIQUID_GLASS_ENABLED_KEY = booleanPreferencesKey("liquid_glass_enabled")
29-
private val HIDE_SEEN_ENABLED_KEY = booleanPreferencesKey("hide_seen_enabled")
30-
3120
override fun getThemeColor(): Flow<AppTheme> =
3221
preferences.data.map { prefs ->
3322
val themeName = prefs[THEME_KEY]
@@ -156,7 +145,32 @@ class TweaksRepositoryImpl(
156145
}
157146
}
158147

148+
override fun getDiscoveryPlatform(): Flow<DiscoveryPlatform> =
149+
preferences.data.map { prefs ->
150+
val platform = prefs[DISCOVERY_PLATFORM_KEY]
151+
DiscoveryPlatform.fromName(platform)
152+
}
153+
154+
override suspend fun setDiscoveryPlatform(platform: DiscoveryPlatform) {
155+
preferences.edit { prefs ->
156+
prefs[DISCOVERY_PLATFORM_KEY] = platform.name
157+
}
158+
}
159+
159160
companion object {
160-
const val DEFAULT_UPDATE_CHECK_INTERVAL_HOURS = 6L
161+
private const val DEFAULT_UPDATE_CHECK_INTERVAL_HOURS = 6L
162+
163+
private val THEME_KEY = stringPreferencesKey("app_theme")
164+
private val AMOLED_KEY = booleanPreferencesKey("amoled_theme")
165+
private val IS_DARK_THEME_KEY = booleanPreferencesKey("is_dark_theme")
166+
private val FONT_KEY = stringPreferencesKey("font_theme")
167+
private val DISCOVERY_PLATFORM_KEY = stringPreferencesKey("discovery_platform")
168+
private val AUTO_DETECT_CLIPBOARD_KEY = booleanPreferencesKey("auto_detect_clipboard_links")
169+
private val INSTALLER_TYPE_KEY = stringPreferencesKey("installer_type")
170+
private val AUTO_UPDATE_KEY = booleanPreferencesKey("auto_update_enabled")
171+
private val UPDATE_CHECK_INTERVAL_KEY = longPreferencesKey("update_check_interval_hours")
172+
private val INCLUDE_PRE_RELEASES_KEY = booleanPreferencesKey("include_pre_releases")
173+
private val LIQUID_GLASS_ENABLED_KEY = booleanPreferencesKey("liquid_glass_enabled")
174+
private val HIDE_SEEN_ENABLED_KEY = booleanPreferencesKey("hide_seen_enabled")
161175
}
162176
}

core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/model/DiscoveryPlatform.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,9 @@ enum class DiscoveryPlatform {
66
Macos,
77
Windows,
88
Linux,
9+
;
10+
11+
companion object {
12+
fun fromName(name: String?): DiscoveryPlatform = DiscoveryPlatform.entries.find { it.name == name } ?: All
13+
}
914
}

core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/repository/TweaksRepository.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package zed.rainxch.core.domain.repository
22

33
import kotlinx.coroutines.flow.Flow
44
import zed.rainxch.core.domain.model.AppTheme
5+
import zed.rainxch.core.domain.model.DiscoveryPlatform
56
import zed.rainxch.core.domain.model.FontTheme
67
import zed.rainxch.core.domain.model.InstallerType
78

@@ -49,4 +50,8 @@ interface TweaksRepository {
4950
fun getHideSeenEnabled(): Flow<Boolean>
5051

5152
suspend fun setHideSeenEnabled(enabled: Boolean)
53+
54+
fun getDiscoveryPlatform(): Flow<DiscoveryPlatform>
55+
56+
suspend fun setDiscoveryPlatform(platform: DiscoveryPlatform)
5257
}

feature/home/presentation/src/commonMain/kotlin/zed/rainxch/home/presentation/HomeAction.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ sealed interface HomeAction {
3232
val topic: TopicCategory?,
3333
) : HomeAction
3434

35-
data class SwitchFilterPlatform(
35+
data class SwitchDiscoveryPlatform(
3636
val platform: DiscoveryPlatform,
3737
) : HomeAction
3838

feature/home/presentation/src/commonMain/kotlin/zed/rainxch/home/presentation/HomeRoot.kt

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import androidx.compose.animation.expandVertically
77
import androidx.compose.animation.fadeIn
88
import androidx.compose.animation.fadeOut
99
import androidx.compose.animation.shrinkVertically
10+
import androidx.compose.animation.slideInVertically
11+
import androidx.compose.animation.slideOutVertically
1012
import androidx.compose.foundation.Image
1113
import androidx.compose.foundation.background
1214
import androidx.compose.foundation.clickable
@@ -24,7 +26,6 @@ import androidx.compose.foundation.layout.height
2426
import androidx.compose.foundation.layout.padding
2527
import androidx.compose.foundation.layout.size
2628
import androidx.compose.foundation.layout.width
27-
import androidx.compose.foundation.lazy.LazyColumn
2829
import androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridState
2930
import androidx.compose.foundation.lazy.staggeredgrid.LazyVerticalStaggeredGrid
3031
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells
@@ -51,14 +52,10 @@ import androidx.compose.material3.TopAppBar
5152
import androidx.compose.runtime.Composable
5253
import androidx.compose.runtime.CompositionLocalProvider
5354
import androidx.compose.runtime.LaunchedEffect
54-
import androidx.compose.runtime.State
5555
import androidx.compose.runtime.derivedStateOf
5656
import androidx.compose.runtime.getValue
57-
import androidx.compose.runtime.mutableIntStateOf
5857
import androidx.compose.runtime.remember
5958
import androidx.compose.runtime.rememberCoroutineScope
60-
import androidx.compose.runtime.rememberUpdatedState
61-
import androidx.compose.runtime.setValue
6259
import androidx.compose.ui.Alignment
6360
import androidx.compose.ui.Modifier
6461
import androidx.compose.ui.draw.clip
@@ -241,7 +238,7 @@ fun HomeScreen(
241238
HomeTopAppBar(
242239
currentPlatform = state.currentPlatform,
243240
onChangePlatform = {
244-
onAction(HomeAction.SwitchFilterPlatform(it))
241+
onAction(HomeAction.SwitchDiscoveryPlatform(it))
245242
},
246243
isPlatformPopupVisible = state.isPlatformPopupVisible,
247244
onTogglePlatformPopup = {
@@ -590,7 +587,11 @@ private fun HomeTopAppBar(
590587
}
591588
}
592589

593-
if (isPlatformPopupVisible) {
590+
AnimatedVisibility(
591+
visible = isPlatformPopupVisible,
592+
enter = slideInVertically(),
593+
exit = slideOutVertically(),
594+
) {
594595
Box {
595596
PlatformsPopup(
596597
onTogglePlatformPopup = onTogglePlatformPopup,
@@ -606,6 +607,7 @@ private fun HomeTopAppBar(
606607
)
607608
}
608609

610+
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
609611
@Composable
610612
private fun PlatformsPopup(
611613
onTogglePlatformPopup: () -> Unit,
@@ -618,19 +620,20 @@ private fun PlatformsPopup(
618620
Column(
619621
modifier =
620622
Modifier
621-
.clip(RoundedCornerShape(8.dp))
622-
.background(MaterialTheme.colorScheme.surfaceContainerHighest)
623-
.padding(6.dp),
623+
.width(250.dp)
624+
.clip(RoundedCornerShape(24.dp))
625+
.background(MaterialTheme.colorScheme.surfaceContainerHighest),
624626
) {
625627
DiscoveryPlatform.entries.forEach { platform ->
626628
Box(
627629
modifier =
628630
Modifier
631+
.fillMaxWidth()
629632
.clickable(onClick = {
630633
onChangePlatform(platform)
631634
onTogglePlatformPopup()
632635
})
633-
.padding(horizontal = 32.dp, vertical = 8.dp),
636+
.padding(horizontal = 24.dp, vertical = 8.dp),
634637
) {
635638
Row(
636639
verticalAlignment = Alignment.CenterVertically,
@@ -645,15 +648,14 @@ private fun PlatformsPopup(
645648
imageVector = Icons.Default.Done,
646649
contentDescription = null,
647650
tint = MaterialTheme.colorScheme.primary,
648-
modifier = Modifier.size(24.dp),
651+
modifier = Modifier.size(20.dp),
649652
)
650653
}
651654

652655
Text(
653656
text = platform.toLabel(),
654-
style = MaterialTheme.typography.titleMedium,
655-
fontWeight = FontWeight.Medium,
656-
color = MaterialTheme.colorScheme.onBackground,
657+
style = MaterialTheme.typography.titleMediumEmphasized,
658+
color = MaterialTheme.colorScheme.onSurface,
657659
)
658660
}
659661
}

feature/home/presentation/src/commonMain/kotlin/zed/rainxch/home/presentation/HomeViewModel.kt

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ import zed.rainxch.core.domain.repository.FavouritesRepository
2323
import zed.rainxch.core.domain.repository.InstalledAppsRepository
2424
import zed.rainxch.core.domain.repository.SeenReposRepository
2525
import zed.rainxch.core.domain.repository.StarredRepository
26-
import zed.rainxch.core.domain.use_cases.SyncInstalledAppsUseCase
2726
import zed.rainxch.core.domain.repository.TweaksRepository
27+
import zed.rainxch.core.domain.use_cases.SyncInstalledAppsUseCase
2828
import zed.rainxch.core.domain.utils.ShareManager
2929
import zed.rainxch.core.presentation.model.DiscoveryRepositoryUi
3030
import zed.rainxch.core.presentation.utils.toUi
@@ -66,6 +66,7 @@ class HomeViewModel(
6666
observeStarredRepos()
6767
observeLiquidGlassEnabled()
6868
observeSeenRepos()
69+
observeDiscoveryPlatform()
6970
observeHideSeenEnabled()
7071

7172
hasLoadedInitialData = true
@@ -120,6 +121,18 @@ class HomeViewModel(
120121
}
121122
}
122123

124+
private fun observeDiscoveryPlatform() {
125+
viewModelScope.launch {
126+
tweaksRepository.getDiscoveryPlatform().collect { platform ->
127+
_state.update {
128+
it.copy(
129+
currentPlatform = platform,
130+
)
131+
}
132+
}
133+
}
134+
}
135+
123136
private fun loadRepos(
124137
isInitial: Boolean = false,
125138
category: HomeCategory? = null,
@@ -147,13 +160,17 @@ class HomeViewModel(
147160

148161
return viewModelScope
149162
.launch {
163+
if (platform != null) {
164+
tweaksRepository.setDiscoveryPlatform(targetPlatform)
165+
}
166+
150167
_state.update {
151168
it.copy(
152169
isLoading = isInitial,
153170
isLoadingMore = !isInitial,
154171
errorMessage = null,
155-
currentCategory = targetCategory,
156172
currentPlatform = targetPlatform,
173+
currentCategory = targetCategory,
157174
selectedTopic = targetTopic,
158175
repos = if (isInitial) persistentListOf() else it.repos,
159176
)
@@ -269,8 +286,9 @@ class HomeViewModel(
269286
val cachedReposWithStatus = mapReposToUi(paginatedRepos.repos)
270287

271288
_state.update { currentState ->
272-
val merged = (currentState.repos + cachedReposWithStatus)
273-
.distinctBy { it.repository.fullName }
289+
val merged =
290+
(currentState.repos + cachedReposWithStatus)
291+
.distinctBy { it.repository.fullName }
274292

275293
currentState.copy(
276294
repos = merged.toImmutableList(),
@@ -291,8 +309,9 @@ class HomeViewModel(
291309
val newReposWithStatus = mapReposToUi(paginatedRepos.repos)
292310

293311
_state.update { currentState ->
294-
val merged = (currentState.repos + newReposWithStatus)
295-
.distinctBy { it.repository.fullName }
312+
val merged =
313+
(currentState.repos + newReposWithStatus)
314+
.distinctBy { it.repository.fullName }
296315

297316
currentState.copy(
298317
repos = merged.toImmutableList(),
@@ -309,9 +328,7 @@ class HomeViewModel(
309328
}
310329
}
311330

312-
private suspend fun mapReposToUi(
313-
repos: List<zed.rainxch.core.domain.model.GithubRepoSummary>,
314-
): List<DiscoveryRepositoryUi> {
331+
private suspend fun mapReposToUi(repos: List<zed.rainxch.core.domain.model.GithubRepoSummary>): List<DiscoveryRepositoryUi> {
315332
val installedAppsMap =
316333
installedAppsRepository
317334
.getAllInstalledApps()
@@ -421,15 +438,15 @@ class HomeViewModel(
421438
}
422439
}
423440

424-
is HomeAction.SwitchFilterPlatform -> {
441+
is HomeAction.SwitchDiscoveryPlatform -> {
425442
if (_state.value.currentPlatform != action.platform) {
426443
nextPageIndex = 1
427444
switchCategoryJob?.cancel()
428445
switchCategoryJob =
429446
viewModelScope.launch {
430447
loadRepos(isInitial = true, platform = action.platform)?.join()
431448
?: return@launch
432-
_events.send(HomeEvent.OnScrollToListTop)
449+
_events.send(OnScrollToListTop)
433450
}
434451
}
435452
}

0 commit comments

Comments
 (0)