mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-08-09 16:38:10 +00:00
ref: continue without account to use local db instead of anon login
This commit is contained in:
parent
a826f58ded
commit
a068b86403
7 changed files with 132 additions and 14 deletions
|
|
@ -7,6 +7,7 @@ import androidx.activity.compose.setContent
|
|||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.SystemBarStyle
|
||||
import androidx.core.view.WindowCompat
|
||||
import com.nuvio.app.core.auth.AuthStorage
|
||||
import com.nuvio.app.core.deeplink.handleAppUrl
|
||||
import com.nuvio.app.core.storage.PlatformLocalAccountDataCleaner
|
||||
import com.nuvio.app.features.addons.AddonStorage
|
||||
|
|
@ -43,6 +44,7 @@ class MainActivity : ComponentActivity() {
|
|||
window.isNavigationBarContrastEnforced = false
|
||||
WindowCompat.getInsetsController(window, window.decorView).isAppearanceLightStatusBars = false
|
||||
AddonStorage.initialize(applicationContext)
|
||||
AuthStorage.initialize(applicationContext)
|
||||
LibraryStorage.initialize(applicationContext)
|
||||
WatchedStorage.initialize(applicationContext)
|
||||
MetaScreenSettingsStorage.initialize(applicationContext)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
package com.nuvio.app.core.auth
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
|
||||
actual object AuthStorage {
|
||||
private const val PREFS_NAME = "nuvio_auth"
|
||||
private const val KEY_ANONYMOUS_USER_ID = "anonymous_user_id"
|
||||
|
||||
private var preferences: SharedPreferences? = null
|
||||
|
||||
fun initialize(context: Context) {
|
||||
preferences = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
||||
}
|
||||
|
||||
actual fun loadAnonymousUserId(): String? =
|
||||
preferences?.getString(KEY_ANONYMOUS_USER_ID, null)
|
||||
|
||||
actual fun saveAnonymousUserId(userId: String) {
|
||||
preferences?.edit()?.putString(KEY_ANONYMOUS_USER_ID, userId)?.apply()
|
||||
}
|
||||
|
||||
actual fun clearAnonymousUserId() {
|
||||
preferences?.edit()?.remove(KEY_ANONYMOUS_USER_ID)?.apply()
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,8 @@ import io.github.jan.supabase.auth.auth
|
|||
import io.github.jan.supabase.auth.providers.builtin.Email
|
||||
import io.github.jan.supabase.auth.status.SessionStatus
|
||||
import io.github.jan.supabase.functions.functions
|
||||
import kotlin.uuid.ExperimentalUuidApi
|
||||
import kotlin.uuid.Uuid
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
|
|
@ -30,22 +32,33 @@ object AuthRepository {
|
|||
fun initialize() {
|
||||
if (initialized) return
|
||||
initialized = true
|
||||
|
||||
val savedAnonId = AuthStorage.loadAnonymousUserId()
|
||||
if (savedAnonId != null) {
|
||||
_state.value = AuthState.Authenticated(
|
||||
userId = savedAnonId,
|
||||
email = null,
|
||||
isAnonymous = true,
|
||||
)
|
||||
}
|
||||
|
||||
scope.launch {
|
||||
SupabaseProvider.client.auth.sessionStatus.collect { status ->
|
||||
if (AuthStorage.loadAnonymousUserId() != null) return@collect
|
||||
when (status) {
|
||||
is SessionStatus.Authenticated -> {
|
||||
val user = status.session.user
|
||||
_state.value = AuthState.Authenticated(
|
||||
userId = user?.id ?: "",
|
||||
email = user?.email,
|
||||
isAnonymous = user?.email.isNullOrBlank(),
|
||||
isAnonymous = false,
|
||||
)
|
||||
}
|
||||
is SessionStatus.NotAuthenticated -> {
|
||||
_state.value = AuthState.Unauthenticated
|
||||
}
|
||||
is SessionStatus.Initializing -> {
|
||||
_state.value = AuthState.Loading
|
||||
if (savedAnonId == null) _state.value = AuthState.Loading
|
||||
}
|
||||
is SessionStatus.RefreshFailure -> {
|
||||
_state.value = AuthState.Unauthenticated
|
||||
|
|
@ -55,13 +68,16 @@ object AuthRepository {
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun signInAnonymously(): Result<Unit> = runCatching {
|
||||
@OptIn(ExperimentalUuidApi::class)
|
||||
fun signInAnonymously() {
|
||||
_error.value = null
|
||||
SupabaseProvider.client.auth.signInAnonymously()
|
||||
Unit
|
||||
}.onFailure { e ->
|
||||
log.e(e) { "Anonymous sign-in failed" }
|
||||
_error.value = e.message ?: "Anonymous sign-in failed"
|
||||
val userId = Uuid.random().toString()
|
||||
AuthStorage.saveAnonymousUserId(userId)
|
||||
_state.value = AuthState.Authenticated(
|
||||
userId = userId,
|
||||
email = null,
|
||||
isAnonymous = true,
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun signUpWithEmail(email: String, password: String): Result<Unit> = runCatching {
|
||||
|
|
@ -89,7 +105,12 @@ object AuthRepository {
|
|||
|
||||
suspend fun signOut(): Result<Unit> = runCatching {
|
||||
_error.value = null
|
||||
SupabaseProvider.client.auth.signOut()
|
||||
val wasAnonymous = AuthStorage.loadAnonymousUserId() != null
|
||||
AuthStorage.clearAnonymousUserId()
|
||||
if (!wasAnonymous) {
|
||||
SupabaseProvider.client.auth.signOut()
|
||||
}
|
||||
_state.value = AuthState.Unauthenticated
|
||||
LocalAccountDataCleaner.wipe()
|
||||
}.onFailure { e ->
|
||||
log.e(e) { "Sign-out failed" }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
package com.nuvio.app.core.auth
|
||||
|
||||
internal expect object AuthStorage {
|
||||
fun loadAnonymousUserId(): String?
|
||||
fun saveAnonymousUserId(userId: String)
|
||||
fun clearAnonymousUserId()
|
||||
}
|
||||
|
|
@ -327,11 +327,7 @@ fun AuthScreen(
|
|||
|
||||
Button(
|
||||
onClick = {
|
||||
isLoading = true
|
||||
scope.launch {
|
||||
AuthRepository.signInAnonymously()
|
||||
isLoading = false
|
||||
}
|
||||
AuthRepository.signInAnonymously()
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ 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.auth.isAnonymous
|
||||
import com.nuvio.app.core.network.SupabaseProvider
|
||||
import com.nuvio.app.features.addons.AddonRepository
|
||||
import com.nuvio.app.features.details.MetaScreenSettingsRepository
|
||||
|
|
@ -94,6 +95,12 @@ object ProfileRepository {
|
|||
}
|
||||
|
||||
suspend fun pullProfiles() {
|
||||
if (AuthRepository.state.value.isAnonymous) {
|
||||
if (!_state.value.isLoaded) {
|
||||
_state.value = _state.value.copy(isLoaded = true)
|
||||
}
|
||||
return
|
||||
}
|
||||
runCatching {
|
||||
val result = SupabaseProvider.client.postgrest.rpc("sync_pull_profiles")
|
||||
val profiles = result.decodeList<NuvioProfile>()
|
||||
|
|
@ -139,6 +146,10 @@ object ProfileRepository {
|
|||
}
|
||||
|
||||
suspend fun pushProfiles(profiles: List<ProfilePushPayload>) {
|
||||
if (AuthRepository.state.value.isAnonymous) {
|
||||
applyPayloadsLocally(profiles)
|
||||
return
|
||||
}
|
||||
runCatching {
|
||||
val params = buildJsonObject {
|
||||
put("p_profiles", json.encodeToJsonElement(profiles))
|
||||
|
|
@ -211,6 +222,18 @@ object ProfileRepository {
|
|||
}
|
||||
|
||||
suspend fun deleteProfile(profileIndex: Int) {
|
||||
if (AuthRepository.state.value.isAnonymous) {
|
||||
val remaining = _state.value.profiles.filter { it.profileIndex != profileIndex }
|
||||
_state.value = _state.value.copy(
|
||||
profiles = remaining,
|
||||
activeProfile = if (_state.value.activeProfile?.profileIndex == profileIndex) remaining.firstOrNull() else _state.value.activeProfile,
|
||||
)
|
||||
if (_state.value.activeProfile != null) {
|
||||
activeProfileIndex = _state.value.activeProfile!!.profileIndex
|
||||
}
|
||||
persist()
|
||||
return
|
||||
}
|
||||
runCatching {
|
||||
val params = buildJsonObject { put("p_profile_id", profileIndex) }
|
||||
SupabaseProvider.client.postgrest.rpc("sync_delete_profile_data", params)
|
||||
|
|
@ -284,6 +307,31 @@ object ProfileRepository {
|
|||
}
|
||||
}
|
||||
|
||||
private fun applyPayloadsLocally(payloads: List<ProfilePushPayload>) {
|
||||
val authState = AuthRepository.state.value as? AuthState.Authenticated ?: return
|
||||
val profiles = payloads.map { p ->
|
||||
NuvioProfile(
|
||||
id = "",
|
||||
userId = authState.userId,
|
||||
profileIndex = p.profileIndex,
|
||||
name = p.name,
|
||||
avatarColorHex = p.avatarColorHex,
|
||||
avatarId = p.avatarId,
|
||||
usesPrimaryAddons = p.usesPrimaryAddons,
|
||||
usesPrimaryPlugins = p.usesPrimaryPlugins,
|
||||
)
|
||||
}.sortedBy { it.profileIndex }
|
||||
_state.value = _state.value.copy(
|
||||
profiles = profiles,
|
||||
isLoaded = true,
|
||||
activeProfile = profiles.find { it.profileIndex == activeProfileIndex } ?: profiles.firstOrNull(),
|
||||
)
|
||||
if (_state.value.activeProfile != null) {
|
||||
activeProfileIndex = _state.value.activeProfile!!.profileIndex
|
||||
}
|
||||
persist()
|
||||
}
|
||||
|
||||
private fun persist() {
|
||||
val authState = AuthRepository.state.value as? AuthState.Authenticated ?: return
|
||||
ProfileStorage.savePayload(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
package com.nuvio.app.core.auth
|
||||
|
||||
import platform.Foundation.NSUserDefaults
|
||||
|
||||
actual object AuthStorage {
|
||||
private const val KEY_ANONYMOUS_USER_ID = "anonymous_user_id"
|
||||
|
||||
actual fun loadAnonymousUserId(): String? =
|
||||
NSUserDefaults.standardUserDefaults.stringForKey(KEY_ANONYMOUS_USER_ID)
|
||||
|
||||
actual fun saveAnonymousUserId(userId: String) {
|
||||
NSUserDefaults.standardUserDefaults.setObject(userId, forKey = KEY_ANONYMOUS_USER_ID)
|
||||
}
|
||||
|
||||
actual fun clearAnonymousUserId() {
|
||||
NSUserDefaults.standardUserDefaults.removeObjectForKey(KEY_ANONYMOUS_USER_ID)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue