diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/Amber.kt b/app/src/main/java/com/greenart7c3/nostrsigner/Amber.kt index 2cdf4651..13101e71 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/Amber.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/Amber.kt @@ -10,11 +10,6 @@ import androidx.work.ExistingPeriodicWorkPolicy import androidx.work.OneTimeWorkRequest import androidx.work.PeriodicWorkRequestBuilder import androidx.work.WorkManager -import coil3.ImageLoader -import coil3.SingletonImageLoader -import coil3.network.okhttp.OkHttpNetworkFetcherFactory -import coil3.request.crossfade -import coil3.util.DebugLogger import com.greenart7c3.nostrsigner.database.AppDatabase import com.greenart7c3.nostrsigner.models.Account import com.greenart7c3.nostrsigner.models.AmberSettings @@ -26,7 +21,6 @@ import com.greenart7c3.nostrsigner.relays.AmberRelayStats import com.greenart7c3.nostrsigner.service.ClearLogsWorker import com.greenart7c3.nostrsigner.service.ConnectivityService import com.greenart7c3.nostrsigner.service.NotificationDataSource -import com.greenart7c3.nostrsigner.service.ProfileDataSource import com.greenart7c3.nostrsigner.service.RelayDisconnectService import com.vitorpamplona.ammolite.relays.COMMON_FEED_TYPES import com.vitorpamplona.ammolite.relays.MutableSubscriptionCache @@ -35,7 +29,6 @@ import com.vitorpamplona.ammolite.relays.Relay import com.vitorpamplona.ammolite.relays.RelaySetupInfo import com.vitorpamplona.ammolite.relays.RelaySetupInfoToConnect import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle -import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent import com.vitorpamplona.quartz.nip01Core.tags.people.PTag import com.vitorpamplona.quartz.nip34Git.issue.GitIssueEvent import com.vitorpamplona.quartz.nip34Git.repository.GitRepositoryEvent @@ -70,8 +63,6 @@ class Amber : Application() { HttpClientManager.getHttpClient(useProxy) } val client: NostrClient = NostrClient(factory) - var profileClient = ConcurrentHashMap() - val profileDataSource = ConcurrentHashMap() val applicationIOScope = CoroutineScope(Dispatchers.IO + SupervisorJob()) private var databases = ConcurrentHashMap() var settings: AmberSettings = AmberSettings() @@ -339,93 +330,6 @@ class Amber : Application() { applicationIOScope.cancel() } - fun fetchProfileData( - account: Account, - onPictureFound: (String) -> Unit, - ) { - @Suppress("KotlinConstantConditions") - if (BuildConfig.FLAVOR == "offline") { - return - } - - SingletonImageLoader.setSafe { - ImageLoader.Builder(this) - .crossfade(true) - .logger(DebugLogger()) - .components { - add( - OkHttpNetworkFetcherFactory( - callFactory = { - HttpClientManager.getHttpClient(settings.useProxy) - }, - ), - ) - } - .build() - } - - val lastMetaData = LocalPreferences.getLastMetadataUpdate(this, account.npub) - val lastCheck = LocalPreferences.getLastCheck(this, account.npub) - val oneDayAgo = TimeUtils.oneDayAgo() - val fifteenMinutesAgo = TimeUtils.fifteenMinutesAgo() - if ((lastMetaData == 0L || oneDayAgo > lastMetaData) && (lastCheck == 0L || fifteenMinutesAgo > lastCheck)) { - Log.d(TAG, "Fetching profile data for ${account.npub}") - LocalPreferences.setLastCheck(this, account.npub, TimeUtils.now()) - val relays = LocalPreferences.loadSettingsFromEncryptedStorage().defaultProfileRelays.map { - RelaySetupInfoToConnect( - url = it.url, - read = it.read, - write = it.write, - feedTypes = it.feedTypes, - forceProxy = settings.useProxy, - ) - }.toTypedArray() - profileClient.getOrPut(account.hexKey, defaultValue = { NostrClient(factory) }).reconnect(relays) - profileDataSource.getOrPut( - account.hexKey, - defaultValue = { - ProfileDataSource( - client = profileClient.get(account.hexKey)!!, - account = account, - onReceiveEvent = { event -> - applicationIOScope.launch { - if (event.kind == MetadataEvent.KIND) { - (event as MetadataEvent).contactMetaData()?.let { metadata -> - metadata.name?.let { name -> - account.name = name - applicationIOScope.launch { - LocalPreferences.saveToEncryptedStorage(account = account, context = this@Amber) - } - } - - metadata.profilePicture()?.let { url -> - LocalPreferences.saveProfileUrlToEncryptedStorage(url, account.npub) - LocalPreferences.setLastMetadataUpdate(this@Amber, account.npub, TimeUtils.now()) - onPictureFound(url) - } - } - } - profileDataSource[account.hexKey]?.stop() - profileClient[account.hexKey]?.getAll()?.forEach { - Log.d(TAG, "disconnecting profile relay ${it.url}") - it.disconnect() - } - } - }, - ) - }, - ) - - profileDataSource.get(account.hexKey)?.start() - } else { - Log.d(TAG, "Using cached profile data for ${account.npub}") - LocalPreferences.loadProfileUrlFromEncryptedStorage(account.npub)?.let { - onPictureFound(it) - return - } - } - } - suspend fun sendFeedBack( subject: String, body: String, diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/service/ProfileFetcherService.kt b/app/src/main/java/com/greenart7c3/nostrsigner/service/ProfileFetcherService.kt new file mode 100644 index 00000000..bdd92512 --- /dev/null +++ b/app/src/main/java/com/greenart7c3/nostrsigner/service/ProfileFetcherService.kt @@ -0,0 +1,108 @@ +package com.greenart7c3.nostrsigner.service + +import android.util.Log +import coil3.ImageLoader +import coil3.SingletonImageLoader +import coil3.network.okhttp.OkHttpNetworkFetcherFactory +import coil3.request.crossfade +import coil3.util.DebugLogger +import com.greenart7c3.nostrsigner.Amber +import com.greenart7c3.nostrsigner.Amber.Companion.TAG +import com.greenart7c3.nostrsigner.BuildConfig +import com.greenart7c3.nostrsigner.LocalPreferences +import com.greenart7c3.nostrsigner.models.Account +import com.greenart7c3.nostrsigner.okhttp.HttpClientManager +import com.vitorpamplona.ammolite.relays.NostrClient +import com.vitorpamplona.ammolite.relays.RelaySetupInfoToConnect +import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent +import com.vitorpamplona.quartz.utils.TimeUtils +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch + +class ProfileFetcherService { + fun fetchProfileData( + account: Account, + onPictureFound: (String) -> Unit, + ) { + @Suppress("KotlinConstantConditions") + if (BuildConfig.FLAVOR == "offline") { + return + } + + SingletonImageLoader.setSafe { + ImageLoader.Builder(Amber.instance) + .crossfade(true) + .logger(DebugLogger()) + .components { + add( + OkHttpNetworkFetcherFactory( + callFactory = { + HttpClientManager.getHttpClient(Amber.instance.settings.useProxy) + }, + ), + ) + } + .build() + } + + val lastMetaData = LocalPreferences.getLastMetadataUpdate(Amber.instance, account.npub) + val lastCheck = LocalPreferences.getLastCheck(Amber.instance, account.npub) + val oneDayAgo = TimeUtils.oneDayAgo() + val fifteenMinutesAgo = TimeUtils.fifteenMinutesAgo() + if ((lastMetaData == 0L || oneDayAgo > lastMetaData) && (lastCheck == 0L || fifteenMinutesAgo > lastCheck)) { + Log.d(TAG, "Fetching profile data for ${account.npub}") + LocalPreferences.setLastCheck(Amber.instance, account.npub, TimeUtils.now()) + val relays = LocalPreferences.loadSettingsFromEncryptedStorage().defaultProfileRelays.map { + RelaySetupInfoToConnect( + url = it.url, + read = it.read, + write = it.write, + feedTypes = it.feedTypes, + forceProxy = Amber.instance.settings.useProxy, + ) + }.toTypedArray() + val client = NostrClient(Amber.instance.factory) + client.reconnect(relays) + val profileDataSource = ProfileDataSource( + client = client, + account = account, + onReceiveEvent = { event -> + Amber.instance.applicationIOScope.launch { + if (event.kind == MetadataEvent.KIND) { + (event as MetadataEvent).contactMetaData()?.let { metadata -> + metadata.name?.let { name -> + account.name = name + Amber.instance.applicationIOScope.launch { + LocalPreferences.saveToEncryptedStorage(account = account, context = Amber.instance) + } + } + + metadata.profilePicture()?.let { url -> + LocalPreferences.saveProfileUrlToEncryptedStorage(url, account.npub) + LocalPreferences.setLastMetadataUpdate(Amber.instance, account.npub, TimeUtils.now()) + onPictureFound(url) + } + } + } + } + }, + ) + + profileDataSource.start() + Amber.instance.applicationIOScope.launch { + delay(60000) + profileDataSource.stop() + client.getAll().forEach { + Log.d(TAG, "disconnecting profile relay ${it.url}") + it.disconnect() + } + } + } else { + Log.d(TAG, "Using cached profile data for ${account.npub}") + LocalPreferences.loadProfileUrlFromEncryptedStorage(account.npub)?.let { + onPictureFound(it) + return + } + } + } +} diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/MainScreen.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/MainScreen.kt index 35682645..99ee2876 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/MainScreen.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/MainScreen.kt @@ -63,6 +63,7 @@ import com.greenart7c3.nostrsigner.models.IntentData import com.greenart7c3.nostrsigner.models.IntentResultType import com.greenart7c3.nostrsigner.relays.AmberRelayStats import com.greenart7c3.nostrsigner.service.NotificationDataSource +import com.greenart7c3.nostrsigner.service.ProfileFetcherService import com.greenart7c3.nostrsigner.ui.actions.AccountBackupScreen import com.greenart7c3.nostrsigner.ui.actions.AccountsBottomSheet import com.greenart7c3.nostrsigner.ui.actions.ActiveRelaysScreen @@ -173,7 +174,7 @@ fun MainScreen( }, ) - Amber.instance.fetchProfileData( + ProfileFetcherService().fetchProfileData( account = account, onPictureFound = { profileUrl = it diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/LoginWithPubKey.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/LoginWithPubKey.kt index f2ea9e3a..3b19501b 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/LoginWithPubKey.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/LoginWithPubKey.kt @@ -39,11 +39,11 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.window.core.layout.WindowWidthSizeClass import coil3.compose.AsyncImage -import com.greenart7c3.nostrsigner.Amber import com.greenart7c3.nostrsigner.BuildConfig import com.greenart7c3.nostrsigner.R import com.greenart7c3.nostrsigner.models.Account import com.greenart7c3.nostrsigner.models.Permission +import com.greenart7c3.nostrsigner.service.ProfileFetcherService import com.greenart7c3.nostrsigner.service.toShortenHex import com.greenart7c3.nostrsigner.ui.RememberType import com.greenart7c3.nostrsigner.ui.navigation.Route @@ -58,7 +58,7 @@ fun ProfilePicture(account: Account) { var profileUrl by remember { mutableStateOf(null) } LaunchedEffect(Unit) { launch(Dispatchers.IO) { - Amber.instance.fetchProfileData( + ProfileFetcherService().fetchProfileData( account = account, onPictureFound = { profileUrl = it