feat: Implement ProfileStorage for managing user profiles and enhance ProfileRepository functionality

This commit is contained in:
tapframe 2026-03-28 20:48:59 +05:30
parent bbdf14d949
commit 69b9f5fabb
6 changed files with 118 additions and 1 deletions

View file

@ -8,6 +8,7 @@ import com.nuvio.app.features.addons.AddonStorage
import com.nuvio.app.features.library.LibraryStorage
import com.nuvio.app.features.home.HomeCatalogSettingsStorage
import com.nuvio.app.features.player.PlayerSettingsStorage
import com.nuvio.app.features.profiles.ProfileStorage
import com.nuvio.app.features.watched.WatchedStorage
import com.nuvio.app.features.watchprogress.ContinueWatchingPreferencesStorage
import com.nuvio.app.features.watchprogress.WatchProgressStorage
@ -21,6 +22,7 @@ class MainActivity : ComponentActivity() {
WatchedStorage.initialize(applicationContext)
HomeCatalogSettingsStorage.initialize(applicationContext)
PlayerSettingsStorage.initialize(applicationContext)
ProfileStorage.initialize(applicationContext)
ContinueWatchingPreferencesStorage.initialize(applicationContext)
WatchProgressStorage.initialize(applicationContext)

View file

@ -0,0 +1,25 @@
package com.nuvio.app.features.profiles
import android.content.Context
import android.content.SharedPreferences
actual object ProfileStorage {
private const val preferencesName = "nuvio_profile_cache"
private const val payloadKey = "profile_payload"
private var preferences: SharedPreferences? = null
fun initialize(context: Context) {
preferences = context.getSharedPreferences(preferencesName, Context.MODE_PRIVATE)
}
actual fun loadPayload(): String? =
preferences?.getString(payloadKey, null)
actual fun savePayload(payload: String) {
preferences
?.edit()
?.putString(payloadKey, payload)
?.apply()
}
}

View file

@ -156,8 +156,13 @@ fun App() {
LaunchedEffect(authState) {
when (authState) {
is AuthState.Loading -> gateScreen = AppGateScreen.Loading.name
is AuthState.Unauthenticated -> gateScreen = AppGateScreen.Auth.name
is AuthState.Unauthenticated -> {
ProfileRepository.clearInMemory()
gateScreen = AppGateScreen.Auth.name
}
is AuthState.Authenticated -> {
val authenticatedState = authState as AuthState.Authenticated
ProfileRepository.ensureLoaded(authenticatedState.userId)
if (gateScreen == AppGateScreen.Loading.name || gateScreen == AppGateScreen.Auth.name) {
gateScreen = AppGateScreen.ProfileSelection.name
}

View file

@ -1,6 +1,8 @@
package com.nuvio.app.features.profiles
import co.touchlab.kermit.Logger
import com.nuvio.app.core.auth.AuthRepository
import com.nuvio.app.core.auth.AuthState
import com.nuvio.app.core.network.SupabaseProvider
import io.github.jan.supabase.postgrest.postgrest
import io.github.jan.supabase.postgrest.rpc
@ -11,11 +13,21 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.encodeToJsonElement
import kotlinx.serialization.json.put
@Serializable
private data class StoredProfilePayload(
val userId: String,
val activeProfileIndex: Int = 1,
val profiles: List<NuvioProfile> = emptyList(),
)
object ProfileRepository {
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
private val log = Logger.withTag("ProfileRepository")
@ -25,9 +37,47 @@ object ProfileRepository {
val state: StateFlow<ProfileState> = _state.asStateFlow()
private var activeProfileIndex: Int = 1
private var loadedCacheForUserId: String? = null
val activeProfileId: Int get() = activeProfileIndex
fun ensureLoaded(userId: String) {
if (loadedCacheForUserId == userId && _state.value.isLoaded) return
loadedCacheForUserId = userId
val payload = ProfileStorage.loadPayload().orEmpty().trim()
if (payload.isEmpty()) {
_state.value = ProfileState()
activeProfileIndex = 1
return
}
val stored = runCatching {
json.decodeFromString<StoredProfilePayload>(payload)
}.getOrNull() ?: return
if (stored.userId != userId) {
_state.value = ProfileState()
activeProfileIndex = 1
return
}
val profiles = stored.profiles.sortedBy { it.profileIndex }
activeProfileIndex = stored.activeProfileIndex
_state.value = ProfileState(
profiles = profiles,
activeProfile = profiles.find { it.profileIndex == activeProfileIndex } ?: profiles.firstOrNull(),
isLoaded = profiles.isNotEmpty(),
)
_state.value.activeProfile?.let { activeProfileIndex = it.profileIndex }
}
fun clearInMemory() {
loadedCacheForUserId = null
activeProfileIndex = 1
_state.value = ProfileState()
}
suspend fun pullProfiles() {
runCatching {
val result = SupabaseProvider.client.postgrest.rpc("sync_pull_profiles")
@ -41,6 +91,7 @@ object ProfileRepository {
if (_state.value.activeProfile != null) {
activeProfileIndex = _state.value.activeProfile!!.profileIndex
}
persist()
}.onFailure { e ->
log.e(e) { "Failed to pull profiles" }
if (!_state.value.isLoaded) {
@ -54,6 +105,7 @@ object ProfileRepository {
_state.value = _state.value.copy(
activeProfile = _state.value.profiles.find { it.profileIndex == profileIndex },
)
persist()
}
suspend fun pushProfiles(profiles: List<ProfilePushPayload>) {
@ -201,6 +253,19 @@ object ProfileRepository {
emptyList()
}
}
private fun persist() {
val authState = AuthRepository.state.value as? AuthState.Authenticated ?: return
ProfileStorage.savePayload(
json.encodeToString(
StoredProfilePayload(
userId = authState.userId,
activeProfileIndex = activeProfileIndex,
profiles = _state.value.profiles,
),
),
)
}
}
@kotlinx.serialization.Serializable

View file

@ -0,0 +1,6 @@
package com.nuvio.app.features.profiles
internal expect object ProfileStorage {
fun loadPayload(): String?
fun savePayload(payload: String)
}

View file

@ -0,0 +1,14 @@
package com.nuvio.app.features.profiles
import platform.Foundation.NSUserDefaults
actual object ProfileStorage {
private const val payloadKey = "profile_payload"
actual fun loadPayload(): String? =
NSUserDefaults.standardUserDefaults.stringForKey(payloadKey)
actual fun savePayload(payload: String) {
NSUserDefaults.standardUserDefaults.setObject(payload, forKey = payloadKey)
}
}