mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-07-31 08:39:24 +00:00
Ignore self-originated realtime sync events
This commit is contained in:
parent
dccbfc43c2
commit
173584b191
13 changed files with 120 additions and 2 deletions
|
|
@ -12,6 +12,7 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
|||
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.core.sync.SyncClientIdentityStorage
|
||||
import com.nuvio.app.features.addons.AddonStorage
|
||||
import com.nuvio.app.features.collection.CollectionMobileSettingsStorage
|
||||
import com.nuvio.app.features.collection.CollectionStorage
|
||||
|
|
@ -66,6 +67,7 @@ class MainActivity : AppCompatActivity() {
|
|||
ThemeSettingsStorage.initialize(applicationContext)
|
||||
super.onCreate(savedInstanceState)
|
||||
window.setBackgroundDrawableResource(R.color.nuvio_background)
|
||||
SyncClientIdentityStorage.initialize(applicationContext)
|
||||
AddonStorage.initialize(applicationContext)
|
||||
AuthStorage.initialize(applicationContext)
|
||||
LibraryStorage.initialize(applicationContext)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
package com.nuvio.app.core.sync
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
|
||||
actual object SyncClientIdentityStorage {
|
||||
private const val preferencesName = "nuvio_sync_client_identity"
|
||||
private const val clientIdKey = "client_instance_id"
|
||||
|
||||
private var preferences: SharedPreferences? = null
|
||||
|
||||
fun initialize(context: Context) {
|
||||
preferences = context.getSharedPreferences(preferencesName, Context.MODE_PRIVATE)
|
||||
}
|
||||
|
||||
actual fun loadClientId(): String? =
|
||||
preferences?.getString(clientIdKey, null)
|
||||
|
||||
actual fun saveClientId(clientId: String) {
|
||||
preferences
|
||||
?.edit()
|
||||
?.putString(clientIdKey, clientId)
|
||||
?.apply()
|
||||
}
|
||||
}
|
||||
|
|
@ -206,6 +206,7 @@ object ProfileSettingsSync {
|
|||
put("p_profile_id", profileId)
|
||||
put("p_platform", MOBILE_SYNC_PLATFORM)
|
||||
put("p_settings_json", json.encodeToJsonElement(MobileProfileSettingsBlob.serializer(), blob))
|
||||
putSyncOriginClientId()
|
||||
}
|
||||
SupabaseProvider.client.postgrest.rpc("sync_push_profile_settings_blob", params)
|
||||
log.d { "pushToRemoteLocked(profileId=$profileId) — success" }
|
||||
|
|
|
|||
|
|
@ -135,13 +135,19 @@ object RealtimeSyncInvalidationService {
|
|||
private fun handleInsert(profileId: Int, record: JsonObject) {
|
||||
val eventId = record["id"]?.jsonPrimitive?.contentOrNull
|
||||
val createdAt = record["created_at"]?.jsonPrimitive?.contentOrNull
|
||||
val originClientId = record["origin_client_id"]?.jsonPrimitive?.contentOrNull
|
||||
if (originClientId != null && originClientId == SyncClientIdentity.currentClientId()) {
|
||||
log.d { "Ignoring self-originated sync invalidation id=$eventId originClientId=$originClientId" }
|
||||
return
|
||||
}
|
||||
val surface = record["surface"]?.jsonPrimitive?.contentOrNull ?: run {
|
||||
log.w { "Received sync invalidation without surface id=$eventId createdAt=$createdAt keys=${record.keys}" }
|
||||
return
|
||||
}
|
||||
val eventProfileId = record["profile_id"]?.jsonPrimitive?.intOrNull
|
||||
log.i {
|
||||
"Received sync invalidation id=$eventId surface=$surface eventProfile=$eventProfileId activeProfile=$profileId createdAt=$createdAt"
|
||||
"Received sync invalidation id=$eventId surface=$surface eventProfile=$eventProfileId " +
|
||||
"activeProfile=$profileId originClientId=$originClientId createdAt=$createdAt"
|
||||
}
|
||||
if (surface != "profiles" && eventProfileId != null && eventProfileId != profileId) {
|
||||
log.d { "Ignoring sync invalidation id=$eventId for inactive profile $eventProfileId" }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
package com.nuvio.app.core.sync
|
||||
|
||||
import kotlinx.serialization.json.JsonObjectBuilder
|
||||
import kotlinx.serialization.json.put
|
||||
import kotlin.random.Random
|
||||
|
||||
private const val CLIENT_ID_LENGTH = 32
|
||||
private const val CLIENT_ID_PREFIX = "nuvio-mobile-"
|
||||
private const val ORIGIN_CLIENT_ID_PARAM = "p_origin_client_id"
|
||||
private const val CLIENT_ID_ALPHABET = "abcdefghijklmnopqrstuvwxyz0123456789"
|
||||
|
||||
object SyncClientIdentity {
|
||||
private var cachedClientId: String? = null
|
||||
|
||||
fun currentClientId(): String {
|
||||
cachedClientId?.let { return it }
|
||||
|
||||
val stored = SyncClientIdentityStorage.loadClientId()
|
||||
?.trim()
|
||||
?.takeIf { it.isValidSyncClientId() }
|
||||
if (stored != null) {
|
||||
cachedClientId = stored
|
||||
return stored
|
||||
}
|
||||
|
||||
val generated = generateClientId()
|
||||
SyncClientIdentityStorage.saveClientId(generated)
|
||||
cachedClientId = generated
|
||||
return generated
|
||||
}
|
||||
|
||||
private fun generateClientId(): String =
|
||||
CLIENT_ID_PREFIX + buildString(CLIENT_ID_LENGTH) {
|
||||
repeat(CLIENT_ID_LENGTH) {
|
||||
append(CLIENT_ID_ALPHABET[Random.nextInt(CLIENT_ID_ALPHABET.length)])
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.isValidSyncClientId(): Boolean =
|
||||
length in 16..96 && all { it.isLetterOrDigit() || it == '-' || it == '_' }
|
||||
}
|
||||
|
||||
internal fun JsonObjectBuilder.putSyncOriginClientId() {
|
||||
put(ORIGIN_CLIENT_ID_PARAM, SyncClientIdentity.currentClientId())
|
||||
}
|
||||
|
||||
internal expect object SyncClientIdentityStorage {
|
||||
fun loadClientId(): String?
|
||||
fun saveClientId(clientId: String)
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.nuvio.app.features.addons
|
|||
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.nuvio.app.core.network.SupabaseProvider
|
||||
import com.nuvio.app.core.sync.putSyncOriginClientId
|
||||
import com.nuvio.app.features.profiles.ProfileRepository
|
||||
import io.github.jan.supabase.postgrest.postgrest
|
||||
import io.github.jan.supabase.postgrest.query.Order
|
||||
|
|
@ -151,6 +152,7 @@ object AddonRepository {
|
|||
val params = buildJsonObject {
|
||||
put("p_profile_id", currentProfileId)
|
||||
put("p_addons", json.encodeToJsonElement(addons))
|
||||
putSyncOriginClientId()
|
||||
}
|
||||
SupabaseProvider.client.postgrest.rpc("sync_push_addons", params)
|
||||
log.i { "pullFromServer() — migration push done (${addons.size} addons)" }
|
||||
|
|
@ -396,6 +398,7 @@ object AddonRepository {
|
|||
val params = buildJsonObject {
|
||||
put("p_profile_id", profileId)
|
||||
put("p_addons", json.encodeToJsonElement(addons))
|
||||
putSyncOriginClientId()
|
||||
}
|
||||
SupabaseProvider.client.postgrest.rpc("sync_push_addons", params)
|
||||
log.d { "pushToServer() — success" }
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ 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 com.nuvio.app.core.sync.putSyncOriginClientId
|
||||
import com.nuvio.app.features.profiles.ProfileRepository
|
||||
import io.github.jan.supabase.postgrest.postgrest
|
||||
import io.github.jan.supabase.postgrest.rpc
|
||||
|
|
@ -117,6 +118,7 @@ object CollectionSyncService {
|
|||
val params = buildJsonObject {
|
||||
put("p_profile_id", profileId)
|
||||
put("p_collections_json", jsonElement)
|
||||
putSyncOriginClientId()
|
||||
}
|
||||
SupabaseProvider.client.postgrest.rpc("sync_push_collections", params)
|
||||
log.d { "pushToRemote — success" }
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.nuvio.app.core.auth.AuthState
|
|||
import com.nuvio.app.core.network.SupabaseProvider
|
||||
import com.nuvio.app.core.sync.HOME_CATALOG_LEGACY_SYNC_PLATFORMS
|
||||
import com.nuvio.app.core.sync.HOME_CATALOG_SHARED_SYNC_PLATFORM
|
||||
import com.nuvio.app.core.sync.putSyncOriginClientId
|
||||
import com.nuvio.app.features.profiles.ProfileRepository
|
||||
import io.github.jan.supabase.postgrest.postgrest
|
||||
import io.github.jan.supabase.postgrest.rpc
|
||||
|
|
@ -136,6 +137,7 @@ object HomeCatalogSettingsSyncService {
|
|||
put("p_profile_id", profileId)
|
||||
put("p_platform", HOME_CATALOG_SHARED_SYNC_PLATFORM)
|
||||
put("p_settings_json", jsonElement)
|
||||
putSyncOriginClientId()
|
||||
}
|
||||
SupabaseProvider.client.postgrest.rpc("sync_push_home_catalog_settings", params)
|
||||
log.d { "pushToRemote — success" }
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import com.nuvio.app.core.auth.AuthRepository
|
|||
import com.nuvio.app.core.ui.NuvioToastController
|
||||
import com.nuvio.app.core.auth.AuthState
|
||||
import com.nuvio.app.core.network.SupabaseProvider
|
||||
import com.nuvio.app.core.sync.putSyncOriginClientId
|
||||
import com.nuvio.app.features.home.PosterShape
|
||||
import com.nuvio.app.features.profiles.ProfileRepository
|
||||
import com.nuvio.app.features.trakt.TraktAuthRepository
|
||||
|
|
@ -421,6 +422,7 @@ object LibraryRepository {
|
|||
val params = buildJsonObject {
|
||||
put("p_profile_id", profileId)
|
||||
put("p_items", json.encodeToJsonElement(syncItems))
|
||||
putSyncOriginClientId()
|
||||
}
|
||||
log.i { "Pushing library to server profile=$profileId itemCount=${syncItems.size}" }
|
||||
SupabaseProvider.client.postgrest.rpc("sync_push_library", params)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ 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.core.sync.putSyncOriginClientId
|
||||
import com.nuvio.app.features.addons.AddonRepository
|
||||
import com.nuvio.app.features.collection.CollectionMobileSettingsRepository
|
||||
import com.nuvio.app.features.collection.CollectionRepository
|
||||
|
|
@ -187,6 +188,7 @@ object ProfileRepository {
|
|||
val params = buildJsonObject {
|
||||
put("p_client_max_profiles", MAX_PROFILES)
|
||||
put("p_profiles", json.encodeToJsonElement(profiles))
|
||||
putSyncOriginClientId()
|
||||
}
|
||||
SupabaseProvider.client.postgrest.rpc("sync_push_profiles", params)
|
||||
pullProfiles()
|
||||
|
|
@ -277,7 +279,10 @@ object ProfileRepository {
|
|||
return
|
||||
}
|
||||
try {
|
||||
val params = buildJsonObject { put("p_profile_id", profileIndex) }
|
||||
val params = buildJsonObject {
|
||||
put("p_profile_id", profileIndex)
|
||||
putSyncOriginClientId()
|
||||
}
|
||||
SupabaseProvider.client.postgrest.rpc("sync_delete_profile_data", params)
|
||||
pullProfiles()
|
||||
} catch (e: Throwable) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.nuvio.app.features.watching.sync
|
||||
|
||||
import com.nuvio.app.core.network.SupabaseProvider
|
||||
import com.nuvio.app.core.sync.putSyncOriginClientId
|
||||
import com.nuvio.app.features.watchprogress.WatchProgressEntry
|
||||
import io.github.jan.supabase.postgrest.postgrest
|
||||
import io.github.jan.supabase.postgrest.rpc
|
||||
|
|
@ -104,6 +105,7 @@ object SupabaseProgressSyncAdapter : ProgressSyncAdapter {
|
|||
val params = buildJsonObject {
|
||||
put("p_profile_id", profileId)
|
||||
put("p_entries", json.encodeToJsonElement(syncEntries))
|
||||
putSyncOriginClientId()
|
||||
}
|
||||
SupabaseProvider.client.postgrest.rpc("sync_push_watch_progress", params)
|
||||
}
|
||||
|
|
@ -122,6 +124,7 @@ object SupabaseProgressSyncAdapter : ProgressSyncAdapter {
|
|||
val params = buildJsonObject {
|
||||
put("p_profile_id", profileId)
|
||||
put("p_keys", json.encodeToJsonElement(progressKeys))
|
||||
putSyncOriginClientId()
|
||||
}
|
||||
SupabaseProvider.client.postgrest.rpc("sync_delete_watch_progress", params)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.nuvio.app.features.watching.sync
|
||||
|
||||
import com.nuvio.app.core.network.SupabaseProvider
|
||||
import com.nuvio.app.core.sync.putSyncOriginClientId
|
||||
import com.nuvio.app.features.watched.WatchedItem
|
||||
import com.nuvio.app.features.watched.normalizeWatchedMarkedAtEpochMs
|
||||
import io.github.jan.supabase.postgrest.postgrest
|
||||
|
|
@ -102,6 +103,7 @@ object SupabaseWatchedSyncAdapter : WatchedSyncAdapter {
|
|||
val params = buildJsonObject {
|
||||
put("p_profile_id", profileId)
|
||||
put("p_items", json.encodeToJsonElement(syncItems))
|
||||
putSyncOriginClientId()
|
||||
}
|
||||
SupabaseProvider.client.postgrest.rpc("sync_push_watched_items", params)
|
||||
}
|
||||
|
|
@ -120,6 +122,7 @@ object SupabaseWatchedSyncAdapter : WatchedSyncAdapter {
|
|||
val params = buildJsonObject {
|
||||
put("p_profile_id", profileId)
|
||||
put("p_keys", json.encodeToJsonElement(keys))
|
||||
putSyncOriginClientId()
|
||||
}
|
||||
SupabaseProvider.client.postgrest.rpc("sync_delete_watched_items", params)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
package com.nuvio.app.core.sync
|
||||
|
||||
import platform.Foundation.NSUserDefaults
|
||||
|
||||
actual object SyncClientIdentityStorage {
|
||||
private const val clientIdKey = "client_instance_id"
|
||||
|
||||
actual fun loadClientId(): String? =
|
||||
NSUserDefaults.standardUserDefaults.stringForKey(clientIdKey)
|
||||
|
||||
actual fun saveClientId(clientId: String) {
|
||||
NSUserDefaults.standardUserDefaults.setObject(clientId, forKey = clientIdKey)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue