refactor(repo,vm): drop local relay persistence and viewmodel state

Removes the local_relay SharedPrefs entry, KeyRepository's
localRelayFlow / save/get/load helpers, and RelayViewModel's
local-relay state and update methods. StartupCoordinator no longer
wires the relay pool to localRelayFlow. reloadPrefs clears the
orphaned local_relay key on account switch.
This commit is contained in:
Barry Deen
2026-05-15 11:36:18 -04:00
parent 008d533ac5
commit 94445d3086
4 changed files with 20 additions and 87 deletions
@@ -55,7 +55,7 @@ class BlossomRepository(private val context: Context, pubkeyHex: String? = null)
// In-memory reset only. `prefs` is shared with KeyRepository
// (`wisp_prefs_{pubkey}`), so `prefs.edit().clear()` would also wipe
// `local_relay`, `relays`, `dm_relays`, etc. `reload(newPubkey)` below
// `relays`, `dm_relays`, etc. `reload(newPubkey)` below
// repoints to the new account's file.
fun clear() {
_servers.value = listOf(Blossom.DEFAULT_SERVER)
@@ -8,7 +8,6 @@ import com.wisp.app.nostr.Keys
import com.wisp.app.nostr.Nip19
import com.wisp.app.nostr.hexToByteArray
import com.wisp.app.nostr.toHex
import com.wisp.app.relay.LocalRelayConfig
import com.wisp.app.relay.RelayConfig
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -63,9 +62,6 @@ class KeyRepository(private val context: Context) {
private val _blockedRelays = MutableStateFlow(loadBlockedRelays())
val blockedRelaysFlow: StateFlow<List<String>> = _blockedRelays
private val _localRelay = MutableStateFlow(loadLocalRelay())
val localRelayFlow: StateFlow<LocalRelayConfig?> = _localRelay
// Strong reference to prevent GC — listener syncs flows when prefs change from any instance
private val prefsListener = SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
when (key) {
@@ -73,7 +69,6 @@ class KeyRepository(private val context: Context) {
"dm_relays" -> _dmRelays.value = loadDmRelays()
"search_relays" -> _searchRelays.value = loadSearchRelays()
"blocked_relays" -> _blockedRelays.value = loadBlockedRelays()
"local_relay" -> _localRelay.value = loadLocalRelay()
}
}
@@ -324,7 +319,7 @@ class KeyRepository(private val context: Context) {
_dmRelays.value = loadDmRelays()
_searchRelays.value = loadSearchRelays()
_blockedRelays.value = loadBlockedRelays()
_localRelay.value = loadLocalRelay()
prefs.edit().remove("local_relay").apply()
}
fun saveRelays(relays: List<RelayConfig>) {
@@ -379,22 +374,6 @@ class KeyRepository(private val context: Context) {
return try { json.decodeFromString(str) } catch (_: Exception) { emptyList() }
}
fun saveLocalRelay(config: LocalRelayConfig?) {
if (config != null) {
prefs.edit().putString("local_relay", json.encodeToString(config)).apply()
} else {
prefs.edit().remove("local_relay").apply()
}
_localRelay.value = config
}
fun getLocalRelay(): LocalRelayConfig? = _localRelay.value
private fun loadLocalRelay(): LocalRelayConfig? {
val str = prefs.getString("local_relay", null) ?: return null
return try { json.decodeFromString(str) } catch (_: Exception) { null }
}
fun isOnboardingComplete(): Boolean {
if (prefs.getBoolean("onboarding_done", false)) return true
// Migration for existing users who had the app before onboarding was added:
@@ -9,8 +9,6 @@ import com.wisp.app.nostr.Nip51
import com.wisp.app.nostr.Nip65
import com.wisp.app.nostr.NostrEvent
import com.wisp.app.nostr.NostrSigner
import com.wisp.app.relay.LocalRelayConfig
import com.wisp.app.relay.LocalRelayWritePolicy
import com.wisp.app.relay.RelayConfig
import com.wisp.app.relay.RelayPool
import com.wisp.app.relay.RelaySetType
@@ -30,7 +28,6 @@ class RelayViewModel(app: Application) : AndroidViewModel(app) {
val dmRelays: StateFlow<List<String>> = keyRepo.dmRelaysFlow
val searchRelays: StateFlow<List<String>> = keyRepo.searchRelaysFlow
val blockedRelays: StateFlow<List<String>> = keyRepo.blockedRelaysFlow
val localRelay: StateFlow<LocalRelayConfig?> = keyRepo.localRelayFlow
/** Re-point prefs at the current user's file so flows pick up their relay data. */
fun reload() {
@@ -52,36 +49,26 @@ class RelayViewModel(app: Application) : AndroidViewModel(app) {
fun addRelay(): Boolean {
val url = _newRelayUrl.value.trim().trimEnd('/')
if (url.isBlank()) return false
if (!RelayConfig.isValidUrl(url)) return false
when (_selectedTab.value) {
RelaySetType.LOCAL -> {
if (!RelayConfig.isLocalRelayUrl(url)) return false
if (localRelay.value != null) return false
keyRepo.saveLocalRelay(LocalRelayConfig(url))
RelaySetType.GENERAL -> {
if (relays.value.any { it.url == url }) return false
keyRepo.saveRelays(relays.value + RelayConfig(url))
}
else -> {
if (!RelayConfig.isValidUrl(url)) return false
when (_selectedTab.value) {
RelaySetType.GENERAL -> {
if (relays.value.any { it.url == url }) return false
keyRepo.saveRelays(relays.value + RelayConfig(url))
}
RelaySetType.DM -> {
if (url in dmRelays.value) return false
keyRepo.saveDmRelays(dmRelays.value + url)
}
RelaySetType.SEARCH -> {
if (url in searchRelays.value) return false
keyRepo.saveSearchRelays(searchRelays.value + url)
}
RelaySetType.BLOCKED -> {
if (url in blockedRelays.value) return false
val updated = blockedRelays.value + url
keyRepo.saveBlockedRelays(updated)
relayPool?.updateBlockedUrls(updated)
}
else -> {}
}
RelaySetType.DM -> {
if (url in dmRelays.value) return false
keyRepo.saveDmRelays(dmRelays.value + url)
}
RelaySetType.SEARCH -> {
if (url in searchRelays.value) return false
keyRepo.saveSearchRelays(searchRelays.value + url)
}
RelaySetType.BLOCKED -> {
if (url in blockedRelays.value) return false
val updated = blockedRelays.value + url
keyRepo.saveBlockedRelays(updated)
relayPool?.updateBlockedUrls(updated)
}
}
_newRelayUrl.value = ""
@@ -104,9 +91,6 @@ class RelayViewModel(app: Application) : AndroidViewModel(app) {
keyRepo.saveBlockedRelays(updated)
relayPool?.updateBlockedUrls(updated)
}
RelaySetType.LOCAL -> {
keyRepo.saveLocalRelay(null)
}
}
}
@@ -131,46 +115,26 @@ class RelayViewModel(app: Application) : AndroidViewModel(app) {
keyRepo.saveRelays(updated)
}
fun toggleLocalRelayEnabled() {
val current = localRelay.value ?: return
keyRepo.saveLocalRelay(current.copy(enabled = !current.enabled))
}
fun updateLocalRelayPolicy(writePolicy: LocalRelayWritePolicy) {
val current = localRelay.value ?: return
keyRepo.saveLocalRelay(current.copy(writePolicy = writePolicy))
}
fun updateLocalRelayKinds(kinds: Set<Int>) {
val current = localRelay.value ?: return
keyRepo.saveLocalRelay(current.copy(kinds = kinds))
}
fun publishRelayList(relayPool: RelayPool, signer: NostrSigner? = null): Boolean {
val s = signer ?: keyRepo.getKeypair()?.let { LocalSigner(it.privkey, it.pubkey) } ?: return false
return try {
val tab = _selectedTab.value
val tags: List<List<String>>
val kind: Int
val kind: Int = tab.eventKind
when (tab) {
RelaySetType.GENERAL -> {
tags = Nip65.buildRelayTags(relays.value)
kind = tab.eventKind
}
RelaySetType.DM -> {
tags = Nip51.buildRelaySetTags(dmRelays.value)
kind = tab.eventKind
}
RelaySetType.SEARCH -> {
tags = Nip51.buildRelaySetTags(searchRelays.value)
kind = tab.eventKind
}
RelaySetType.BLOCKED -> {
tags = Nip51.buildRelaySetTags(blockedRelays.value)
kind = tab.eventKind
}
RelaySetType.LOCAL -> return false // Local relays are never published
}
viewModelScope.launch {
@@ -227,16 +227,6 @@ class StartupCoordinator(
relayPool.updateDmRelays(dmRelays)
eventRepo.dmRelayUrls = dmRelays.toSet()
// Connect local relay if configured
relayPool.updateLocalRelay(keyRepo.getLocalRelay(), getUserPubkey())
// Observe local relay config changes
scope.launch {
keyRepo.localRelayFlow.drop(1).collectLatest { config ->
relayPool.updateLocalRelay(config, getUserPubkey())
}
}
scope.launch {
relayInfoRepo.prefetchAll(initialRelays.map { it.url })
}