mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-06 03:38:23 +00:00
refactor: replace Amethyst.instance reads in screens with app services and host ports
- AppServices gains appStores (the preference DataStores) and torBootstrapped; the chess view-model factory, search history and the relay-group Tor banner read them instead of Amethyst.instance. - The relay-group banner reads Tor routing from the host; ClinkOfferPayer takes the host's MoneyOpRelayRouting; the napplet manifest lookup reads LocalCache directly. - AccountViewModelHost gains hasBackedUpKeys/setHasBackedUpKeys for the backup nudge, over LocalPreferences on Android. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S7FuNBSKiyVecARSoE4B9P
This commit is contained in:
@@ -20,7 +20,7 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.napplet
|
||||
|
||||
import com.vitorpamplona.amethyst.Amethyst
|
||||
import com.vitorpamplona.amethyst.commons.model.cache.LocalCache
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip5aStaticWebsites.NamedSiteEvent
|
||||
import com.vitorpamplona.quartz.nip5aStaticWebsites.RootSiteEvent
|
||||
@@ -38,7 +38,7 @@ fun resolveNappletMeta(
|
||||
untitled: String,
|
||||
): Pair<String, String?> {
|
||||
val events =
|
||||
Amethyst.instance.cache
|
||||
LocalCache
|
||||
.filter(
|
||||
Filter(
|
||||
kinds = listOf(RootNappletEvent.KIND, NamedNappletEvent.KIND, RootSiteEvent.KIND, NamedSiteEvent.KIND),
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.service
|
||||
|
||||
import com.vitorpamplona.amethyst.Amethyst
|
||||
import com.vitorpamplona.amethyst.commons.model.Account
|
||||
import com.vitorpamplona.amethyst.commons.tor.MoneyOpRelayRouting
|
||||
import com.vitorpamplona.quartz.experimental.clink.client.OfferClient
|
||||
import com.vitorpamplona.quartz.experimental.clink.offers.OfferEvent
|
||||
import com.vitorpamplona.quartz.experimental.clink.offers.OfferResponse
|
||||
@@ -57,6 +57,7 @@ object ClinkOfferPayer {
|
||||
*/
|
||||
suspend fun requestInvoice(
|
||||
account: Account,
|
||||
moneyOpRelays: MoneyOpRelayRouting,
|
||||
offer: NOffer,
|
||||
amountSats: Long? = null,
|
||||
timeoutMs: Long = DEFAULT_TIMEOUT_MS,
|
||||
@@ -103,7 +104,7 @@ object ClinkOfferPayer {
|
||||
// pushing the payment through Tor (and failing on services that block Tor exits) even when
|
||||
// the user disabled Tor for money operations. The subscribe() below triggers a reconnect, and
|
||||
// BasicRelayClient rebuilds any socket left on the now-wrong (Tor) transport onto clearnet.
|
||||
val torState = Amethyst.instance.torEvaluatorFlow
|
||||
val torState = moneyOpRelays
|
||||
torState.registerMoneyOpRelays(relays)
|
||||
account.client.subscribe(subId, filters, listener)
|
||||
try {
|
||||
|
||||
+1
-1
@@ -182,7 +182,7 @@ fun ClinkOfferPreview(
|
||||
) {
|
||||
val amount = if (amountRequired) amountInput.toLongOrNull() else useOffer.price
|
||||
|
||||
val response = ClinkOfferPayer.requestInvoice(accountViewModel.account, useOffer, amountSats = amount)
|
||||
val response = ClinkOfferPayer.requestInvoice(accountViewModel.account, accountViewModel.host.moneyOpRelays, useOffer, amountSats = amount)
|
||||
|
||||
val bolt11 = response?.bolt11
|
||||
val movedTo =
|
||||
|
||||
@@ -59,6 +59,7 @@ import com.vitorpamplona.amethyst.commons.ui.navigation.routes.routeFor
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.ClickableUserPicture
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.LoadUser
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.UsernameDisplay
|
||||
import com.vitorpamplona.amethyst.commons.ui.platform.LocalAppServices
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chess.ChessViewModelFactory
|
||||
@@ -127,7 +128,7 @@ fun RenderLiveChessChallenge(
|
||||
val chessViewModel: ChessViewModel =
|
||||
viewModel(
|
||||
key = "ChessViewModel-${accountViewModel.account.userProfile().pubkeyHex}",
|
||||
factory = ChessViewModelFactory(accountViewModel.account),
|
||||
factory = ChessViewModelFactory(accountViewModel.account, LocalAppServices.current.appStores),
|
||||
)
|
||||
|
||||
val isOpenChallenge = event.opponentPubkey() == null
|
||||
|
||||
@@ -26,6 +26,7 @@ import com.vitorpamplona.amethyst.commons.browser.BrowserIconRegistry
|
||||
import com.vitorpamplona.amethyst.commons.connectedApps.signers.NostrSignerPermissionStore
|
||||
import com.vitorpamplona.amethyst.commons.favorites.FavoriteAppsRegistry
|
||||
import com.vitorpamplona.amethyst.commons.model.location.DeviceLocation
|
||||
import com.vitorpamplona.amethyst.commons.model.preferences.AppPreferenceStores
|
||||
import com.vitorpamplona.amethyst.commons.napplet.permissions.NappletPermissionLedger
|
||||
import com.vitorpamplona.amethyst.commons.service.AppServices
|
||||
import com.vitorpamplona.amethyst.commons.service.BlossomServerFinder
|
||||
@@ -33,6 +34,8 @@ import com.vitorpamplona.amethyst.commons.service.ai.AltTextSuggester
|
||||
import com.vitorpamplona.amethyst.commons.tor.TorSettingsFlow
|
||||
import com.vitorpamplona.amethyst.service.ai.MLKitImageLabelService
|
||||
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.NamecoinNameResolver
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
/**
|
||||
* [AppServices] over the main process's app modules. Every member is a getter, so installing
|
||||
@@ -52,6 +55,12 @@ object AndroidAppServices : AppServices {
|
||||
|
||||
override val torSettings: TorSettingsFlow get() = Amethyst.instance.torPrefs.value
|
||||
|
||||
override val torBootstrapped: Flow<Boolean> get() =
|
||||
Amethyst.instance.torManager.status
|
||||
.map { it.isFullyBootstrapped }
|
||||
|
||||
override val appStores: AppPreferenceStores get() = Amethyst.instance.appStores
|
||||
|
||||
override val namecoinResolver: NamecoinNameResolver get() = Amethyst.instance.namecoinResolver
|
||||
|
||||
override val blossomServerFinder: BlossomServerFinder = AndroidBlossomServerFinder
|
||||
|
||||
+7
@@ -86,6 +86,13 @@ class AndroidAccountViewModelHost(
|
||||
|
||||
override val scheduledPostStore: ScheduledPostStore get() = modules.scheduledPostStore
|
||||
|
||||
override suspend fun hasBackedUpKeys(npub: String): StateFlow<Boolean> = LocalPreferences.hasBackedUpKeys(npub)
|
||||
|
||||
override suspend fun setHasBackedUpKeys(
|
||||
npub: String,
|
||||
value: Boolean,
|
||||
) = LocalPreferences.setHasBackedUpKeys(value, npub)
|
||||
|
||||
override suspend fun anonymizeVoice(
|
||||
input: Path,
|
||||
presetName: String,
|
||||
|
||||
+4
-5
@@ -62,7 +62,6 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.vitorpamplona.amethyst.Amethyst
|
||||
import com.vitorpamplona.amethyst.commons.buzz.ui.PresenceDot
|
||||
import com.vitorpamplona.amethyst.commons.chats.publicChannels.relayGroup.newestTimelineNote
|
||||
import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
|
||||
@@ -107,6 +106,7 @@ import com.vitorpamplona.amethyst.commons.ui.note.UserPicture
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.rememberTimeAgoLabels
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.timeAgoShort
|
||||
import com.vitorpamplona.amethyst.commons.ui.platform.AppBottomBar
|
||||
import com.vitorpamplona.amethyst.commons.ui.platform.LocalAppServices
|
||||
import com.vitorpamplona.amethyst.commons.ui.pluralStringRes
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.LocalDisplaySettings
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.buzz.BuzzDmListViewModel
|
||||
@@ -366,7 +366,7 @@ fun RelayGroupChannelListScreen(
|
||||
// was never consulted, and neither was whether this relay is Tor-routed at all (Tor being *on*
|
||||
// doesn't mean this relay goes through it; the per-role presets decide).
|
||||
val torEvaluation =
|
||||
Amethyst.instance.torEvaluatorFlow.flow
|
||||
accountViewModel.host.torRelayEvaluation
|
||||
.collectAsStateWithLifecycle()
|
||||
// The same predicate the relay pool itself dials with, so the banner can't claim Tor for a relay
|
||||
// the app is reaching over clearnet (onion / localhost / trusted-off-Tor are all folded in here).
|
||||
@@ -374,9 +374,8 @@ fun RelayGroupChannelListScreen(
|
||||
|
||||
// While Tor is bootstrapping every Tor-routed relay is silent — that's Tor's own failure (and its
|
||||
// own dialog), so don't let it read as "this relay blocks Tor exits".
|
||||
val torStatus by Amethyst.instance.torManager.status
|
||||
.collectAsStateWithLifecycle()
|
||||
val torIsUp = torStatus.isFullyBootstrapped
|
||||
val torIsUp by LocalAppServices.current.torBootstrapped
|
||||
.collectAsStateWithLifecycle(false)
|
||||
|
||||
// The offer adds the relay to the kind-10089 Trusted list, which only moves it to clearnet while
|
||||
// trusted relays are *off* Tor. Under the Small-Payloads / Full-Privacy presets they are on Tor,
|
||||
|
||||
+2
-3
@@ -22,7 +22,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.chess
|
||||
|
||||
import androidx.datastore.core.DataStore
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import com.vitorpamplona.amethyst.Amethyst
|
||||
import com.vitorpamplona.amethyst.commons.model.preferences.AppPreferenceStores
|
||||
|
||||
/**
|
||||
* Where Android keeps the dismissed-chess-games store.
|
||||
@@ -32,5 +32,4 @@ import com.vitorpamplona.amethyst.Amethyst
|
||||
* data over is not worth the code. Anyone who had dismissed a game sees it once
|
||||
* more and dismisses it again.
|
||||
*/
|
||||
internal val chessDismissedGamesData: DataStore<Preferences>
|
||||
get() = Amethyst.instance.appStores.getDataStore("chess_dismissed_games_v2")
|
||||
internal fun chessDismissedGamesData(stores: AppPreferenceStores): DataStore<Preferences> = stores.getDataStore("chess_dismissed_games_v2")
|
||||
|
||||
+2
-1
@@ -76,6 +76,7 @@ import com.vitorpamplona.amethyst.commons.resources.chess_loading_game
|
||||
import com.vitorpamplona.amethyst.commons.resources.go_back
|
||||
import com.vitorpamplona.amethyst.commons.resources.relay_settings
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.platform.LocalAppServices
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.Height4dpModifier
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
@@ -104,7 +105,7 @@ fun ChessGameScreen(
|
||||
viewModel(
|
||||
viewModelStoreOwner = activity,
|
||||
key = "ChessViewModel-${accountViewModel.account.userProfile().pubkeyHex}",
|
||||
factory = ChessViewModelFactory(accountViewModel.account),
|
||||
factory = ChessViewModelFactory(accountViewModel.account, LocalAppServices.current.appStores),
|
||||
)
|
||||
|
||||
val activeGames by chessViewModel.activeGames.collectAsState()
|
||||
|
||||
+2
-1
@@ -82,6 +82,7 @@ import com.vitorpamplona.amethyst.commons.resources.route_chess
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.RefresheableBox
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.bottombars.FabBottomBarPadded
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.platform.LocalAppServices
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chess.datasource.ChessSubscription
|
||||
@@ -102,7 +103,7 @@ fun ChessLobbyScreen(
|
||||
viewModel(
|
||||
viewModelStoreOwner = activity,
|
||||
key = "ChessViewModel-${accountViewModel.account.userProfile().pubkeyHex}",
|
||||
factory = ChessViewModelFactory(accountViewModel.account),
|
||||
factory = ChessViewModelFactory(accountViewModel.account, LocalAppServices.current.appStores),
|
||||
)
|
||||
|
||||
// Subscribe to chess events when screen is visible
|
||||
|
||||
+3
-1
@@ -24,6 +24,7 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewmodel.CreationExtras
|
||||
import com.vitorpamplona.amethyst.commons.model.Account
|
||||
import com.vitorpamplona.amethyst.commons.model.preferences.AppPreferenceStores
|
||||
import com.vitorpamplona.amethyst.commons.nip64Chess.ChessDismissedGamesStore
|
||||
import com.vitorpamplona.amethyst.commons.nip64Chess.ChessPollingDefaults
|
||||
import com.vitorpamplona.amethyst.commons.nip64Chess.ChessViewModel
|
||||
@@ -34,6 +35,7 @@ import kotlin.reflect.KClass
|
||||
*/
|
||||
class ChessViewModelFactory(
|
||||
private val account: Account,
|
||||
private val appStores: AppPreferenceStores,
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(
|
||||
@@ -47,7 +49,7 @@ class ChessViewModelFactory(
|
||||
fetcher = AndroidRelayFetcher(account),
|
||||
metadataProvider = AndroidMetadataProvider(),
|
||||
pollingConfig = ChessPollingDefaults.android,
|
||||
dismissedStorage = ChessDismissedGamesStore(chessDismissedGamesData),
|
||||
dismissedStorage = ChessDismissedGamesStore(chessDismissedGamesData(appStores)),
|
||||
) as T
|
||||
}
|
||||
throw IllegalArgumentException("Unknown ViewModel class")
|
||||
|
||||
+2
-1
@@ -37,6 +37,7 @@ import com.vitorpamplona.amethyst.commons.nip64Chess.ui.NewChessGameDialog
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.new_chess_game
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.platform.LocalAppServices
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.Size55Modifier
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
@@ -56,7 +57,7 @@ fun NewChessGameButton(
|
||||
val chessViewModel: ChessViewModel =
|
||||
viewModel(
|
||||
key = "ChessViewModel-${accountViewModel.account.userProfile().pubkeyHex}",
|
||||
factory = ChessViewModelFactory(accountViewModel.account),
|
||||
factory = ChessViewModelFactory(accountViewModel.account, LocalAppServices.current.appStores),
|
||||
)
|
||||
|
||||
FloatingActionButton(
|
||||
|
||||
+9
-9
@@ -41,7 +41,6 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.LocalPreferences
|
||||
import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
|
||||
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
|
||||
import com.vitorpamplona.amethyst.commons.model.navigation.Route
|
||||
@@ -58,7 +57,7 @@ import com.vitorpamplona.amethyst.commons.ui.theme.StdVertSpacer
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.quartz.nip01Core.core.hexToByteArray
|
||||
import com.vitorpamplona.quartz.nip19Bech32.toNpub
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
@@ -66,7 +65,7 @@ import kotlinx.coroutines.launch
|
||||
* generated accounts that haven't saved their secret key yet. It never blocks
|
||||
* navigation: the user either backs up (navigates to [Route.AccountBackup]) or
|
||||
* confirms they already saved the key. Both actions flip the per-account
|
||||
* [LocalPreferences.setHasBackedUpKeys] flag so the nudge stops appearing.
|
||||
* backed-up flag so the nudge stops appearing.
|
||||
*/
|
||||
@Composable
|
||||
fun BackupKeysNudge(
|
||||
@@ -81,19 +80,20 @@ fun BackupKeysNudge(
|
||||
|
||||
// Seed the reactive flag off a background read. Rendering only proceeds once the
|
||||
// flow resolves, so the observing composable never conditionally calls hooks.
|
||||
val flow by produceState<MutableStateFlow<Boolean>?>(initialValue = null, key1 = npub) {
|
||||
value = LocalPreferences.hasBackedUpKeys(npub)
|
||||
val flow by produceState<StateFlow<Boolean>?>(initialValue = null, key1 = npub) {
|
||||
value = accountViewModel.host.hasBackedUpKeys(npub)
|
||||
}
|
||||
|
||||
flow?.let { stateFlow ->
|
||||
WatchBackupKeysNudge(stateFlow, npub, nav, modifier)
|
||||
WatchBackupKeysNudge(stateFlow, npub, accountViewModel, nav, modifier)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun WatchBackupKeysNudge(
|
||||
stateFlow: MutableStateFlow<Boolean>,
|
||||
stateFlow: StateFlow<Boolean>,
|
||||
npub: String,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
modifier: Modifier,
|
||||
) {
|
||||
@@ -106,11 +106,11 @@ private fun WatchBackupKeysNudge(
|
||||
onBackupNow = {
|
||||
// Best-effort: opening the backup screen counts as backing up so the
|
||||
// nudge doesn't linger after the user follows through.
|
||||
scope.launch { LocalPreferences.setHasBackedUpKeys(true, npub) }
|
||||
scope.launch { accountViewModel.host.setHasBackedUpKeys(npub, true) }
|
||||
nav.nav(Route.AccountBackup)
|
||||
},
|
||||
onAlreadySaved = {
|
||||
scope.launch { LocalPreferences.setHasBackedUpKeys(true, npub) }
|
||||
scope.launch { accountViewModel.host.setHasBackedUpKeys(npub, true) }
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
+1
-1
@@ -488,7 +488,7 @@ private fun SendPaymentLoaded(
|
||||
amount: Long?,
|
||||
followMoved: Boolean,
|
||||
) {
|
||||
val response = ClinkOfferPayer.requestInvoice(accountViewModel.account, useOffer, amountSats = amount)
|
||||
val response = ClinkOfferPayer.requestInvoice(accountViewModel.account, accountViewModel.host.moneyOpRelays, useOffer, amountSats = amount)
|
||||
|
||||
val bolt11 = response?.bolt11
|
||||
val movedTo =
|
||||
|
||||
+3
-2
@@ -79,7 +79,6 @@ import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.vitorpamplona.amethyst.Amethyst
|
||||
import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
|
||||
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
|
||||
import com.vitorpamplona.amethyst.commons.model.cache.LocalCache
|
||||
@@ -130,6 +129,7 @@ import com.vitorpamplona.amethyst.commons.ui.note.UserCompose
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.creators.userSuggestions.ShowUserSuggestionList
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.creators.userSuggestions.UserSuggestionState
|
||||
import com.vitorpamplona.amethyst.commons.ui.platform.AppBottomBar
|
||||
import com.vitorpamplona.amethyst.commons.ui.platform.LocalAppServices
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.LocalDisplaySettings
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.chats.rooms.ChannelName
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.BasicRelaySetupInfoClickableRow
|
||||
@@ -168,7 +168,8 @@ fun SearchScreen(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
val historyStorage = remember { DataStoreSearchHistoryStorage(Amethyst.instance.appStores.getDataStore(DataStoreSearchHistoryStorage.FILE_NAME)) }
|
||||
val appStores = LocalAppServices.current.appStores
|
||||
val historyStorage = remember { DataStoreSearchHistoryStorage(appStores.getDataStore(DataStoreSearchHistoryStorage.FILE_NAME)) }
|
||||
val searchBarViewModel: SearchBarViewModel =
|
||||
viewModel(
|
||||
// Keyed on the seed: navigating from one screen's search button to another's has to
|
||||
|
||||
+8
@@ -25,10 +25,12 @@ import com.vitorpamplona.amethyst.commons.browser.BrowserIconRegistry
|
||||
import com.vitorpamplona.amethyst.commons.connectedApps.signers.NostrSignerPermissionStore
|
||||
import com.vitorpamplona.amethyst.commons.favorites.FavoriteAppsRegistry
|
||||
import com.vitorpamplona.amethyst.commons.model.location.DeviceLocation
|
||||
import com.vitorpamplona.amethyst.commons.model.preferences.AppPreferenceStores
|
||||
import com.vitorpamplona.amethyst.commons.napplet.permissions.NappletPermissionLedger
|
||||
import com.vitorpamplona.amethyst.commons.service.ai.AltTextSuggester
|
||||
import com.vitorpamplona.amethyst.commons.tor.TorSettingsFlow
|
||||
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.NamecoinNameResolver
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
/**
|
||||
* App-wide stores that outlive any one account and that screens read directly: the favorites
|
||||
@@ -53,6 +55,12 @@ interface AppServices {
|
||||
|
||||
val torSettings: TorSettingsFlow
|
||||
|
||||
/** Whether the embedded Tor service has fully bootstrapped, so Tor-routed relays can answer. */
|
||||
val torBootstrapped: Flow<Boolean>
|
||||
|
||||
/** The app's preference files, one DataStore per name. */
|
||||
val appStores: AppPreferenceStores
|
||||
|
||||
/** Resolves `.bit` names and `d/`/`id/` identifiers over the configured ElectrumX servers. */
|
||||
val namecoinResolver: NamecoinNameResolver
|
||||
|
||||
|
||||
+13
@@ -36,6 +36,7 @@ import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.sockets.WebsocketBuilder
|
||||
import kotlinx.collections.immutable.PersistentMap
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import okio.Path
|
||||
|
||||
@@ -93,6 +94,18 @@ interface AccountViewModelHost {
|
||||
/** A new on-device writing assistant, or null where the platform has none. */
|
||||
fun createWritingAssistant(): WritingAssistant? = null
|
||||
|
||||
/**
|
||||
* Whether the account [npub] has saved its secret key, as a live flag. Absent reads as true,
|
||||
* so a platform that does not track it never shows the backup nudge.
|
||||
*/
|
||||
suspend fun hasBackedUpKeys(npub: String): StateFlow<Boolean> = MutableStateFlow(true)
|
||||
|
||||
/** Records whether the account [npub] has saved its secret key. */
|
||||
suspend fun setHasBackedUpKeys(
|
||||
npub: String,
|
||||
value: Boolean,
|
||||
) = Unit
|
||||
|
||||
/** Public keys of every account saved on this device. */
|
||||
val savedAccounts: Flow<Set<HexKey>>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user