mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-09-20 14:01:22 +02:00
Establishing a scope for each account loaded.
This commit is contained in:
@@ -26,8 +26,6 @@ import android.util.Log
|
|||||||
import androidx.security.crypto.EncryptedSharedPreferences
|
import androidx.security.crypto.EncryptedSharedPreferences
|
||||||
import coil3.disk.DiskCache
|
import coil3.disk.DiskCache
|
||||||
import coil3.memory.MemoryCache
|
import coil3.memory.MemoryCache
|
||||||
import com.vitorpamplona.amethyst.model.Account
|
|
||||||
import com.vitorpamplona.amethyst.model.AccountSettings
|
|
||||||
import com.vitorpamplona.amethyst.model.LocalCache
|
import com.vitorpamplona.amethyst.model.LocalCache
|
||||||
import com.vitorpamplona.amethyst.model.accountsCache.AccountCacheState
|
import com.vitorpamplona.amethyst.model.accountsCache.AccountCacheState
|
||||||
import com.vitorpamplona.amethyst.service.connectivity.ConnectivityManager
|
import com.vitorpamplona.amethyst.service.connectivity.ConnectivityManager
|
||||||
@@ -54,13 +52,9 @@ import com.vitorpamplona.amethyst.service.relayClient.speedLogger.RelaySpeedLogg
|
|||||||
import com.vitorpamplona.amethyst.service.uploads.nip95.Nip95CacheFactory
|
import com.vitorpamplona.amethyst.service.uploads.nip95.Nip95CacheFactory
|
||||||
import com.vitorpamplona.amethyst.ui.navigation.navs.EmptyNav.scope
|
import com.vitorpamplona.amethyst.ui.navigation.navs.EmptyNav.scope
|
||||||
import com.vitorpamplona.amethyst.ui.tor.TorManager
|
import com.vitorpamplona.amethyst.ui.tor.TorManager
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.toHexKey
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
|
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
|
||||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerInternal
|
|
||||||
import com.vitorpamplona.quartz.nip03Timestamp.VerificationStateCache
|
import com.vitorpamplona.quartz.nip03Timestamp.VerificationStateCache
|
||||||
import com.vitorpamplona.quartz.nip03Timestamp.ots.okhttp.OtsBlockHeightCache
|
import com.vitorpamplona.quartz.nip03Timestamp.ots.okhttp.OtsBlockHeightCache
|
||||||
import com.vitorpamplona.quartz.nip55AndroidSigner.client.NostrSignerExternal
|
|
||||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@@ -161,13 +155,14 @@ class Amethyst : Application() {
|
|||||||
// Coordinates all subscriptions for the Nostr Client
|
// Coordinates all subscriptions for the Nostr Client
|
||||||
val sources: RelaySubscriptionsCoordinator = RelaySubscriptionsCoordinator(LocalCache, client, applicationIOScope)
|
val sources: RelaySubscriptionsCoordinator = RelaySubscriptionsCoordinator(LocalCache, client, applicationIOScope)
|
||||||
|
|
||||||
|
// keeps all accounts live
|
||||||
val accountsCache =
|
val accountsCache =
|
||||||
AccountCacheState(
|
AccountCacheState(
|
||||||
geolocationFlow = locationManager.geohashStateFlow,
|
geolocationFlow = locationManager.geohashStateFlow,
|
||||||
nwcFilterAssembler = sources.nwc,
|
nwcFilterAssembler = sources.nwc,
|
||||||
|
contentResolver = contentResolver,
|
||||||
cache = cache,
|
cache = cache,
|
||||||
client = client,
|
client = client,
|
||||||
scope = applicationIOScope,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// saves the .content of NIP-95 blobs in disk to save memory
|
// saves the .content of NIP-95 blobs in disk to save memory
|
||||||
@@ -242,31 +237,6 @@ class Amethyst : Application() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadAccount(accountSettings: AccountSettings): Account {
|
|
||||||
val keyPair = accountSettings.keyPair
|
|
||||||
return accountsCache.loadAccount(
|
|
||||||
signer =
|
|
||||||
if (keyPair.privKey != null) {
|
|
||||||
NostrSignerInternal(keyPair)
|
|
||||||
} else {
|
|
||||||
when (val packageName = accountSettings.externalSignerPackageName) {
|
|
||||||
null -> NostrSignerInternal(keyPair)
|
|
||||||
else ->
|
|
||||||
NostrSignerExternal(
|
|
||||||
pubKey = keyPair.pubKey.toHexKey(),
|
|
||||||
packageName = packageName,
|
|
||||||
contentResolver = contentResolver,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
accountSettings = accountSettings,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun removeAccount(pubkey: HexKey) {
|
|
||||||
accountsCache.removeAccount(pubkey)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
lateinit var instance: Amethyst
|
lateinit var instance: Amethyst
|
||||||
private set
|
private set
|
||||||
|
@@ -20,6 +20,8 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.amethyst.model.accountsCache
|
package com.vitorpamplona.amethyst.model.accountsCache
|
||||||
|
|
||||||
|
import android.content.ContentResolver
|
||||||
|
import android.util.Log
|
||||||
import androidx.collection.LruCache
|
import androidx.collection.LruCache
|
||||||
import com.vitorpamplona.amethyst.model.Account
|
import com.vitorpamplona.amethyst.model.Account
|
||||||
import com.vitorpamplona.amethyst.model.AccountSettings
|
import com.vitorpamplona.amethyst.model.AccountSettings
|
||||||
@@ -27,22 +29,59 @@ import com.vitorpamplona.amethyst.model.LocalCache
|
|||||||
import com.vitorpamplona.amethyst.service.location.LocationState
|
import com.vitorpamplona.amethyst.service.location.LocationState
|
||||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.nwc.NWCPaymentFilterAssembler
|
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.nwc.NWCPaymentFilterAssembler
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.toHexKey
|
||||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerInternal
|
||||||
|
import com.vitorpamplona.quartz.nip55AndroidSigner.client.NostrSignerExternal
|
||||||
|
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import kotlinx.coroutines.cancel
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
|
||||||
class AccountCacheState(
|
class AccountCacheState(
|
||||||
val geolocationFlow: StateFlow<LocationState.LocationResult>,
|
val geolocationFlow: StateFlow<LocationState.LocationResult>,
|
||||||
val nwcFilterAssembler: NWCPaymentFilterAssembler,
|
val nwcFilterAssembler: NWCPaymentFilterAssembler,
|
||||||
|
val contentResolver: ContentResolver,
|
||||||
val cache: LocalCache,
|
val cache: LocalCache,
|
||||||
val client: INostrClient,
|
val client: INostrClient,
|
||||||
val scope: CoroutineScope,
|
|
||||||
) {
|
) {
|
||||||
val accounts = LruCache<HexKey, Account>(20)
|
val accounts =
|
||||||
|
object : LruCache<HexKey, Account>(20) {
|
||||||
|
override fun entryRemoved(
|
||||||
|
evicted: Boolean,
|
||||||
|
key: HexKey,
|
||||||
|
oldValue: Account,
|
||||||
|
newValue: Account?,
|
||||||
|
) {
|
||||||
|
super.entryRemoved(evicted, key, oldValue, newValue)
|
||||||
|
oldValue.scope.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun removeAccount(pubkey: HexKey) = accounts.remove(pubkey)
|
fun removeAccount(pubkey: HexKey) = accounts.remove(pubkey)
|
||||||
|
|
||||||
|
fun loadAccount(accountSettings: AccountSettings): Account =
|
||||||
|
loadAccount(
|
||||||
|
signer =
|
||||||
|
if (accountSettings.keyPair.privKey != null) {
|
||||||
|
NostrSignerInternal(accountSettings.keyPair)
|
||||||
|
} else {
|
||||||
|
when (val packageName = accountSettings.externalSignerPackageName) {
|
||||||
|
null -> NostrSignerInternal(accountSettings.keyPair)
|
||||||
|
else ->
|
||||||
|
NostrSignerExternal(
|
||||||
|
pubKey = accountSettings.keyPair.pubKey.toHexKey(),
|
||||||
|
packageName = packageName,
|
||||||
|
contentResolver = contentResolver,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
accountSettings = accountSettings,
|
||||||
|
)
|
||||||
|
|
||||||
fun loadAccount(
|
fun loadAccount(
|
||||||
signer: NostrSigner,
|
signer: NostrSigner,
|
||||||
accountSettings: AccountSettings,
|
accountSettings: AccountSettings,
|
||||||
@@ -57,7 +96,14 @@ class AccountCacheState(
|
|||||||
nwcFilterAssembler = nwcFilterAssembler,
|
nwcFilterAssembler = nwcFilterAssembler,
|
||||||
cache = cache,
|
cache = cache,
|
||||||
client = client,
|
client = client,
|
||||||
scope = scope,
|
scope =
|
||||||
|
CoroutineScope(
|
||||||
|
Dispatchers.Default +
|
||||||
|
SupervisorJob() +
|
||||||
|
CoroutineExceptionHandler { _, throwable ->
|
||||||
|
Log.e("AccountCacheState", "Account ${signer.pubKey} caught exception: ${throwable.message}", throwable)
|
||||||
|
},
|
||||||
|
),
|
||||||
).also {
|
).also {
|
||||||
accounts.put(signer.pubKey, it)
|
accounts.put(signer.pubKey, it)
|
||||||
}
|
}
|
||||||
|
@@ -71,7 +71,7 @@ class EventNotificationConsumer(
|
|||||||
LocalPreferences.loadAccountConfigFromEncryptedStorage(it.npub)?.let { acc ->
|
LocalPreferences.loadAccountConfigFromEncryptedStorage(it.npub)?.let { acc ->
|
||||||
Log.d(TAG, "New Notification Testing if for ${it.npub}")
|
Log.d(TAG, "New Notification Testing if for ${it.npub}")
|
||||||
try {
|
try {
|
||||||
val account = Amethyst.instance.loadAccount(acc)
|
val account = Amethyst.instance.accountsCache.loadAccount(acc)
|
||||||
consumeIfMatchesAccount(event, account)
|
consumeIfMatchesAccount(event, account)
|
||||||
matchAccount = true
|
matchAccount = true
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@@ -128,7 +128,7 @@ class EventNotificationConsumer(
|
|||||||
LocalPreferences.loadAccountConfigFromEncryptedStorage(it.npub)?.let { accountSettings ->
|
LocalPreferences.loadAccountConfigFromEncryptedStorage(it.npub)?.let { accountSettings ->
|
||||||
Log.d(TAG, "New Notification Testing if for ${it.npub}")
|
Log.d(TAG, "New Notification Testing if for ${it.npub}")
|
||||||
try {
|
try {
|
||||||
val account = Amethyst.instance.loadAccount(accountSettings)
|
val account = Amethyst.instance.accountsCache.loadAccount(accountSettings)
|
||||||
consumeNotificationEvent(event, account)
|
consumeNotificationEvent(event, account)
|
||||||
matchAccount = true
|
matchAccount = true
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
@@ -59,7 +59,7 @@ class RegisterAccounts(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return mapNotNullAsync(remainingTos) { info ->
|
return mapNotNullAsync(remainingTos) { info ->
|
||||||
val account = Amethyst.instance.loadAccount(info.accountSettings)
|
val account = Amethyst.instance.accountsCache.loadAccount(info.accountSettings)
|
||||||
RelayAuthEvent.create(info.relays, notificationToken, account.signer)
|
RelayAuthEvent.create(info.relays, notificationToken, account.signer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -185,7 +185,7 @@ class AccountStateViewModel : ViewModel() {
|
|||||||
route: Route? = null,
|
route: Route? = null,
|
||||||
) = withContext(Dispatchers.Main) {
|
) = withContext(Dispatchers.Main) {
|
||||||
_accountContent.update {
|
_accountContent.update {
|
||||||
AccountState.LoggedIn(Amethyst.instance.loadAccount(accountSettings), route)
|
AccountState.LoggedIn(Amethyst.instance.accountsCache.loadAccount(accountSettings), route)
|
||||||
}
|
}
|
||||||
|
|
||||||
collectorJob?.cancel()
|
collectorJob?.cancel()
|
||||||
@@ -390,12 +390,12 @@ class AccountStateViewModel : ViewModel() {
|
|||||||
// log off and relogin with the 0 account
|
// log off and relogin with the 0 account
|
||||||
prepareLogoutOrSwitch()
|
prepareLogoutOrSwitch()
|
||||||
LocalPreferences.deleteAccount(accountInfo)
|
LocalPreferences.deleteAccount(accountInfo)
|
||||||
Amethyst.instance.removeAccount(accountInfo.npub.bechToBytes().toHexKey())
|
Amethyst.instance.accountsCache.removeAccount(accountInfo.npub.bechToBytes().toHexKey())
|
||||||
loginWithDefaultAccount()
|
loginWithDefaultAccount()
|
||||||
} else {
|
} else {
|
||||||
// delete without switching logins
|
// delete without switching logins
|
||||||
LocalPreferences.deleteAccount(accountInfo)
|
LocalPreferences.deleteAccount(accountInfo)
|
||||||
Amethyst.instance.removeAccount(accountInfo.npub.bechToBytes().toHexKey())
|
Amethyst.instance.accountsCache.removeAccount(accountInfo.npub.bechToBytes().toHexKey())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user