mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-05 11:18:24 +00:00
refactor: move the profile and the app, NIP-46, Namecoin and OTS settings screens
Clear the platform blockers in the profile and settings screens and move them to commonsUI: - Copying an npub, nprofile or picture URL uses the shared Clipboard.setText; sharing a profile uses TextSharer; copy confirmations are shared notices. - The identity-claim brand icons (X, Telegram, Mastodon, GitHub) are Compose resources. - AppPlatform gains SpeakNameButton (Android text-to-speech) and rememberAppLanguages (Android reads locales_config.xml). - AppServices gains namecoinSettings, namecoinClients (a port over the JVM-only ElectrumX and Core RPC clients), otsSettings and nip46ClientStore, so these screens stop reading Amethyst.instance. - OtsSettings and OtsSettingsStore move to commonMain on quartz's new BitcoinExplorerUrls; RpcProbeResult moves to quartz commonMain; deviceDescription() is an expect in commons. - The payment sheet opens wallets through WalletAppLauncher; payViaIntent stays in the app for its remaining callers. - The profile zaps total and ordering use the shared BigDecimal API. 210 of 252 screens are now shared. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S7FuNBSKiyVecARSoE4B9P
This commit is contained in:
@@ -207,6 +207,8 @@ import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.polls.PollsScreen
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.polls.results.PollResultsScreen
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.privacy.PrivacyOptionsScreen
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.products.ProductsScreen
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.ProfileScreen
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.payment.SendPaymentScreen
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.publicChats.PublicChatsScreen
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.qrcode.ShowQRScreen
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.redirect.LoadRedirectScreen
|
||||
@@ -232,11 +234,14 @@ import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.HomeTabsSe
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.MessagesSettingsScreen
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.MutedThreadsScreen
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.NIP47SetupScreen
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.NamecoinSettingsScreen
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.NowPlayingSettingsScreen
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.OtsSettingsScreen
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.ProfileUiSettingsScreen
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.ReactionsSettingsScreen
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.SearchEngineSettingsScreen
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.SecurityFiltersScreen
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.SettingsScreen
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.SpammingUsersScreen
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.UpdateZapAmountScreen
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.UserSettingsScreen
|
||||
@@ -245,6 +250,7 @@ import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.cordn.Cord
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.cordn.CordnHubScreen
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.cordn.CordnKeyPackagesScreen
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.cordn.CordnLinkScreen
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.nip46.Nip46ConnectedAppsScreen
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.nip46.Nip46SignerScreen
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.shorts.ShortsScreen
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.softwareapps.SoftwareAppDetailScreen
|
||||
@@ -328,19 +334,13 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.napplets.NappletsScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.nests.NestsScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.NotificationScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.podcasts.authoring.NewPodcastEpisodeScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.ProfileScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.payment.SendPaymentScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.qrcode.ScanQrImageScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.nip86.RelayManagementScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.scheduledposts.ScheduledPostsScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.NamecoinSettingsScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.NotificationSettingsScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.OtsSettingsScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.ResourceUsageScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.SettingsScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.cordn.CordnBackupScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.cordn.CordnMigrateScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.nip46.Nip46ConnectedAppsScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.video.hls.NewHlsVideoScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.webBookmarks.WebBookmarksScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.workouts.NewWorkoutScreen
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.note
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.core.net.toUri
|
||||
import kotlin.coroutines.cancellation.CancellationException
|
||||
|
||||
fun payViaIntent(
|
||||
invoice: String,
|
||||
context: Context,
|
||||
noWalletFound: String,
|
||||
onPaid: () -> Unit,
|
||||
onError: (String) -> Unit,
|
||||
) {
|
||||
try {
|
||||
val intent = Intent(Intent.ACTION_VIEW, "lightning:$invoice".toUri())
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
|
||||
context.startActivity(intent)
|
||||
onPaid()
|
||||
} catch (e: Exception) {
|
||||
if (e is CancellationException) throw e
|
||||
// don't display ugly error messages
|
||||
// if (e.message != null) {
|
||||
// onError(stringRes(Res.string.no_wallet_found_with_error, e.message!!))
|
||||
// } else {
|
||||
onError(noWalletFound)
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hands a reusable BOLT12 offer (`lno1…`, from a recipient's kind:10058) off to an
|
||||
* installed wallet. Unlike a BOLT11 invoice, a BOLT12 offer is NOT a `lightning:`
|
||||
* payload — that scheme is defined for `lnbc…` invoices. Offers travel as the `lno`
|
||||
* parameter of a BIP21/BIP321 bitcoin URI (`bitcoin:?lno=lno1…`), where the on-chain
|
||||
* address is optional so a Lightning-only offer stands on its own. The wallet resolves
|
||||
* the offer, collects the amount, and completes the payment; this is a plain intent,
|
||||
* not a NIP-57/NIP-B1 zap, so it produces no Nostr receipt.
|
||||
*/
|
||||
fun payViaBolt12Intent(
|
||||
offer: String,
|
||||
context: Context,
|
||||
noWalletFound: String,
|
||||
onPaid: () -> Unit,
|
||||
onError: (String) -> Unit,
|
||||
) {
|
||||
try {
|
||||
val intent = Intent(Intent.ACTION_VIEW, "bitcoin:?lno=$offer".toUri())
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
|
||||
context.startActivity(intent)
|
||||
onPaid()
|
||||
} catch (e: Exception) {
|
||||
if (e is CancellationException) throw e
|
||||
onError(noWalletFound)
|
||||
}
|
||||
}
|
||||
@@ -192,6 +192,7 @@ import com.vitorpamplona.amethyst.commons.ui.note.elements.ShareOptionsBottomShe
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.platform.MAX_VOICE_RECORD_SECONDS
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.types.EditState
|
||||
import com.vitorpamplona.amethyst.commons.ui.richtext.InLineIconRenderer
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.header.paymentTargetStyleFor
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.wallet.OnchainZapSendDialog
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.wallet.navigateToReloadMint
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
@@ -232,7 +233,6 @@ import com.vitorpamplona.amethyst.model.zap.RailCapabilityResolver
|
||||
import com.vitorpamplona.amethyst.service.payments.PayToAppAvailability
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.RecordAudioBox
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.PaymentTargetsDialog
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.paymentTargetStyleFor
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent
|
||||
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
|
||||
|
||||
@@ -20,8 +20,6 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.note
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
@@ -58,7 +56,6 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
@@ -68,7 +65,6 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import androidx.core.net.toUri
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
@@ -118,12 +114,14 @@ import com.vitorpamplona.amethyst.commons.ui.theme.Size55dp
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.StdHorzSpacer
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.ZeroPadding
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText
|
||||
import com.vitorpamplona.amethyst.commons.ui.wallet.payInvoice
|
||||
import com.vitorpamplona.amethyst.commons.ui.wallet.rememberWalletAppLauncher
|
||||
import com.vitorpamplona.amethyst.commons.util.showAmount
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
|
||||
import com.vitorpamplona.quartz.utils.BigDecimal
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.coroutines.CancellationException
|
||||
|
||||
class ZapOptionViewModel : ViewModel() {
|
||||
private var account: Account? = null
|
||||
@@ -266,7 +264,7 @@ fun ZapCustomDialog(
|
||||
onClick = {
|
||||
postViewModel.customAmount = TextFieldValue(amount.toString())
|
||||
},
|
||||
label = { Text("⚡ ${showAmount(amount.toBigDecimal())}") },
|
||||
label = { Text("⚡ ${showAmount(BigDecimal(amount))}") },
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -517,7 +515,7 @@ fun DisplayPayable(
|
||||
}
|
||||
Row {
|
||||
Text(
|
||||
text = showAmount((payable.amountMilliSats / 1000.0f).toBigDecimal()),
|
||||
text = showAmount(BigDecimal((payable.amountMilliSats / 1000.0f).toString())),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
fontWeight = FontWeight.Bold,
|
||||
@@ -535,10 +533,10 @@ fun DisplayPayable(
|
||||
}
|
||||
|
||||
Spacer(modifier = DoubleHorzSpacer)
|
||||
val context = LocalContext.current
|
||||
val walletLauncher = rememberWalletAppLauncher()
|
||||
|
||||
PayButton(isActive = !paid.value) {
|
||||
payViaIntent(payable.invoice, context, noWalletFoundStr, { paid.value = true }) {
|
||||
walletLauncher.payInvoice(payable.invoice, noWalletFoundStr, { paid.value = true }) {
|
||||
accountViewModel.toastManager.toast(
|
||||
Res.string.error_dialog_zap_error,
|
||||
UserBasedErrorMessage(it, payable.info.user),
|
||||
@@ -548,58 +546,6 @@ fun DisplayPayable(
|
||||
}
|
||||
}
|
||||
|
||||
fun payViaIntent(
|
||||
invoice: String,
|
||||
context: Context,
|
||||
noWalletFound: String,
|
||||
onPaid: () -> Unit,
|
||||
onError: (String) -> Unit,
|
||||
) {
|
||||
try {
|
||||
val intent = Intent(Intent.ACTION_VIEW, "lightning:$invoice".toUri())
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
|
||||
context.startActivity(intent)
|
||||
onPaid()
|
||||
} catch (e: Exception) {
|
||||
if (e is CancellationException) throw e
|
||||
// don't display ugly error messages
|
||||
// if (e.message != null) {
|
||||
// onError(stringRes(Res.string.no_wallet_found_with_error, e.message!!))
|
||||
// } else {
|
||||
onError(noWalletFound)
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hands a reusable BOLT12 offer (`lno1…`, from a recipient's kind:10058) off to an
|
||||
* installed wallet. Unlike a BOLT11 invoice, a BOLT12 offer is NOT a `lightning:`
|
||||
* payload — that scheme is defined for `lnbc…` invoices. Offers travel as the `lno`
|
||||
* parameter of a BIP21/BIP321 bitcoin URI (`bitcoin:?lno=lno1…`), where the on-chain
|
||||
* address is optional so a Lightning-only offer stands on its own. The wallet resolves
|
||||
* the offer, collects the amount, and completes the payment; this is a plain intent,
|
||||
* not a NIP-57/NIP-B1 zap, so it produces no Nostr receipt.
|
||||
*/
|
||||
fun payViaBolt12Intent(
|
||||
offer: String,
|
||||
context: Context,
|
||||
noWalletFound: String,
|
||||
onPaid: () -> Unit,
|
||||
onError: (String) -> Unit,
|
||||
) {
|
||||
try {
|
||||
val intent = Intent(Intent.ACTION_VIEW, "bitcoin:?lno=$offer".toUri())
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
|
||||
context.startActivity(intent)
|
||||
onPaid()
|
||||
} catch (e: Exception) {
|
||||
if (e is CancellationException) throw e
|
||||
onError(noWalletFound)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PayButton(
|
||||
isActive: Boolean,
|
||||
|
||||
+1
-1
@@ -57,6 +57,7 @@ import com.vitorpamplona.amethyst.commons.resources.clink_requesting_invoice
|
||||
import com.vitorpamplona.amethyst.commons.resources.error_dialog_pay_invoice_error
|
||||
import com.vitorpamplona.amethyst.commons.resources.pay
|
||||
import com.vitorpamplona.amethyst.commons.resources.sats
|
||||
import com.vitorpamplona.amethyst.commons.service.ClinkOfferPayer
|
||||
import com.vitorpamplona.amethyst.commons.ui.loadStringRes
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.routes.routeFor
|
||||
@@ -71,7 +72,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.Size25dp
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.SmallBorder
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.StdHorzSpacer
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.service.ClinkOfferPayer
|
||||
import com.vitorpamplona.amethyst.ui.components.PaymentCard
|
||||
import com.vitorpamplona.amethyst.ui.components.PaymentCardAmount
|
||||
import com.vitorpamplona.quartz.experimental.clink.common.SatRange
|
||||
|
||||
@@ -38,8 +38,10 @@ import com.vitorpamplona.amethyst.commons.ui.settings.SettingsCategory
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.favorites.FavoriteAppLauncher
|
||||
import com.vitorpamplona.amethyst.ui.navigation.topbars.AndroidAroundMeLocationLabel
|
||||
import com.vitorpamplona.amethyst.ui.note.DrawPlayName
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.nests.room.activity.NestActivity
|
||||
import com.vitorpamplona.quartz.concord.cord02Community.ImagePointer
|
||||
import kotlinx.collections.immutable.ImmutableMap
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.bottombars.AppBottomBar as AppBottomBarImpl
|
||||
import com.vitorpamplona.amethyst.favorites.rememberManifestIconModel as AppRememberManifestIconModel
|
||||
import com.vitorpamplona.amethyst.favorites.rememberNappletIconModel as AppRememberNappletIconModel
|
||||
@@ -60,6 +62,15 @@ object AndroidAppPlatform : AppPlatform {
|
||||
// Per flavour: Play links the hosted policies, F-Droid surfaces none.
|
||||
override fun legalSettingsCategory(uriHandler: UriHandler): SettingsCategory? = flavorLegalSettingsCategory(uriHandler)
|
||||
|
||||
@Composable
|
||||
override fun SpeakNameButton(name: String) = DrawPlayName(name)
|
||||
|
||||
@Composable
|
||||
override fun rememberAppLanguages(): ImmutableMap<String, String> {
|
||||
val context = LocalContext.current
|
||||
return remember(context) { context.getLangPreferenceDropdownEntries() }
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun AppBottomBar(
|
||||
selectedRoute: Route?,
|
||||
|
||||
@@ -23,18 +23,26 @@ package com.vitorpamplona.amethyst.ui.platform
|
||||
import com.vitorpamplona.amethyst.Amethyst
|
||||
import com.vitorpamplona.amethyst.commons.browser.BrowserHistoryRegistry
|
||||
import com.vitorpamplona.amethyst.commons.browser.BrowserIconRegistry
|
||||
import com.vitorpamplona.amethyst.commons.connectedApps.nip46.Nip46ClientStore
|
||||
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.model.preferences.NamecoinSettingsStore
|
||||
import com.vitorpamplona.amethyst.commons.model.preferences.OtsSettingsStore
|
||||
import com.vitorpamplona.amethyst.commons.napplet.permissions.NappletPermissionLedger
|
||||
import com.vitorpamplona.amethyst.commons.service.AppServices
|
||||
import com.vitorpamplona.amethyst.commons.service.BlossomServerFinder
|
||||
import com.vitorpamplona.amethyst.commons.service.ai.AltTextSuggester
|
||||
import com.vitorpamplona.amethyst.commons.service.namecoin.NamecoinClients
|
||||
import com.vitorpamplona.amethyst.commons.tor.TorSettingsFlow
|
||||
import com.vitorpamplona.amethyst.service.ai.MLKitImageLabelService
|
||||
import com.vitorpamplona.amethyst.service.location.CachedReversedGeoLocations
|
||||
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.ElectrumxServer
|
||||
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.NamecoinCoreRpcConfig
|
||||
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.NamecoinNameResolver
|
||||
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.RpcProbeResult
|
||||
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.ServerTestResult
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
@@ -64,6 +72,14 @@ object AndroidAppServices : AppServices {
|
||||
|
||||
override val namecoinResolver: NamecoinNameResolver get() = Amethyst.instance.namecoinResolver
|
||||
|
||||
override val namecoinSettings: NamecoinSettingsStore get() = Amethyst.instance.namecoinPrefs
|
||||
|
||||
override val namecoinClients: NamecoinClients = AndroidNamecoinClients
|
||||
|
||||
override val otsSettings: OtsSettingsStore get() = Amethyst.instance.otsPrefs
|
||||
|
||||
override val nip46ClientStore: Nip46ClientStore get() = Amethyst.instance.nip46ClientStore
|
||||
|
||||
override val blossomServerFinder: BlossomServerFinder = AndroidBlossomServerFinder
|
||||
|
||||
override val deviceLocation: DeviceLocation get() = Amethyst.instance.locationManager
|
||||
@@ -85,3 +101,19 @@ private object AndroidBlossomServerFinder : BlossomServerFinder {
|
||||
.findServers(blossomUri)
|
||||
?.serverUrl
|
||||
}
|
||||
|
||||
/** The app's ElectrumX and Namecoin Core clients, built lazily by [Amethyst.instance]. */
|
||||
private object AndroidNamecoinClients : NamecoinClients {
|
||||
override suspend fun testElectrumxServer(server: ElectrumxServer): ServerTestResult = Amethyst.instance.electrumXClient.testServer(server)
|
||||
|
||||
override suspend fun probeCoreRpc(config: NamecoinCoreRpcConfig): RpcProbeResult = Amethyst.instance.namecoinCoreRpcClient.probe(config)
|
||||
|
||||
override fun addPinnedCert(pem: String) {
|
||||
Amethyst.instance.electrumXClient.addPinnedCert(pem)
|
||||
Amethyst.instance.namecoinCoreRpcClient.addPinnedCert(pem)
|
||||
}
|
||||
|
||||
override fun setCoreRpcConfig(config: NamecoinCoreRpcConfig) {
|
||||
Amethyst.instance.namecoinCoreRpcClient.setConfig(config)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.platform
|
||||
|
||||
import android.content.Context
|
||||
import androidx.core.os.LocaleListCompat
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import kotlinx.collections.immutable.ImmutableMap
|
||||
import kotlinx.collections.immutable.toImmutableMap
|
||||
import org.xmlpull.v1.XmlPullParser
|
||||
import org.xmlpull.v1.XmlPullParserException
|
||||
import java.io.IOException
|
||||
|
||||
private fun Context.getLocaleListFromXml(): LocaleListCompat {
|
||||
val tagsList = mutableListOf<CharSequence>()
|
||||
try {
|
||||
val xpp: XmlPullParser = resources.getXml(R.xml.locales_config)
|
||||
while (xpp.eventType != XmlPullParser.END_DOCUMENT) {
|
||||
if (xpp.eventType == XmlPullParser.START_TAG && xpp.name == "locale") {
|
||||
tagsList.add(xpp.getAttributeValue(0))
|
||||
}
|
||||
xpp.next()
|
||||
}
|
||||
} catch (e: XmlPullParserException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
return LocaleListCompat.forLanguageTags(tagsList.joinToString(","))
|
||||
}
|
||||
|
||||
fun Context.getLangPreferenceDropdownEntries(): ImmutableMap<String, String> {
|
||||
val localeList = getLocaleListFromXml()
|
||||
val map = mutableMapOf<String, String>()
|
||||
|
||||
for (a in 0 until localeList.size()) {
|
||||
localeList[a].let {
|
||||
map.put(
|
||||
it!!.getDisplayName(it).replaceFirstChar { char -> char.uppercase() },
|
||||
it.toLanguageTag(),
|
||||
)
|
||||
}
|
||||
}
|
||||
return map.toImmutableMap()
|
||||
}
|
||||
+6
-6
@@ -126,6 +126,12 @@ import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.TopBarWithBackButton
|
||||
import com.vitorpamplona.amethyst.commons.ui.platform.rememberManifestIconModel
|
||||
import com.vitorpamplona.amethyst.commons.ui.platform.rememberWebAppIconModel
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.nip46.Nip46AppIcon
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.nip46.Nip46LiveStatus
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.nip46.Nip46ReconnectPill
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.nip46.Nip46StatusDot
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.nip46.nip46AppOnline
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.nip46.nip46ClientSubtitle
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.napplet.NappletBrokerService
|
||||
@@ -134,12 +140,6 @@ import com.vitorpamplona.amethyst.napplet.counterpartyLabel
|
||||
import com.vitorpamplona.amethyst.napplet.descriptionRes
|
||||
import com.vitorpamplona.amethyst.napplet.labelRes
|
||||
import com.vitorpamplona.amethyst.napplet.resolveNappletMeta
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.nip46.Nip46AppIcon
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.nip46.Nip46LiveStatus
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.nip46.Nip46ReconnectPill
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.nip46.Nip46StatusDot
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.nip46.nip46AppOnline
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.nip46.nip46ClientSubtitle
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer
|
||||
import com.vitorpamplona.quartz.nip42RelayAuth.RelayAuthEvent
|
||||
|
||||
+2
@@ -66,6 +66,8 @@ import com.vitorpamplona.amethyst.commons.ui.components.M3ActionSection
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.util.setText
|
||||
import com.vitorpamplona.amethyst.commons.ui.loadStringRes
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.ErrorMessageDialog
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.header.PaymentTargetPill
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.header.paymentTargetUri
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.qrcode.QrCodeDrawer
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.Size20Modifier
|
||||
|
||||
+1
-1
@@ -29,8 +29,8 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.RefresheableFeedView
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.pinnedNotes.dal.UserProfilePinnedNotesFeedViewModel
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.pinnedNotes.dal.UserProfilePinnedNotesFeedViewModel
|
||||
|
||||
@Composable
|
||||
fun TabPinnedNotes(
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
<vector android:height="128dp" android:viewportHeight="512"
|
||||
android:viewportWidth="512" android:width="128dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#3A6CAC" android:fillType="evenOdd" android:pathData="M296.13,354.17c49.88,-5.89 102.94,-24.03 102.94,-110.19c0,-24.49 -8.62,-44.45 -22.67,-59.87c2.27,-5.89 9.52,-28.11 -2.73,-58.95c0,0 -18.14,-5.9 -60.76,22.67c-18.14,-4.98 -38.09,-8.16 -56.68,-8.16c-19.05,0 -39.01,3.18 -56.7,8.16c-43.08,-28.57 -61.22,-22.67 -61.22,-22.67c-12.24,30.83 -4.98,53.06 -2.72,58.95c-14.06,15.42 -22.68,35.38 -22.68,59.87c0,86.16 53.06,104.3 102.94,110.19c-6.34,5.45 -12.24,15.87 -14.51,30.39c-12.7,5.44 -45.81,15.87 -65.76,-18.59c0,0 -11.8,-21.31 -34.01,-22.67c0,0 -22.22,-0.45 -1.81,13.59c0,0 14.96,6.81 24.94,32.65c0,0 13.6,43.09 76.18,29.48v38.54c0,5.91 -4.53,12.7 -15.86,10.89C96.14,438.98 32.2,354.63 32.2,255.77c0,-123.81 100.22,-224.02 224.03,-224.02c123.35,0 224.02,100.22 223.57,224.02c0,98.86 -63.95,182.75 -152.83,212.69c-11.34,2.27 -15.87,-4.53 -15.87,-10.89V395.45C311.1,374.58 304.29,360.98 296.13,354.17L296.13,354.17zM512,256.23C512,114.73 397.26,0 256.23,0C114.73,0 0,114.73 0,256.23C0,397.26 114.73,512 256.23,512C397.26,512 512,397.26 512,256.23L512,256.23z"/>
|
||||
</vector>
|
||||
@@ -477,7 +477,16 @@ assemblers, `EventSync`, …). Packages are renamed on the way:
|
||||
voice reply, the calendar collections and event detail, the geocache detail and editor,
|
||||
the live-activity channel, chess, Cordn group creation, the QR share and the compose
|
||||
settings. 204 of 252 screens shared.
|
||||
- **Next:** 48 screens remain, and what blocks them is the platform itself:
|
||||
- **Wave 11 (2026-10-04):** Profile and settings. `AppServices` gained `namecoinSettings`,
|
||||
`namecoinClients` (a port over the JVM-only ElectrumX and Core RPC clients), `otsSettings`
|
||||
and `nip46ClientStore`. `AppPlatform` gained `rememberAppLanguages` (Android reads
|
||||
`locales_config.xml`) and `SpeakNameButton` (text-to-speech). `OtsSettings` and its store are
|
||||
in commonMain, on quartz's new `BitcoinExplorerUrls`; `RpcProbeResult` is too.
|
||||
`deviceDescription()` is an expect in commons. Copy-to-clipboard goes through the shared
|
||||
`Clipboard.setText`; the brand icons for identity claims are Compose resources. Moved: the
|
||||
profile (60 files: header, tabs, payment sheet), the app, NIP-46, Namecoin and OTS settings.
|
||||
210 of 252 screens shared.
|
||||
- **Next:** 42 screens remain, and what blocks them is the platform itself:
|
||||
- `ReactionsRow` (2.7k lines: pay-to-app, voice replies, wallet intents).
|
||||
- The Nests activity and PiP.
|
||||
- The call screens.
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.commons.util
|
||||
|
||||
import android.os.Build
|
||||
|
||||
actual fun deviceDescription(): String = "${Build.MANUFACTURER} ${Build.MODEL}, Android ${Build.VERSION.RELEASE} (API ${Build.VERSION.SDK_INT})"
|
||||
+3
-3
@@ -21,7 +21,7 @@
|
||||
package com.vitorpamplona.amethyst.commons.model.nip03Timestamp
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.vitorpamplona.quartz.nip03Timestamp.okhttp.OkHttpBitcoinExplorer
|
||||
import com.vitorpamplona.quartz.nip03Timestamp.BitcoinExplorerUrls
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
@@ -57,8 +57,8 @@ data class OtsSettings(
|
||||
|
||||
val KNOWN_EXPLORERS =
|
||||
listOf(
|
||||
OkHttpBitcoinExplorer.MEMPOOL_API_URL to "mempool.space (Tor-friendly)",
|
||||
OkHttpBitcoinExplorer.BLOCKSTREAM_API_URL to "blockstream.info",
|
||||
BitcoinExplorerUrls.MEMPOOL to "mempool.space (Tor-friendly)",
|
||||
BitcoinExplorerUrls.BLOCKSTREAM to "blockstream.info",
|
||||
)
|
||||
|
||||
fun isValidUrl(url: String): Boolean {
|
||||
+16
@@ -22,12 +22,16 @@ package com.vitorpamplona.amethyst.commons.service
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.browser.BrowserHistoryRegistry
|
||||
import com.vitorpamplona.amethyst.commons.browser.BrowserIconRegistry
|
||||
import com.vitorpamplona.amethyst.commons.connectedApps.nip46.Nip46ClientStore
|
||||
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.model.preferences.NamecoinSettingsStore
|
||||
import com.vitorpamplona.amethyst.commons.model.preferences.OtsSettingsStore
|
||||
import com.vitorpamplona.amethyst.commons.napplet.permissions.NappletPermissionLedger
|
||||
import com.vitorpamplona.amethyst.commons.service.ai.AltTextSuggester
|
||||
import com.vitorpamplona.amethyst.commons.service.namecoin.NamecoinClients
|
||||
import com.vitorpamplona.amethyst.commons.tor.TorSettingsFlow
|
||||
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.NamecoinNameResolver
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
@@ -64,6 +68,18 @@ interface AppServices {
|
||||
/** Resolves `.bit` names and `d/`/`id/` identifiers over the configured ElectrumX servers. */
|
||||
val namecoinResolver: NamecoinNameResolver
|
||||
|
||||
/** The Namecoin backend settings (servers, Core RPC, pinned certificates). */
|
||||
val namecoinSettings: NamecoinSettingsStore
|
||||
|
||||
/** The live Namecoin clients the settings screen tests and reconfigures. */
|
||||
val namecoinClients: NamecoinClients get() = NamecoinClients.None
|
||||
|
||||
/** The OpenTimestamps blockchain explorer setting. */
|
||||
val otsSettings: OtsSettingsStore
|
||||
|
||||
/** The NIP-46 remote-signer clients this device has connected to. */
|
||||
val nip46ClientStore: Nip46ClientStore
|
||||
|
||||
/** Finds the server behind a `blossom:` URI (BUD-10). */
|
||||
val blossomServerFinder: BlossomServerFinder get() = BlossomServerFinder.None
|
||||
|
||||
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.commons.service.namecoin
|
||||
|
||||
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.ElectrumxServer
|
||||
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.NamecoinCoreRpcConfig
|
||||
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.RpcProbeResult
|
||||
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.ServerTestResult
|
||||
|
||||
/**
|
||||
* What the Namecoin settings screen asks of the live clients: test a server, probe a Namecoin
|
||||
* Core node, and hand them a pinned certificate or a new Core RPC config. The ElectrumX and Core
|
||||
* RPC clients themselves are JVM-only (raw TLS sockets, OkHttp), so the screen reaches them here.
|
||||
*/
|
||||
interface NamecoinClients {
|
||||
suspend fun testElectrumxServer(server: ElectrumxServer): ServerTestResult
|
||||
|
||||
suspend fun probeCoreRpc(config: NamecoinCoreRpcConfig): RpcProbeResult
|
||||
|
||||
/** Pins a self-signed certificate on both backends, so the user confirms it once. */
|
||||
fun addPinnedCert(pem: String)
|
||||
|
||||
fun setCoreRpcConfig(config: NamecoinCoreRpcConfig)
|
||||
|
||||
/** No clients: every test reports that nothing is available. */
|
||||
object None : NamecoinClients {
|
||||
override suspend fun testElectrumxServer(server: ElectrumxServer): ServerTestResult = ServerTestResult(server = server, success = false, responseTimeMs = 0, error = "Namecoin is not available on this platform")
|
||||
|
||||
override suspend fun probeCoreRpc(config: NamecoinCoreRpcConfig): RpcProbeResult = RpcProbeResult(success = false, elapsedMs = 0, error = "Namecoin is not available on this platform")
|
||||
|
||||
override fun addPinnedCert(pem: String) {}
|
||||
|
||||
override fun setCoreRpcConfig(config: NamecoinCoreRpcConfig) {}
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.commons.util
|
||||
|
||||
/** A one-line description of this device and OS, for diagnostic screens: "Google Pixel 8, Android 15 (API 35)". */
|
||||
expect fun deviceDescription(): String
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.commons.util
|
||||
|
||||
import platform.UIKit.UIDevice
|
||||
|
||||
actual fun deviceDescription(): String {
|
||||
val device = UIDevice.currentDevice
|
||||
return "${device.model}, ${device.systemName} ${device.systemVersion}"
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.commons.util
|
||||
|
||||
actual fun deviceDescription(): String = "${System.getProperty("os.name")} ${System.getProperty("os.version")} (${System.getProperty("os.arch")}), Java ${System.getProperty("java.version")}"
|
||||
+2
-1
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.service
|
||||
package com.vitorpamplona.amethyst.commons.service
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.Account
|
||||
import com.vitorpamplona.amethyst.commons.tor.MoneyOpRelayRouting
|
||||
@@ -36,6 +36,7 @@ import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerInternal
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.IO
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
|
||||
+6
-7
@@ -18,9 +18,8 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.components.util
|
||||
package com.vitorpamplona.amethyst.commons.ui.components.util
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
@@ -30,12 +29,12 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalClipboard
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.copied_to_clipboard
|
||||
import com.vitorpamplona.amethyst.commons.resources.copy_to_clipboard
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.rememberShortNotice
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.util.setText
|
||||
import com.vitorpamplona.amethyst.commons.ui.loadStringRes
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
@@ -44,7 +43,7 @@ import org.jetbrains.compose.resources.StringResource
|
||||
|
||||
/**
|
||||
* Text composable that opens [onClick] on a tap and copies [copyValue] to the
|
||||
* system clipboard on a long-press (with a Toast confirmation).
|
||||
* system clipboard on a long-press (with a short notice confirming it).
|
||||
*
|
||||
* This is the long-press-to-copy counterpart of [com.vitorpamplona.amethyst.commons.ui.components.ClickableTextPrimary].
|
||||
* It deliberately uses a plain [Text] + [combinedClickable] outer modifier
|
||||
@@ -58,7 +57,7 @@ import org.jetbrains.compose.resources.StringResource
|
||||
* @param copyValue raw value placed on the clipboard on long-press
|
||||
* (typically the full, unmodified profile field).
|
||||
* @param onClick tap action — usually "open the link" or "expand zap UI".
|
||||
* @param toastResId string resource shown via [Toast.LENGTH_SHORT] after the
|
||||
* @param toastResId string resource shown as a short notice after the
|
||||
* value is placed on the clipboard. Defaults to a generic
|
||||
* "Copied to clipboard" message.
|
||||
* @param onLongClickLabelResId accessibility label exposed to TalkBack for
|
||||
@@ -78,7 +77,7 @@ fun LongPressCopyText(
|
||||
toastResId: StringResource = Res.string.copied_to_clipboard,
|
||||
onLongClickLabelResId: StringResource = Res.string.copy_to_clipboard,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val shortNotice = rememberShortNotice()
|
||||
val clipboard = LocalClipboard.current
|
||||
val scope = rememberCoroutineScope()
|
||||
val longClickLabel = stringRes(onLongClickLabelResId)
|
||||
@@ -96,7 +95,7 @@ fun LongPressCopyText(
|
||||
onLongClick = {
|
||||
scope.launch {
|
||||
clipboard.setText(copyValue)
|
||||
Toast.makeText(context, loadStringRes(toastResId), Toast.LENGTH_SHORT).show()
|
||||
shortNotice.show(loadStringRes(toastResId))
|
||||
}
|
||||
},
|
||||
onLongClickLabel = longClickLabel,
|
||||
+16
@@ -32,6 +32,8 @@ import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.qrcode.ScanOutcome
|
||||
import com.vitorpamplona.amethyst.commons.ui.settings.SettingsCategory
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.quartz.concord.cord02Community.ImagePointer
|
||||
import kotlinx.collections.immutable.ImmutableMap
|
||||
import kotlinx.collections.immutable.persistentMapOf
|
||||
|
||||
/**
|
||||
* The app shell's pieces that shared screens embed but only the front end can draw yet: the
|
||||
@@ -47,6 +49,13 @@ interface AppPlatform {
|
||||
/** The settings screen's legal links, when the build's distribution calls for them. */
|
||||
fun legalSettingsCategory(uriHandler: UriHandler): SettingsCategory? = null
|
||||
|
||||
/**
|
||||
* The languages the app is translated into, as display name ("Deutsch") to language tag
|
||||
* ("de"), for the language picker. Empty where the platform follows the system language only.
|
||||
*/
|
||||
@Composable
|
||||
fun rememberAppLanguages(): ImmutableMap<String, String> = persistentMapOf()
|
||||
|
||||
@Composable
|
||||
fun AppBottomBar(
|
||||
selectedRoute: Route?,
|
||||
@@ -58,6 +67,10 @@ interface AppPlatform {
|
||||
@Composable
|
||||
fun AroundMeLocationLabel() {}
|
||||
|
||||
/** A button that reads [name] aloud with the platform's text-to-speech; nothing where there is none. */
|
||||
@Composable
|
||||
fun SpeakNameButton(name: String) {}
|
||||
|
||||
/** A full-screen dialog to pick a place (map, search, "use my location"), as a geohash. */
|
||||
@Composable
|
||||
fun GeohashLocationPickerDialog(
|
||||
@@ -135,6 +148,9 @@ fun AppBottomBar(
|
||||
@Composable
|
||||
fun AroundMeLocationLabel() = LocalAppPlatform.current.AroundMeLocationLabel()
|
||||
|
||||
@Composable
|
||||
fun SpeakNameButton(name: String) = LocalAppPlatform.current.SpeakNameButton(name)
|
||||
|
||||
@Composable
|
||||
fun GeohashLocationPickerDialog(
|
||||
initialGeohash: String?,
|
||||
|
||||
+47
-47
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile
|
||||
|
||||
import androidx.compose.foundation.ScrollState
|
||||
import androidx.compose.foundation.gestures.scrollBy
|
||||
@@ -63,7 +63,6 @@ import androidx.compose.ui.layout.onSizeChanged
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.model.cache.LocalCache
|
||||
import com.vitorpamplona.amethyst.commons.model.navigation.Route
|
||||
@@ -76,47 +75,48 @@ import com.vitorpamplona.amethyst.commons.resources.mutual
|
||||
import com.vitorpamplona.amethyst.commons.resources.notes
|
||||
import com.vitorpamplona.amethyst.commons.resources.profile_tab_apps
|
||||
import com.vitorpamplona.amethyst.commons.resources.replies
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.rememberViewModel
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.UserBlockedFeed
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.WatchLifecycleAndUpdateModel
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.platform.AppBottomBar
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.apps.TabApps
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.apps.dal.UserProfileAppsFeedViewModel
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.bookmarks.BookmarkTabHeader
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.bookmarks.TabBookmarks
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.bookmarks.dal.UserProfileBookmarksFeedViewModel
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.conversations.TabNotesConversations
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.conversations.dal.UserProfileConversationsFeedViewModel
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.followers.FollowersTabHeader
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.followers.TabFollowers
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.followers.dal.UserProfileFollowersUserFeedViewModel
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.follows.FollowTabHeader
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.follows.TabFollows
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.follows.dal.UserProfileFollowsUserFeedViewModel
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.gallery.TabGallery
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.gallery.dal.UserProfileGalleryFeedViewModel
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.hashtags.FollowedTagsTabHeader
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.hashtags.TabFollowedTags
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.header.ProfileHeader
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.header.ProfileTopBar
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.header.apps.UserAppRecommendationsFeedViewModel
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.mutual.TabMutualConversations
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.mutual.dal.UserProfileMutualFeedViewModel
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.newthreads.TabNotesNewThreads
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.newthreads.dal.UserProfileNewThreadsFeedViewModel
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.pinnedNotes.dal.UserProfilePinnedNotesFeedViewModel
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.relays.TabRelays
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.reports.ReportsTabHeader
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.reports.TabReports
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.reports.dal.UserProfileReportFeedViewModel
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.zaps.TabReceivedZaps
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.zaps.ZapTabHeader
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.zaps.dal.UserProfileZapsViewModel
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.DividerThickness
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.Size8dp
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.UserExternalIdentitiesViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.apps.TabApps
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.apps.dal.UserProfileAppsFeedViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.bookmarks.BookmarkTabHeader
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.bookmarks.TabBookmarks
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.bookmarks.dal.UserProfileBookmarksFeedViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.conversations.TabNotesConversations
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.conversations.dal.UserProfileConversationsFeedViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.followers.FollowersTabHeader
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.followers.TabFollowers
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.followers.dal.UserProfileFollowersUserFeedViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.follows.FollowTabHeader
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.follows.TabFollows
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.follows.dal.UserProfileFollowsUserFeedViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.gallery.TabGallery
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.gallery.dal.UserProfileGalleryFeedViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.hashtags.FollowedTagsTabHeader
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.hashtags.TabFollowedTags
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.ProfileHeader
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.ProfileTopBar
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.apps.UserAppRecommendationsFeedViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.mutual.TabMutualConversations
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.mutual.dal.UserProfileMutualFeedViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.newthreads.TabNotesNewThreads
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.newthreads.dal.UserProfileNewThreadsFeedViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.pinnedNotes.dal.UserProfilePinnedNotesFeedViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.relays.TabRelays
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.reports.ReportsTabHeader
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.reports.TabReports
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.reports.dal.UserProfileReportFeedViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.zaps.TabReceivedZaps
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.zaps.ZapTabHeader
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.zaps.dal.UserProfileZapsViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
@@ -154,7 +154,7 @@ fun PrepareViewModels(
|
||||
nav: INav,
|
||||
) {
|
||||
val followsFeedViewModel: UserProfileFollowsUserFeedViewModel =
|
||||
viewModel(
|
||||
rememberViewModel(
|
||||
key = baseUser.pubkeyHex + "UserProfileFollowsUserFeedViewModel",
|
||||
factory =
|
||||
UserProfileFollowsUserFeedViewModel.Factory(
|
||||
@@ -164,7 +164,7 @@ fun PrepareViewModels(
|
||||
)
|
||||
|
||||
val galleryFeedViewModel: UserProfileGalleryFeedViewModel =
|
||||
viewModel(
|
||||
rememberViewModel(
|
||||
key = baseUser.pubkeyHex + "UserGalleryFeedViewModel",
|
||||
factory =
|
||||
UserProfileGalleryFeedViewModel.Factory(
|
||||
@@ -174,7 +174,7 @@ fun PrepareViewModels(
|
||||
)
|
||||
|
||||
val followersFeedViewModel: UserProfileFollowersUserFeedViewModel =
|
||||
viewModel(
|
||||
rememberViewModel(
|
||||
key = baseUser.pubkeyHex + "UserProfileFollowersUserFeedViewModel",
|
||||
factory =
|
||||
UserProfileFollowersUserFeedViewModel.Factory(
|
||||
@@ -184,26 +184,26 @@ fun PrepareViewModels(
|
||||
)
|
||||
|
||||
val appRecommendations: UserAppRecommendationsFeedViewModel =
|
||||
viewModel(
|
||||
rememberViewModel(
|
||||
key = baseUser.pubkeyHex + "UserAppRecommendationsFeedViewModel",
|
||||
factory = UserAppRecommendationsFeedViewModel.Factory(baseUser),
|
||||
)
|
||||
|
||||
val externalIdentities: UserExternalIdentitiesViewModel =
|
||||
viewModel(
|
||||
rememberViewModel(
|
||||
key = baseUser.pubkeyHex + "UserExternalIdentitiesViewModel",
|
||||
factory = UserExternalIdentitiesViewModel.Factory(baseUser),
|
||||
)
|
||||
|
||||
val zapFeedViewModel: UserProfileZapsViewModel =
|
||||
viewModel(
|
||||
rememberViewModel(
|
||||
key = baseUser.pubkeyHex + "UserProfileZapsFeedViewModel",
|
||||
factory =
|
||||
UserProfileZapsViewModel.Factory(baseUser, accountViewModel.account),
|
||||
)
|
||||
|
||||
val threadsViewModel: UserProfileNewThreadsFeedViewModel =
|
||||
viewModel(
|
||||
rememberViewModel(
|
||||
key = baseUser.pubkeyHex + "UserProfileNewThreadsFeedViewModel",
|
||||
factory =
|
||||
UserProfileNewThreadsFeedViewModel.Factory(
|
||||
@@ -213,7 +213,7 @@ fun PrepareViewModels(
|
||||
)
|
||||
|
||||
val repliesViewModel: UserProfileConversationsFeedViewModel =
|
||||
viewModel(
|
||||
rememberViewModel(
|
||||
key = baseUser.pubkeyHex + "UserProfileConversationsFeedViewModel",
|
||||
factory =
|
||||
UserProfileConversationsFeedViewModel.Factory(
|
||||
@@ -223,7 +223,7 @@ fun PrepareViewModels(
|
||||
)
|
||||
|
||||
val mutualViewModel: UserProfileMutualFeedViewModel =
|
||||
viewModel(
|
||||
rememberViewModel(
|
||||
key = baseUser.pubkeyHex + "UserProfileMutualFeedViewModel",
|
||||
factory =
|
||||
UserProfileMutualFeedViewModel.Factory(
|
||||
@@ -233,7 +233,7 @@ fun PrepareViewModels(
|
||||
)
|
||||
|
||||
val bookmarksFeedViewModel: UserProfileBookmarksFeedViewModel =
|
||||
viewModel(
|
||||
rememberViewModel(
|
||||
key = baseUser.pubkeyHex + "UserProfileBookmarksFeedViewModel",
|
||||
factory =
|
||||
UserProfileBookmarksFeedViewModel.Factory(
|
||||
@@ -243,7 +243,7 @@ fun PrepareViewModels(
|
||||
)
|
||||
|
||||
val pinnedNotesFeedViewModel: UserProfilePinnedNotesFeedViewModel =
|
||||
viewModel(
|
||||
rememberViewModel(
|
||||
key = baseUser.pubkeyHex + "UserProfilePinnedNotesFeedViewModel",
|
||||
factory =
|
||||
UserProfilePinnedNotesFeedViewModel.Factory(
|
||||
@@ -253,7 +253,7 @@ fun PrepareViewModels(
|
||||
)
|
||||
|
||||
val reportsFeedViewModel: UserProfileReportFeedViewModel =
|
||||
viewModel(
|
||||
rememberViewModel(
|
||||
key = baseUser.pubkeyHex + "UserProfileReportFeedViewModel",
|
||||
factory =
|
||||
UserProfileReportFeedViewModel.Factory(
|
||||
@@ -263,7 +263,7 @@ fun PrepareViewModels(
|
||||
)
|
||||
|
||||
val appsFeedViewModel: UserProfileAppsFeedViewModel =
|
||||
viewModel(
|
||||
rememberViewModel(
|
||||
key = baseUser.pubkeyHex + "UserProfileAppsFeedViewModel",
|
||||
factory =
|
||||
UserProfileAppsFeedViewModel.Factory(
|
||||
+2
-2
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.apps
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.apps
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
@@ -29,8 +29,8 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.RefresheableFeedView
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.apps.dal.UserProfileAppsFeedViewModel
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.apps.dal.UserProfileAppsFeedViewModel
|
||||
|
||||
/**
|
||||
* Profile tab listing the user's published nsites and napplets. Each row is the inert feed card;
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.apps.dal
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.apps.dal
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter
|
||||
import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams
|
||||
+7
-2
@@ -18,14 +18,16 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.apps.dal
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.apps.dal
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
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.User
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.AndroidFeedViewModel
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@Stable
|
||||
class UserProfileAppsFeedViewModel(
|
||||
@@ -37,6 +39,9 @@ class UserProfileAppsFeedViewModel(
|
||||
val account: Account,
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T = UserProfileAppsFeedViewModel(user, account) as T
|
||||
override fun <T : ViewModel> create(
|
||||
modelClass: KClass<T>,
|
||||
extras: CreationExtras,
|
||||
): T = UserProfileAppsFeedViewModel(user, account) as T
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.bookmarks
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.bookmarks
|
||||
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
+2
-2
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.bookmarks
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.bookmarks
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
@@ -29,8 +29,8 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.RefresheableFeedView
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.bookmarks.dal.UserProfileBookmarksFeedViewModel
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.bookmarks.dal.UserProfileBookmarksFeedViewModel
|
||||
|
||||
@Composable
|
||||
fun TabBookmarks(
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.bookmarks.dal
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.bookmarks.dal
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.feeds.FeedFilter
|
||||
import com.vitorpamplona.amethyst.commons.model.Account
|
||||
+7
-2
@@ -18,14 +18,16 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.bookmarks.dal
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.bookmarks.dal
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
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.User
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.AndroidFeedViewModel
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@Stable
|
||||
class UserProfileBookmarksFeedViewModel(
|
||||
@@ -37,6 +39,9 @@ class UserProfileBookmarksFeedViewModel(
|
||||
val account: Account,
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T = UserProfileBookmarksFeedViewModel(user, account) as T
|
||||
override fun <T : ViewModel> create(
|
||||
modelClass: KClass<T>,
|
||||
extras: CreationExtras,
|
||||
): T = UserProfileBookmarksFeedViewModel(user, account) as T
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.conversations
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.conversations
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
@@ -26,8 +26,8 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.RefresheableFeedView
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.conversations.dal.UserProfileConversationsFeedViewModel
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.conversations.dal.UserProfileConversationsFeedViewModel
|
||||
|
||||
@Composable
|
||||
fun TabNotesConversations(
|
||||
+1
-1
@@ -20,7 +20,7 @@
|
||||
*/
|
||||
@file:Suppress("DEPRECATION")
|
||||
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.conversations.dal
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.conversations.dal
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter
|
||||
import com.vitorpamplona.amethyst.commons.feeds.sortedByDefaultFeedOrder
|
||||
+7
-2
@@ -18,14 +18,16 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.conversations.dal
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.conversations.dal
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
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.User
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.AndroidFeedViewModel
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@Stable
|
||||
class UserProfileConversationsFeedViewModel(
|
||||
@@ -37,6 +39,9 @@ class UserProfileConversationsFeedViewModel(
|
||||
val account: Account,
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T = UserProfileConversationsFeedViewModel(user, account) as T
|
||||
override fun <T : ViewModel> create(
|
||||
modelClass: KClass<T>,
|
||||
extras: CreationExtras,
|
||||
): T = UserProfileConversationsFeedViewModel(user, account) as T
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.followers
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.followers
|
||||
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -28,9 +28,9 @@ import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.user.observeUserAssertionsFollowerCount
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.number_followers
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.followers.dal.UserProfileFollowersUserFeedViewModel
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.followers.dal.UserProfileFollowersUserFeedViewModel
|
||||
|
||||
@Composable
|
||||
fun FollowersTabHeader(
|
||||
+2
-2
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.followers
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.followers
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
@@ -33,10 +33,10 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.UserCompose
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.followers.dal.UserProfileFollowersUserFeedViewModel
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.DividerThickness
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.FeedPadding
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.followers.dal.UserProfileFollowersUserFeedViewModel
|
||||
|
||||
@Composable
|
||||
fun TabFollowers(
|
||||
+8
-2
@@ -18,24 +18,27 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.followers.dal
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.followers.dal
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.viewmodel.CreationExtras
|
||||
import com.vitorpamplona.amethyst.commons.model.Account
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.IO
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.sample
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@Stable
|
||||
class UserProfileFollowersUserFeedViewModel(
|
||||
@@ -92,6 +95,9 @@ class UserProfileFollowersUserFeedViewModel(
|
||||
val account: Account,
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T = UserProfileFollowersUserFeedViewModel(user, account) as T
|
||||
override fun <T : ViewModel> create(
|
||||
modelClass: KClass<T>,
|
||||
extras: CreationExtras,
|
||||
): T = UserProfileFollowersUserFeedViewModel(user, account) as T
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.follows
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.follows
|
||||
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -26,9 +26,9 @@ import androidx.compose.runtime.getValue
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.number_following
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.follows.dal.UserProfileFollowsUserFeedViewModel
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.follows.dal.UserProfileFollowsUserFeedViewModel
|
||||
|
||||
@Composable
|
||||
fun FollowTabHeader(
|
||||
+2
-2
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.follows
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.follows
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
@@ -33,10 +33,10 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.UserCompose
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.follows.dal.UserProfileFollowsUserFeedViewModel
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.DividerThickness
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.FeedPadding
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.follows.dal.UserProfileFollowsUserFeedViewModel
|
||||
|
||||
@Composable
|
||||
fun TabFollows(
|
||||
+8
-2
@@ -18,17 +18,19 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.follows.dal
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.follows.dal
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.viewmodel.CreationExtras
|
||||
import com.vitorpamplona.amethyst.commons.model.Account
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.model.cache.LocalCache
|
||||
import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.IO
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
@@ -36,6 +38,7 @@ import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.flow.sample
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@Stable
|
||||
class UserProfileFollowsUserFeedViewModel(
|
||||
@@ -89,6 +92,9 @@ class UserProfileFollowsUserFeedViewModel(
|
||||
val account: Account,
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T = UserProfileFollowsUserFeedViewModel(user, account) as T
|
||||
override fun <T : ViewModel> create(
|
||||
modelClass: KClass<T>,
|
||||
extras: CreationExtras,
|
||||
): T = UserProfileFollowsUserFeedViewModel(user, account) as T
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.gallery
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.gallery
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.gallery
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.gallery
|
||||
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.layout.Arrangement.spacedBy
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.gallery
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.gallery
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
+2
-2
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.gallery
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.gallery
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
@@ -28,8 +28,8 @@ import androidx.compose.ui.Modifier
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.ScrollStateKeys
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.SaveableGridFeedState
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.gallery.dal.UserProfileGalleryFeedViewModel
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.gallery.dal.UserProfileGalleryFeedViewModel
|
||||
|
||||
@Composable
|
||||
fun TabGallery(
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.gallery.dal
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.gallery.dal
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter
|
||||
import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams
|
||||
+7
-2
@@ -18,14 +18,16 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.gallery.dal
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.gallery.dal
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
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.User
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.AndroidFeedViewModel
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@Stable
|
||||
class UserProfileGalleryFeedViewModel(
|
||||
@@ -37,6 +39,9 @@ class UserProfileGalleryFeedViewModel(
|
||||
val account: Account,
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T = UserProfileGalleryFeedViewModel(user, account) as T
|
||||
override fun <T : ViewModel> create(
|
||||
modelClass: KClass<T>,
|
||||
extras: CreationExtras,
|
||||
): T = UserProfileGalleryFeedViewModel(user, account) as T
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.hashtags
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.hashtags
|
||||
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.hashtags
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.hashtags
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.header
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
+2
-2
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.header
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
@@ -35,10 +35,10 @@ import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.error_dialog_payment_error
|
||||
import com.vitorpamplona.amethyst.commons.resources.no_payment_app_found_for_type
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.payment.ProfilePaymentMethod
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.BitcoinOrange
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.Size16Modifier
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.payment.ProfilePaymentMethod
|
||||
import com.vitorpamplona.quartz.nipA3PaymentTargets.PaymentTarget
|
||||
import com.vitorpamplona.quartz.nipBCOnchainZaps.taproot.SegwitAddress
|
||||
|
||||
+20
-22
@@ -18,9 +18,8 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.header
|
||||
|
||||
import android.content.ClipData
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.Row
|
||||
@@ -40,16 +39,13 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.ClipEntry
|
||||
import androidx.compose.ui.platform.LocalClipboard
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.commons.buzz.ui.BuzzAgentLabel
|
||||
import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
|
||||
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
|
||||
@@ -67,12 +63,19 @@ import com.vitorpamplona.amethyst.commons.resources.show_nprofile_as_a_qr_code
|
||||
import com.vitorpamplona.amethyst.commons.resources.telegram
|
||||
import com.vitorpamplona.amethyst.commons.resources.twitter
|
||||
import com.vitorpamplona.amethyst.commons.resources.website
|
||||
import com.vitorpamplona.amethyst.commons.resources.x
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.TranslatableRichTextViewer
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.util.LongPressCopyText
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.util.setText
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.ObserveAndRenderNIP05VerifiedSymbol
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.lastSeenSentence
|
||||
import com.vitorpamplona.amethyst.commons.ui.painterRes
|
||||
import com.vitorpamplona.amethyst.commons.ui.platform.SpeakNameButton
|
||||
import com.vitorpamplona.amethyst.commons.ui.richtext.CreateTextWithEmoji
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.header.apps.DisplayAppRecommendations
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.header.apps.UserAppRecommendationsFeedViewModel
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.header.badges.DisplayBadges
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.Size15Modifier
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.SpacedBy3dp
|
||||
@@ -82,12 +85,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText
|
||||
import com.vitorpamplona.amethyst.commons.util.toShortDisplay
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.UserExternalIdentitiesViewModel
|
||||
import com.vitorpamplona.amethyst.ui.components.util.LongPressCopyText
|
||||
import com.vitorpamplona.amethyst.ui.note.DrawPlayName
|
||||
import com.vitorpamplona.amethyst.ui.painterRes
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.apps.DisplayAppRecommendations
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.apps.UserAppRecommendationsFeedViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.badges.DisplayBadges
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip39ExtIdentities.GitHubIdentity
|
||||
@@ -96,9 +93,11 @@ import com.vitorpamplona.quartz.nip39ExtIdentities.MastodonIdentity
|
||||
import com.vitorpamplona.quartz.nip39ExtIdentities.TelegramIdentity
|
||||
import com.vitorpamplona.quartz.nip39ExtIdentities.TwitterIdentity
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.IO
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
import org.jetbrains.compose.resources.DrawableResource
|
||||
import org.jetbrains.compose.resources.StringResource
|
||||
|
||||
private const val IDENTITY_ICON_CACHE_KEY = 0
|
||||
@@ -151,7 +150,7 @@ fun DrawAdditionalInfo(
|
||||
Spacer(StdHorzSpacer)
|
||||
}
|
||||
|
||||
DrawPlayName(displayName)
|
||||
SpeakNameButton(displayName)
|
||||
}
|
||||
|
||||
// A Buzz agent says so, and whose it is: the owner from the NIP-OA tag on its kind 0,
|
||||
@@ -184,7 +183,7 @@ fun DrawAdditionalInfo(
|
||||
modifier = Modifier.size(23.dp).padding(start = 5.dp),
|
||||
onClick = {
|
||||
scope.launch {
|
||||
clipboard.setClipEntry(ClipEntry(ClipData.newPlainText("npub", baseUser.pubkeyNpub())))
|
||||
clipboard.setText(baseUser.pubkeyNpub())
|
||||
}
|
||||
},
|
||||
) {
|
||||
@@ -208,7 +207,7 @@ fun DrawAdditionalInfo(
|
||||
modifier = Modifier.size(23.dp).padding(start = 5.dp),
|
||||
onClick = {
|
||||
scope.launch {
|
||||
clipboard.setClipEntry(ClipEntry(ClipData.newPlainText("nprofile", baseUser.toNProfile())))
|
||||
clipboard.setText(baseUser.toNProfile())
|
||||
}
|
||||
},
|
||||
) {
|
||||
@@ -271,7 +270,7 @@ fun DrawAdditionalInfo(
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Icon(
|
||||
tint = Color.Unspecified,
|
||||
painter = painterRes(resourceId = getIdentityClaimIcon(identity), IDENTITY_ICON_CACHE_KEY),
|
||||
painter = painterRes(getIdentityClaimIcon(identity), IDENTITY_ICON_CACHE_KEY),
|
||||
contentDescription = stringRes(getIdentityClaimDescription(identity)),
|
||||
modifier = Modifier.size(18.dp),
|
||||
)
|
||||
@@ -341,7 +340,6 @@ fun DisplayLastSeen(
|
||||
val lastSeen by lastSeenFlow.collectAsStateWithLifecycle(null)
|
||||
|
||||
lastSeen?.let { timestamp ->
|
||||
val context = LocalContext.current
|
||||
Text(
|
||||
text = lastSeenSentence(timestamp),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
@@ -384,13 +382,13 @@ fun DisplayNip05ProfileStatus(
|
||||
}
|
||||
}
|
||||
|
||||
fun getIdentityClaimIcon(identity: IdentityClaimTag): Int =
|
||||
fun getIdentityClaimIcon(identity: IdentityClaimTag): DrawableResource =
|
||||
when (identity) {
|
||||
is TwitterIdentity -> R.drawable.x
|
||||
is TelegramIdentity -> R.drawable.telegram
|
||||
is MastodonIdentity -> R.drawable.mastodon
|
||||
is GitHubIdentity -> R.drawable.github
|
||||
else -> R.drawable.github
|
||||
is TwitterIdentity -> Res.drawable.x
|
||||
is TelegramIdentity -> Res.drawable.telegram
|
||||
is MastodonIdentity -> Res.drawable.mastodon
|
||||
is GitHubIdentity -> Res.drawable.github
|
||||
else -> Res.drawable.github
|
||||
}
|
||||
|
||||
fun getIdentityClaimDescription(identity: IdentityClaimTag): StringResource =
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.header
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.Image
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.header
|
||||
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.header
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
+6
-8
@@ -18,9 +18,8 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.header
|
||||
|
||||
import android.content.ClipData
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
@@ -47,11 +46,9 @@ import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.geometry.Rect
|
||||
import androidx.compose.ui.layout.boundsInWindow
|
||||
import androidx.compose.ui.layout.onGloballyPositioned
|
||||
import androidx.compose.ui.platform.ClipEntry
|
||||
import androidx.compose.ui.platform.LocalClipboard
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
|
||||
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
@@ -62,16 +59,18 @@ import com.vitorpamplona.amethyst.commons.richtext.RichTextParser
|
||||
import com.vitorpamplona.amethyst.commons.ui.actions.NewUserMetadataViewModel
|
||||
import com.vitorpamplona.amethyst.commons.ui.actions.uploads.GallerySelectSingle
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LoadingAnimation
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.rememberViewModel
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.util.setText
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.ClickableUserPicture
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.platform.ZoomableImageDialog
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.header.apps.UserAppRecommendationsFeedViewModel
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.Size100dp
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.Size35dp
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.userProfileBorderModifier
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.UserExternalIdentitiesViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.apps.UserAppRecommendationsFeedViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
@@ -148,8 +147,7 @@ fun ZoomableUserPicture(
|
||||
onLongClick = {
|
||||
baseUser.profilePicture()?.let { it1 ->
|
||||
scope.launch {
|
||||
val clipData = ClipData.newPlainText("profile picture url", it1)
|
||||
clipboardManager.setClipEntry(ClipEntry(clipData))
|
||||
clipboardManager.setText(it1)
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -191,7 +189,7 @@ private fun ProfilePictureUploadButton(
|
||||
accountViewModel: AccountViewModel,
|
||||
size: Dp,
|
||||
) {
|
||||
val postViewModel: NewUserMetadataViewModel = viewModel()
|
||||
val postViewModel: NewUserMetadataViewModel = rememberViewModel { NewUserMetadataViewModel() }
|
||||
postViewModel.init(accountViewModel)
|
||||
|
||||
var showGallerySelect by remember { mutableStateOf(false) }
|
||||
+6
-7
@@ -18,9 +18,8 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.header
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
@@ -44,7 +43,6 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalClipboard
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -68,17 +66,18 @@ import com.vitorpamplona.amethyst.commons.resources.copy_to_clipboard
|
||||
import com.vitorpamplona.amethyst.commons.resources.send_payment_method_cashu
|
||||
import com.vitorpamplona.amethyst.commons.resources.send_payment_method_lightning
|
||||
import com.vitorpamplona.amethyst.commons.resources.send_payment_method_onchain
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.rememberShortNotice
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.util.setText
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.LoadAddressableNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.header.Bolt12OffersDialog
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.header.abbreviateBolt12Offer
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.payment.ProfilePaymentMethod
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.payment.rememberProfileClinkOffer
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.BitcoinOrange
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.Size16Modifier
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.payment.ProfilePaymentMethod
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.payment.rememberProfileClinkOffer
|
||||
import com.vitorpamplona.quartz.experimental.clink.pointers.NOffer
|
||||
import com.vitorpamplona.quartz.nipA3PaymentTargets.PaymentTarget
|
||||
import com.vitorpamplona.quartz.nipA3PaymentTargets.PaymentTargetsEvent
|
||||
@@ -315,7 +314,7 @@ fun ProfilePaymentChip(
|
||||
copyValue: String? = null,
|
||||
icon: @Composable () -> Unit,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val shortNotice = rememberShortNotice()
|
||||
val clipboard = LocalClipboard.current
|
||||
val scope = rememberCoroutineScope()
|
||||
val copyLabel = stringRes(Res.string.copy_to_clipboard)
|
||||
@@ -328,7 +327,7 @@ fun ProfilePaymentChip(
|
||||
onLongClick = {
|
||||
scope.launch {
|
||||
clipboard.setText(copyValue)
|
||||
Toast.makeText(context, copiedMessage, Toast.LENGTH_SHORT).show()
|
||||
shortNotice.show(copiedMessage)
|
||||
}
|
||||
},
|
||||
onLongClickLabel = copyLabel,
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.header
|
||||
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.header
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
+4
-16
@@ -18,15 +18,13 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.header
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.platform.LocalClipboard
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.nip85TrustedAssertions.ui.EditNicknameDialog
|
||||
@@ -47,6 +45,7 @@ import com.vitorpamplona.amethyst.commons.resources.unblock_user
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.M3ActionDialog
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.M3ActionRow
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.M3ActionSection
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.rememberTextSharer
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.util.setText
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.creators.emojiSuggestions.WatchAndLoadMyEmojiList
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.externalLinkForUser
|
||||
@@ -85,7 +84,7 @@ fun UserProfileDropDownMenu(
|
||||
) {
|
||||
val clipboardManager = LocalClipboard.current
|
||||
val scope = rememberCoroutineScope()
|
||||
val context = LocalContext.current
|
||||
val textSharer = rememberTextSharer()
|
||||
|
||||
// Share section
|
||||
M3ActionSection {
|
||||
@@ -102,18 +101,7 @@ fun UserProfileDropDownMenu(
|
||||
icon = MaterialSymbols.Share,
|
||||
text = stringRes(Res.string.quick_action_share),
|
||||
) {
|
||||
val sendIntent =
|
||||
Intent().apply {
|
||||
action = Intent.ACTION_SEND
|
||||
type = "text/plain"
|
||||
putExtra(Intent.EXTRA_TEXT, externalLinkForUser(user))
|
||||
putExtra(
|
||||
Intent.EXTRA_TITLE,
|
||||
quickActionShareBrowserLinkStr,
|
||||
)
|
||||
}
|
||||
val shareIntent = Intent.createChooser(sendIntent, quickActionShareStr)
|
||||
context.startActivity(shareIntent)
|
||||
textSharer.share(externalLinkForUser(user), quickActionShareBrowserLinkStr, quickActionShareStr)
|
||||
onDismiss()
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.apps
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.header.apps
|
||||
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
+7
-2
@@ -18,13 +18,15 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.apps
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.header.apps
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewmodel.CreationExtras
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.AndroidFeedViewModel
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@Stable
|
||||
class UserAppRecommendationsFeedViewModel(
|
||||
@@ -34,6 +36,9 @@ class UserAppRecommendationsFeedViewModel(
|
||||
val user: User,
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T = UserAppRecommendationsFeedViewModel(user) as T
|
||||
override fun <T : ViewModel> create(
|
||||
modelClass: KClass<T>,
|
||||
extras: CreationExtras,
|
||||
): T = UserAppRecommendationsFeedViewModel(user) as T
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.apps
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.header.apps
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter
|
||||
import com.vitorpamplona.amethyst.commons.feeds.sortedByDefaultFeedOrder
|
||||
+2
-1
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.badges
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.header.badges
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
@@ -90,6 +90,7 @@ import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.IO
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
+2
-2
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.mutual
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.mutual
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
@@ -26,8 +26,8 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.RefresheableFeedView
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.mutual.dal.UserProfileMutualFeedViewModel
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.mutual.dal.UserProfileMutualFeedViewModel
|
||||
|
||||
@Composable
|
||||
fun TabMutualConversations(
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.mutual.dal
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.mutual.dal
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter
|
||||
import com.vitorpamplona.amethyst.commons.feeds.isRenderableRepost
|
||||
+7
-2
@@ -18,14 +18,16 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.mutual.dal
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.mutual.dal
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
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.User
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.AndroidFeedViewModel
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@Stable
|
||||
class UserProfileMutualFeedViewModel(
|
||||
@@ -37,6 +39,9 @@ class UserProfileMutualFeedViewModel(
|
||||
val account: Account,
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T = UserProfileMutualFeedViewModel(user, account) as T
|
||||
override fun <T : ViewModel> create(
|
||||
modelClass: KClass<T>,
|
||||
extras: CreationExtras,
|
||||
): T = UserProfileMutualFeedViewModel(user, account) as T
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.newthreads
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.newthreads
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@@ -45,11 +45,11 @@ import com.vitorpamplona.amethyst.commons.ui.feeds.StickToTopOnPrepend
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.WatchScrollToTop
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.NoteCompose
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.newthreads.dal.UserProfileNewThreadsFeedViewModel
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.pinnedNotes.dal.UserProfilePinnedNotesFeedViewModel
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.DividerThickness
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.FeedPadding
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.newthreads.dal.UserProfileNewThreadsFeedViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.pinnedNotes.dal.UserProfilePinnedNotesFeedViewModel
|
||||
|
||||
@Composable
|
||||
fun TabNotesNewThreads(
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.newthreads.dal
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.newthreads.dal
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter
|
||||
import com.vitorpamplona.amethyst.commons.feeds.isRenderableRepost
|
||||
+7
-2
@@ -18,14 +18,16 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.newthreads.dal
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.newthreads.dal
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
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.User
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.AndroidFeedViewModel
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@Stable
|
||||
class UserProfileNewThreadsFeedViewModel(
|
||||
@@ -37,6 +39,9 @@ class UserProfileNewThreadsFeedViewModel(
|
||||
val account: Account,
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T = UserProfileNewThreadsFeedViewModel(user, account) as T
|
||||
override fun <T : ViewModel> create(
|
||||
modelClass: KClass<T>,
|
||||
extras: CreationExtras,
|
||||
): T = UserProfileNewThreadsFeedViewModel(user, account) as T
|
||||
}
|
||||
}
|
||||
+5
-4
@@ -18,9 +18,8 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.payment
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.payment
|
||||
|
||||
import android.util.LruCache
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -32,7 +31,9 @@ import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.quartz.experimental.clink.pointers.ClinkPointerParser
|
||||
import com.vitorpamplona.quartz.experimental.clink.pointers.NOffer
|
||||
import com.vitorpamplona.quartz.nip05DnsIdentifiers.Nip05Id
|
||||
import com.vitorpamplona.quartz.utils.cache.ConcurrentLruCache
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.IO
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
/**
|
||||
@@ -41,13 +42,13 @@ import kotlinx.coroutines.withContext
|
||||
* profile visit (and every relay-pushed kind-0 refresh while a profile is open) would
|
||||
* re-fetch the domain's nostr.json. Caches "no offer" results too so profiles without one
|
||||
* aren't re-hit. A [ResolvedClinkOffer] wrapper holds the nullable parsed pointer
|
||||
* (LruCache can't store nulls); absence means "not fetched yet".
|
||||
* (the cache can't store nulls); absence means "not fetched yet".
|
||||
*/
|
||||
private class ResolvedClinkOffer(
|
||||
val noffer: NOffer?,
|
||||
)
|
||||
|
||||
private val clinkOfferNip05Cache = LruCache<String, ResolvedClinkOffer>(256)
|
||||
private val clinkOfferNip05Cache = ConcurrentLruCache<String, ResolvedClinkOffer>(256)
|
||||
|
||||
/**
|
||||
* Resolves a profile's advertised CLINK Offer, preferring the kind-0 `clink_offer`
|
||||
+6
-6
@@ -18,9 +18,8 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.payment
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.payment
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
@@ -59,7 +58,6 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalClipboard
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
@@ -91,6 +89,7 @@ import com.vitorpamplona.amethyst.commons.resources.send_payment_method_onchain
|
||||
import com.vitorpamplona.amethyst.commons.resources.send_payment_no_methods
|
||||
import com.vitorpamplona.amethyst.commons.resources.send_payment_receipt_section
|
||||
import com.vitorpamplona.amethyst.commons.resources.send_payment_try_again
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.rememberShortNotice
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.util.setText
|
||||
import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
@@ -100,6 +99,7 @@ import com.vitorpamplona.amethyst.commons.ui.theme.ThemeComparisonColumn
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText
|
||||
import com.vitorpamplona.amethyst.commons.util.showAmount
|
||||
import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
|
||||
import com.vitorpamplona.quartz.utils.BigDecimal
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
@@ -469,7 +469,7 @@ private fun MethodChip(
|
||||
enabled: Boolean,
|
||||
onSelect: () -> Unit,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val shortNotice = rememberShortNotice()
|
||||
val clipboard = LocalClipboard.current
|
||||
val scope = rememberCoroutineScope()
|
||||
val copyLabel = stringRes(Res.string.copy_to_clipboard)
|
||||
@@ -493,7 +493,7 @@ private fun MethodChip(
|
||||
{
|
||||
scope.launch {
|
||||
clipboard.setText(value)
|
||||
Toast.makeText(context, copiedMessage, Toast.LENGTH_SHORT).show()
|
||||
shortNotice.show(copiedMessage)
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -596,7 +596,7 @@ private fun AmountSection(
|
||||
SuggestionChip(
|
||||
enabled = enabled,
|
||||
onClick = { onAmountChange(amount.toString()) },
|
||||
label = { Text("⚡ ${showAmount(amount.toBigDecimal())}") },
|
||||
label = { Text("⚡ ${showAmount(BigDecimal(amount))}") },
|
||||
)
|
||||
}
|
||||
}
|
||||
+12
-10
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.payment
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.payment
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@@ -40,7 +40,6 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
@@ -96,6 +95,7 @@ import com.vitorpamplona.amethyst.commons.resources.zap_type_private
|
||||
import com.vitorpamplona.amethyst.commons.resources.zap_type_private_explainer
|
||||
import com.vitorpamplona.amethyst.commons.resources.zap_type_public
|
||||
import com.vitorpamplona.amethyst.commons.resources.zap_type_public_explainer
|
||||
import com.vitorpamplona.amethyst.commons.service.ClinkOfferPayer
|
||||
import com.vitorpamplona.amethyst.commons.service.nwc.nwcFailureDetail
|
||||
import com.vitorpamplona.amethyst.commons.service.nwc.nwcTimeoutMessage
|
||||
import com.vitorpamplona.amethyst.commons.ui.loadStringRes
|
||||
@@ -108,10 +108,11 @@ import com.vitorpamplona.amethyst.commons.ui.note.UsernameDisplay
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.wallet.FeeTier
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.wallet.rateFor
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.ui.wallet.payInvoice
|
||||
import com.vitorpamplona.amethyst.commons.ui.wallet.rememberWalletAppLauncher
|
||||
import com.vitorpamplona.amethyst.commons.util.DecimalPatternFormatter
|
||||
import com.vitorpamplona.amethyst.commons.util.showAmount
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.service.ClinkOfferPayer
|
||||
import com.vitorpamplona.amethyst.ui.note.payViaIntent
|
||||
import com.vitorpamplona.quartz.experimental.clink.common.SatRange
|
||||
import com.vitorpamplona.quartz.experimental.clink.offers.OfferErrorCode
|
||||
import com.vitorpamplona.quartz.experimental.clink.pointers.ClinkPointerParser
|
||||
@@ -121,10 +122,12 @@ import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
|
||||
import com.vitorpamplona.quartz.nipBCOnchainZaps.chain.FeeEstimates
|
||||
import com.vitorpamplona.quartz.nipBCOnchainZaps.taproot.SegwitAddress
|
||||
import com.vitorpamplona.quartz.nipBCOnchainZaps.taproot.TaprootAddress
|
||||
import com.vitorpamplona.quartz.utils.BigDecimal
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.IO
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
@@ -180,7 +183,7 @@ private fun SendPaymentLoaded(
|
||||
nav: INav,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val walletLauncher = rememberWalletAppLauncher()
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
val userInfo by observeUserInfo(user, accountViewModel)
|
||||
@@ -368,7 +371,7 @@ private fun SendPaymentLoaded(
|
||||
selectedMethod == ProfilePaymentMethod.CASHU ->
|
||||
stringRes(
|
||||
Res.string.send_payment_cashu_balance,
|
||||
showAmount((cashuFunding?.bestSingleMintSats ?: 0L).toBigDecimal()),
|
||||
showAmount(BigDecimal(cashuFunding?.bestSingleMintSats ?: 0L)),
|
||||
)
|
||||
selectedMethod == ProfilePaymentMethod.CLINK && clinkRange?.min != null && clinkRange?.max != null ->
|
||||
stringRes(Res.string.clink_offer_amount_range, clinkRange?.min.toString(), clinkRange?.max.toString())
|
||||
@@ -457,9 +460,8 @@ private fun SendPaymentLoaded(
|
||||
// startActivity belongs on Main. sendSats now delivers onNewInvoice
|
||||
// there already; the hop also covers any other caller of payBolt11.
|
||||
scope.launch {
|
||||
payViaIntent(
|
||||
walletLauncher.payInvoice(
|
||||
invoice,
|
||||
context,
|
||||
noWalletFoundStr,
|
||||
onPaid = { scope.launch { postStage(PaymentFlowStage.Success(successTitle, sentToWalletLabel)) } },
|
||||
onError = { scope.launch { postStage(PaymentFlowStage.Failure(it)) } },
|
||||
@@ -664,7 +666,7 @@ private fun SendPaymentLoaded(
|
||||
canSend = canSend,
|
||||
sendLabel =
|
||||
if (amountSats != null && amountSats > 0) {
|
||||
stringRes(Res.string.send_payment_pay_button, showAmount(amountSats.toBigDecimal()))
|
||||
stringRes(Res.string.send_payment_pay_button, showAmount(BigDecimal(amountSats)))
|
||||
} else {
|
||||
stringRes(Res.string.send_payment_pay_button_empty)
|
||||
},
|
||||
@@ -800,7 +802,7 @@ private fun OnchainFeeSection(
|
||||
stringRes(
|
||||
Res.string.onchain_send_fee_tier_label_rate_eta,
|
||||
stringRes(tier.labelRes),
|
||||
"%.1f".format(rate),
|
||||
DecimalPatternFormatter("0.0").format(rate),
|
||||
stringRes(tier.etaLabelRes),
|
||||
)
|
||||
} else {
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.pinnedNotes.dal
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.pinnedNotes.dal
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.feeds.FeedFilter
|
||||
import com.vitorpamplona.amethyst.commons.model.Account
|
||||
+7
-2
@@ -18,14 +18,16 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.pinnedNotes.dal
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.pinnedNotes.dal
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
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.User
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.AndroidFeedViewModel
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@Stable
|
||||
class UserProfilePinnedNotesFeedViewModel(
|
||||
@@ -37,6 +39,9 @@ class UserProfilePinnedNotesFeedViewModel(
|
||||
val account: Account,
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T = UserProfilePinnedNotesFeedViewModel(user, account) as T
|
||||
override fun <T : ViewModel> create(
|
||||
modelClass: KClass<T>,
|
||||
extras: CreationExtras,
|
||||
): T = UserProfilePinnedNotesFeedViewModel(user, account) as T
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.relays
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.relays
|
||||
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.padding
|
||||
+3
-3
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.relays
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.relays
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
@@ -28,8 +28,8 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleEventObserver
|
||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.rememberViewModel
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.RelayFeedViewModel
|
||||
@@ -40,7 +40,7 @@ fun TabRelays(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
val feedViewModel: RelayFeedViewModel = viewModel()
|
||||
val feedViewModel: RelayFeedViewModel = rememberViewModel { RelayFeedViewModel() }
|
||||
|
||||
val lifeCycleOwner = LocalLifecycleOwner.current
|
||||
|
||||
+2
-2
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.reports
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.reports
|
||||
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -28,9 +28,9 @@ import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.number_reports
|
||||
import com.vitorpamplona.amethyst.commons.resources.reports
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.reports.dal.UserProfileReportFeedViewModel
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.reports.dal.UserProfileReportFeedViewModel
|
||||
|
||||
@Composable
|
||||
fun ReportsTabHeader(
|
||||
+2
-2
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.reports
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.reports
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
@@ -36,10 +36,10 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.NoteCompose
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.reports.dal.UserProfileReportFeedViewModel
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.DividerThickness
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.FeedPadding
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.reports.dal.UserProfileReportFeedViewModel
|
||||
|
||||
@Composable
|
||||
fun TabReports(
|
||||
+8
-2
@@ -18,22 +18,25 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.reports.dal
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.reports.dal
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.viewmodel.CreationExtras
|
||||
import com.vitorpamplona.amethyst.commons.model.Account
|
||||
import com.vitorpamplona.amethyst.commons.model.Note
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.IO
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.sample
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@Stable
|
||||
class UserProfileReportFeedViewModel(
|
||||
@@ -76,6 +79,9 @@ class UserProfileReportFeedViewModel(
|
||||
val account: Account,
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T = UserProfileReportFeedViewModel(user, account) as T
|
||||
override fun <T : ViewModel> create(
|
||||
modelClass: KClass<T>,
|
||||
extras: CreationExtras,
|
||||
): T = UserProfileReportFeedViewModel(user, account) as T
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.zaps
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.zaps
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
@@ -32,10 +32,10 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.zaps.dal.UserProfileZapsViewModel
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.DividerThickness
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.FeedPadding
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.zaps.dal.UserProfileZapsViewModel
|
||||
|
||||
@Composable
|
||||
fun TabReceivedZaps(
|
||||
+3
-3
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.zaps
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.zaps
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
@@ -42,13 +42,13 @@ import com.vitorpamplona.amethyst.commons.ui.navigation.routes.routeFor
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.UserActionOptions
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.UserPicture
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.UsernameDisplay
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.zaps.dal.ZapAmount
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.BitcoinOrange
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.Size55dp
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.ThemeComparisonColumn
|
||||
import com.vitorpamplona.amethyst.commons.util.showAmountInteger
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.mockAccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.zaps.dal.ZapAmount
|
||||
import com.vitorpamplona.quartz.utils.BigDecimal
|
||||
|
||||
@Composable
|
||||
@@ -77,7 +77,7 @@ fun RenderZapNoteSlimPreview() {
|
||||
|
||||
ThemeComparisonColumn {
|
||||
RenderZapNoteSlim(
|
||||
ZapAmount(user1, BigDecimal.TEN),
|
||||
ZapAmount(user1, BigDecimal(10)),
|
||||
accountViewModel,
|
||||
EmptyNav(),
|
||||
)
|
||||
+2
-2
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.zaps
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.zaps
|
||||
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -26,10 +26,10 @@ import androidx.compose.runtime.getValue
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.zaps
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.zaps.dal.UserProfileZapsViewModel
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.util.showAmountInteger
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.zaps.dal.UserProfileZapsViewModel
|
||||
|
||||
@Composable
|
||||
fun ZapTabHeader(
|
||||
+19
-11
@@ -18,13 +18,14 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.zaps.dal
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.zaps.dal
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.viewmodel.CreationExtras
|
||||
import com.vitorpamplona.amethyst.commons.model.Account
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.model.cache.LocalCache
|
||||
@@ -34,13 +35,16 @@ import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
|
||||
import com.vitorpamplona.quartz.nipB1Bolt12Zaps.zap.Bolt12ZapEvent
|
||||
import com.vitorpamplona.quartz.nipBCOnchainZaps.zap.OnchainZapEvent
|
||||
import com.vitorpamplona.quartz.utils.BigDecimal
|
||||
import com.vitorpamplona.quartz.utils.plus
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.IO
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.sample
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@Immutable
|
||||
data class ZapAmount(
|
||||
@@ -59,14 +63,15 @@ class UserProfileZapsViewModel(
|
||||
tags = mapOf("p" to listOf(user.pubkeyHex)),
|
||||
)
|
||||
|
||||
// Largest amount first. The shared BigDecimal is not Comparable, so the order comes from the sign of the difference.
|
||||
val sortingModel: Comparator<ZapAmount> =
|
||||
compareByDescending<ZapAmount> { it.amount }.thenBy { it.user.pubkeyHex }
|
||||
Comparator<ZapAmount> { a, b -> b.amount.subtract(a.amount).signum() }.thenBy { it.user.pubkeyHex }
|
||||
|
||||
suspend fun mapRequest(zapEvent: ZapReceiptEvent): ZapAmount? {
|
||||
val zapRequest =
|
||||
zapEvent.zapRequest ?: return ZapAmount(
|
||||
LocalCache.getOrCreateUser(zapEvent.pubKey),
|
||||
zapEvent.amount ?: BigDecimal.ZERO,
|
||||
zapEvent.amount ?: BigDecimal(0),
|
||||
)
|
||||
|
||||
return if (zapRequest.isPrivateZap()) {
|
||||
@@ -76,24 +81,24 @@ class UserProfileZapsViewModel(
|
||||
if (cachedPrivateRequest != null) {
|
||||
ZapAmount(
|
||||
LocalCache.getOrCreateUser(cachedPrivateRequest.pubKey),
|
||||
zapEvent.amount ?: BigDecimal.ZERO,
|
||||
zapEvent.amount ?: BigDecimal(0),
|
||||
)
|
||||
} else {
|
||||
ZapAmount(
|
||||
LocalCache.getOrCreateUser(zapRequest.pubKey),
|
||||
zapEvent.amount ?: BigDecimal.ZERO,
|
||||
zapEvent.amount ?: BigDecimal(0),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
ZapAmount(
|
||||
LocalCache.getOrCreateUser(zapRequest.pubKey),
|
||||
zapEvent.amount ?: BigDecimal.ZERO,
|
||||
zapEvent.amount ?: BigDecimal(0),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
ZapAmount(
|
||||
LocalCache.getOrCreateUser(zapRequest.pubKey),
|
||||
zapEvent.amount ?: BigDecimal.ZERO,
|
||||
zapEvent.amount ?: BigDecimal(0),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -128,7 +133,7 @@ class UserProfileZapsViewModel(
|
||||
else -> null
|
||||
}
|
||||
if (zapAmount != null) {
|
||||
val existingAmount = results[zapAmount.user] ?: BigDecimal.ZERO
|
||||
val existingAmount = results[zapAmount.user] ?: BigDecimal(0)
|
||||
results[zapAmount.user] = existingAmount + zapAmount.amount
|
||||
}
|
||||
}
|
||||
@@ -152,11 +157,11 @@ class UserProfileZapsViewModel(
|
||||
|
||||
val totalReceivedZaps =
|
||||
receivedZapAmountsByUser
|
||||
.map { it.sumOf { it.amount } }
|
||||
.map { amounts -> amounts.fold(BigDecimal(0)) { total, zap -> total + zap.amount } }
|
||||
.flowOn(Dispatchers.IO)
|
||||
.stateIn(
|
||||
viewModelScope,
|
||||
initialValue = BigDecimal.ZERO,
|
||||
initialValue = BigDecimal(0),
|
||||
started = SharingStarted.Lazily,
|
||||
)
|
||||
|
||||
@@ -165,6 +170,9 @@ class UserProfileZapsViewModel(
|
||||
val account: Account,
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T = UserProfileZapsViewModel(user, account) as T
|
||||
override fun <T : ViewModel> create(
|
||||
modelClass: KClass<T>,
|
||||
extras: CreationExtras,
|
||||
): T = UserProfileZapsViewModel(user, account) as T
|
||||
}
|
||||
}
|
||||
+3
-46
@@ -18,9 +18,8 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.settings
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
@@ -53,7 +52,6 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.semantics.contentDescription
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
@@ -62,8 +60,6 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.core.os.LocaleListCompat
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
|
||||
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbol
|
||||
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
|
||||
@@ -120,6 +116,7 @@ import com.vitorpamplona.amethyst.commons.ui.components.TitleExplainer
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.EmptyNav
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.TopBarWithBackButton
|
||||
import com.vitorpamplona.amethyst.commons.ui.platform.LocalAppPlatform
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.SegmentedChoiceTile
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.SettingsBlockTile
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.SettingsControlRow
|
||||
@@ -134,12 +131,8 @@ import com.vitorpamplona.amethyst.commons.ui.theme.toFontFamily
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import kotlinx.collections.immutable.ImmutableMap
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableMap
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import org.jetbrains.compose.resources.StringResource
|
||||
import org.xmlpull.v1.XmlPullParser
|
||||
import org.xmlpull.v1.XmlPullParserException
|
||||
import java.io.IOException
|
||||
|
||||
@Composable
|
||||
fun SettingsScreen(
|
||||
@@ -436,9 +429,7 @@ private fun AccentColorTile(sharedPrefs: UiSettingsFlow) {
|
||||
// matches the app's other "opens a picker" rows instead of leaving a lone text-field dropdown here.
|
||||
@Composable
|
||||
private fun LanguageTile(sharedPrefs: UiSettingsFlow) {
|
||||
val context = LocalContext.current
|
||||
|
||||
val languageEntries = remember { context.getLangPreferenceDropdownEntries() }
|
||||
val languageEntries = LocalAppPlatform.current.rememberAppLanguages()
|
||||
val languageList = remember { languageEntries.keys.map { TitleExplainer(it) }.toImmutableList() }
|
||||
|
||||
val language by sharedPrefs.preferredLanguage.collectAsState()
|
||||
@@ -482,40 +473,6 @@ private fun LanguageTile(sharedPrefs: UiSettingsFlow) {
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.getLocaleListFromXml(): LocaleListCompat {
|
||||
val tagsList = mutableListOf<CharSequence>()
|
||||
try {
|
||||
val xpp: XmlPullParser = resources.getXml(R.xml.locales_config)
|
||||
while (xpp.eventType != XmlPullParser.END_DOCUMENT) {
|
||||
if (xpp.eventType == XmlPullParser.START_TAG && xpp.name == "locale") {
|
||||
tagsList.add(xpp.getAttributeValue(0))
|
||||
}
|
||||
xpp.next()
|
||||
}
|
||||
} catch (e: XmlPullParserException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
return LocaleListCompat.forLanguageTags(tagsList.joinToString(","))
|
||||
}
|
||||
|
||||
fun Context.getLangPreferenceDropdownEntries(): ImmutableMap<String, String> {
|
||||
val localeList = getLocaleListFromXml()
|
||||
val map = mutableMapOf<String, String>()
|
||||
|
||||
for (a in 0 until localeList.size()) {
|
||||
localeList[a].let {
|
||||
map.put(
|
||||
it!!.getDisplayName(it).replaceFirstChar { char -> char.uppercase() },
|
||||
it.toLanguageTag(),
|
||||
)
|
||||
}
|
||||
}
|
||||
return map.toImmutableMap()
|
||||
}
|
||||
|
||||
fun getLanguageIndex(
|
||||
languageEntries: ImmutableMap<String, String>,
|
||||
language: String?,
|
||||
+10
-17
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.settings
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
@@ -33,34 +33,28 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.Amethyst
|
||||
import com.vitorpamplona.amethyst.commons.model.preferences.NamecoinSettingsStore
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.namecoin_settings
|
||||
import com.vitorpamplona.amethyst.commons.service.namecoin.NamecoinClients
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.TopBarWithBackButton
|
||||
import com.vitorpamplona.amethyst.commons.ui.platform.LocalAppServices
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.ElectrumXClient
|
||||
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.NamecoinCoreRpcClient
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun NamecoinSettingsScreen(nav: INav) {
|
||||
NamecoinSettingsScreen(
|
||||
Amethyst.instance.namecoinPrefs,
|
||||
electrumXClient = { Amethyst.instance.electrumXClient },
|
||||
namecoinCoreRpcClient = { Amethyst.instance.namecoinCoreRpcClient },
|
||||
nav,
|
||||
)
|
||||
val services = LocalAppServices.current
|
||||
NamecoinSettingsScreen(services.namecoinSettings, services.namecoinClients, nav)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun NamecoinSettingsScreen(
|
||||
namecoinPrefs: NamecoinSettingsStore,
|
||||
electrumXClient: () -> ElectrumXClient,
|
||||
namecoinCoreRpcClient: () -> NamecoinCoreRpcClient,
|
||||
clients: NamecoinClients,
|
||||
nav: INav,
|
||||
) {
|
||||
val namecoinSettings by namecoinPrefs.settings.collectAsStateWithLifecycle()
|
||||
@@ -92,14 +86,13 @@ fun NamecoinSettingsScreen(
|
||||
onReset = {
|
||||
scope.launch { namecoinPrefs.reset() }
|
||||
},
|
||||
onTestServer = { server -> electrumXClient().testServer(server) },
|
||||
onTestServer = { server -> clients.testElectrumxServer(server) },
|
||||
onPinCert = { pem ->
|
||||
scope.launch {
|
||||
namecoinPrefs.addPinnedCert(pem)
|
||||
// Share the pinned PEM across both backends so the
|
||||
// user only has to confirm a self-signed cert once.
|
||||
electrumXClient().addPinnedCert(pem)
|
||||
namecoinCoreRpcClient().addPinnedCert(pem)
|
||||
clients.addPinnedCert(pem)
|
||||
}
|
||||
},
|
||||
onSetBackend = { backend ->
|
||||
@@ -108,7 +101,7 @@ fun NamecoinSettingsScreen(
|
||||
onSetCoreRpcConfig = { cfg ->
|
||||
scope.launch {
|
||||
namecoinPrefs.setCoreRpcConfig(cfg)
|
||||
namecoinCoreRpcClient().setConfig(cfg)
|
||||
clients.setCoreRpcConfig(cfg)
|
||||
}
|
||||
},
|
||||
onSetFallbackToCustomElectrumx = { enabled ->
|
||||
@@ -117,7 +110,7 @@ fun NamecoinSettingsScreen(
|
||||
onSetFallbackToDefaultElectrumx = { enabled ->
|
||||
scope.launch { namecoinPrefs.setFallbackToDefaultElectrumx(enabled) }
|
||||
},
|
||||
onTestCoreRpc = { cfg -> namecoinCoreRpcClient().probe(cfg) },
|
||||
onTestCoreRpc = { cfg -> clients.probeCoreRpc(cfg) },
|
||||
)
|
||||
}
|
||||
}
|
||||
+16
-16
@@ -18,9 +18,8 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.settings
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings
|
||||
|
||||
import android.os.Build
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.expandVertically
|
||||
import androidx.compose.animation.shrinkVertically
|
||||
@@ -60,7 +59,6 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
@@ -68,6 +66,7 @@ import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.vitorpamplona.amethyst.commons.browser.OmniboxInput
|
||||
import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
|
||||
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
|
||||
import com.vitorpamplona.amethyst.commons.model.nip05DnsIdentifiers.namecoin.NamecoinSettings
|
||||
@@ -89,14 +88,16 @@ import com.vitorpamplona.amethyst.commons.resources.namecoin_tls_info
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.DateTimeStyle
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.formatDateTime
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.util.deviceDescription
|
||||
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.DEFAULT_ELECTRUMX_SERVERS
|
||||
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.ElectrumxServer
|
||||
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.NamecoinBackend
|
||||
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.NamecoinCoreRpcConfig
|
||||
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.RpcProbeResult
|
||||
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.ServerTestResult
|
||||
import com.vitorpamplona.quartz.utils.currentTimeMillis
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.Locale
|
||||
import kotlin.math.roundToLong
|
||||
|
||||
/**
|
||||
* Complete settings section for Namecoin ElectrumX server configuration.
|
||||
@@ -349,7 +350,7 @@ private fun TestConnectionSection(
|
||||
)
|
||||
}
|
||||
}
|
||||
lastTestTimestamp = System.currentTimeMillis()
|
||||
lastTestTimestamp = currentTimeMillis()
|
||||
isTesting = false
|
||||
// Show confirmation dialog for each new cert
|
||||
if (newCerts.isNotEmpty()) {
|
||||
@@ -512,9 +513,8 @@ private fun DiagnosticCard(
|
||||
|
||||
// Last test timestamp
|
||||
if (lastTestTimestamp != null) {
|
||||
val context = LocalContext.current
|
||||
val formatted =
|
||||
remember(lastTestTimestamp, context) {
|
||||
remember(lastTestTimestamp) {
|
||||
formatDateTime(lastTestTimestamp, DateTimeStyle.MEDIUM, DateTimeStyle.SHORT)
|
||||
}
|
||||
val successCount = testResults.count { it.success }
|
||||
@@ -535,7 +535,7 @@ private fun DiagnosticCard(
|
||||
// Device info
|
||||
DiagnosticRow(
|
||||
label = stringRes(Res.string.namecoin_device_info),
|
||||
value = "${Build.MANUFACTURER} ${Build.MODEL}, Android ${Build.VERSION.RELEASE} (API ${Build.VERSION.SDK_INT})",
|
||||
value = deviceDescription(),
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(4.dp))
|
||||
@@ -1108,13 +1108,7 @@ private fun NamecoinCoreRpcSection(
|
||||
!candidate.usePinnedTrustStore
|
||||
)
|
||||
) {
|
||||
val host =
|
||||
try {
|
||||
java.net.URI(candidate.url).host
|
||||
?: candidate.url
|
||||
} catch (_: Exception) {
|
||||
candidate.url
|
||||
}
|
||||
val host = OmniboxInput.hostOf(candidate.url) ?: candidate.url
|
||||
pendingPin =
|
||||
PendingCertPin(
|
||||
serverHost = host,
|
||||
@@ -1167,7 +1161,7 @@ private fun NamecoinCoreRpcSection(
|
||||
val chain = probe.chain ?: "?"
|
||||
val blocks = probe.blocks?.toString() ?: "?"
|
||||
val pct =
|
||||
probe.verificationProgress?.let { String.format(Locale.ROOT, "%.2f%%", it * 100) }
|
||||
probe.verificationProgress?.let { percentTwoDecimals(it) }
|
||||
?: "?"
|
||||
Text(
|
||||
"chain=$chain height=$blocks sync=$pct" +
|
||||
@@ -1316,3 +1310,9 @@ private fun ToggleRow(
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** [fraction] as a percentage with exactly two decimals and a `.` mark ("99.50%"), as Core RPC reports it. */
|
||||
private fun percentTwoDecimals(fraction: Double): String {
|
||||
val hundredths = (fraction * 10_000).roundToLong()
|
||||
return "${hundredths / 100}.${(hundredths % 100).toString().padStart(2, '0')}%"
|
||||
}
|
||||
+3
-3
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.settings
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
@@ -33,7 +33,6 @@ import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.Amethyst
|
||||
import com.vitorpamplona.amethyst.commons.model.preferences.OtsSettingsStore
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.ots_explorer_settings
|
||||
@@ -41,13 +40,14 @@ import com.vitorpamplona.amethyst.commons.tor.TorSettingsFlow
|
||||
import com.vitorpamplona.amethyst.commons.tor.TorType
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.TopBarWithBackButton
|
||||
import com.vitorpamplona.amethyst.commons.ui.platform.LocalAppServices
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun OtsSettingsScreen(nav: INav) {
|
||||
OtsSettingsScreen(Amethyst.instance.otsPrefs, Amethyst.instance.torPrefs.value, nav)
|
||||
LocalAppServices.current.let { OtsSettingsScreen(it.otsSettings, it.torSettings, nav) }
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
+3
-3
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.settings
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
@@ -64,7 +64,7 @@ import com.vitorpamplona.amethyst.commons.resources.ots_reset_to_auto_select
|
||||
import com.vitorpamplona.amethyst.commons.resources.save
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.BitcoinOrange
|
||||
import com.vitorpamplona.quartz.nip03Timestamp.okhttp.OkHttpBitcoinExplorer
|
||||
import com.vitorpamplona.quartz.nip03Timestamp.BitcoinExplorerUrls
|
||||
|
||||
/**
|
||||
* Settings section for configuring the blockchain explorer used for OTS verification.
|
||||
@@ -180,7 +180,7 @@ private fun ActiveExplorerDisplay(
|
||||
val isCustom = settings.hasCustomExplorer
|
||||
val activeUrl =
|
||||
settings.normalizedUrl()
|
||||
?: if (isTorActive) OkHttpBitcoinExplorer.MEMPOOL_API_URL else OkHttpBitcoinExplorer.BLOCKSTREAM_API_URL
|
||||
?: if (isTorActive) BitcoinExplorerUrls.MEMPOOL else BitcoinExplorerUrls.BLOCKSTREAM
|
||||
|
||||
Column {
|
||||
Row(
|
||||
+22
-18
@@ -18,9 +18,8 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.nip46
|
||||
package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.nip46
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
@@ -56,14 +55,12 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import coil3.compose.AsyncImage
|
||||
import com.vitorpamplona.amethyst.Amethyst
|
||||
import com.vitorpamplona.amethyst.commons.connectedApps.nip46.Nip46ClientInfo
|
||||
import com.vitorpamplona.amethyst.commons.connectedApps.nip46.Nip46PermissionAuthorizer
|
||||
import com.vitorpamplona.amethyst.commons.connectedApps.signers.AppSignerPolicy
|
||||
@@ -84,11 +81,14 @@ import com.vitorpamplona.amethyst.commons.resources.nip46_signer_apps_empty
|
||||
import com.vitorpamplona.amethyst.commons.resources.nip46_signer_manage_apps
|
||||
import com.vitorpamplona.amethyst.commons.resources.nip46_signer_reconnecting
|
||||
import com.vitorpamplona.amethyst.commons.resources.nip46_signer_remote_app
|
||||
import com.vitorpamplona.amethyst.commons.service.AppServices
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.rememberShortNotice
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.TopBarWithBackButton
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.timeAgo
|
||||
import com.vitorpamplona.amethyst.commons.ui.platform.LocalAppServices
|
||||
import com.vitorpamplona.amethyst.commons.ui.pluralStringRes
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.util.toTimeAgo
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
@@ -125,7 +125,8 @@ fun Nip46ConnectedAppsScreen(
|
||||
val reconnectingStr = stringRes(Res.string.nip46_signer_reconnecting)
|
||||
val account = accountViewModel.account
|
||||
val signerPubKey = remember { account.signer.pubKey }
|
||||
val context = LocalContext.current
|
||||
val shortNotice = rememberShortNotice()
|
||||
val services = LocalAppServices.current
|
||||
|
||||
// Live relay state so each app shows whether the signer currently reaches it. An app that brought
|
||||
// its own relays (nostrconnect) is judged on those; a bunker-flow app rides the inbox relays.
|
||||
@@ -137,7 +138,7 @@ fun Nip46ConnectedAppsScreen(
|
||||
var refreshKey by remember { mutableIntStateOf(0) }
|
||||
|
||||
LaunchedEffect(refreshKey) {
|
||||
items = withContext(Dispatchers.Default) { loadNip46Apps(signerPubKey) }
|
||||
items = withContext(Dispatchers.Default) { loadNip46Apps(services, signerPubKey) }
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
@@ -187,7 +188,7 @@ fun Nip46ConnectedAppsScreen(
|
||||
online = online,
|
||||
onReconnect = {
|
||||
account.client.reconnect(ignoreRetryDelays = true)
|
||||
Toast.makeText(context, reconnectingStr, Toast.LENGTH_SHORT).show()
|
||||
shortNotice.show(reconnectingStr)
|
||||
},
|
||||
onClick = { nav.nav(Route.ConnectedAppDetail(entry.coordinate)) },
|
||||
)
|
||||
@@ -241,7 +242,7 @@ private fun Nip46AppCard(
|
||||
val meta =
|
||||
buildList {
|
||||
if (relayCount > 0) add(pluralStringRes(Res.plurals.nip46_signer_app_relay_count, relayCount, relayCount))
|
||||
entry.lastUsedSeconds?.let { add(stringRes(Res.string.nip46_signer_app_last_used, it.toTimeAgo().trim())) }
|
||||
entry.lastUsedSeconds?.let { add(stringRes(Res.string.nip46_signer_app_last_used, timeAgo(it).trim())) }
|
||||
}.joinToString(" · ")
|
||||
if (meta.isNotEmpty()) {
|
||||
Text(
|
||||
@@ -291,7 +292,7 @@ private val LiveGreen = Color(0xFF3DDC84)
|
||||
|
||||
/** A colored dot: green when the signer currently reaches the relay(s), muted otherwise. */
|
||||
@Composable
|
||||
internal fun Nip46StatusDot(online: Boolean) {
|
||||
fun Nip46StatusDot(online: Boolean) {
|
||||
Box(Modifier.size(8.dp).clip(CircleShape).background(if (online) LiveGreen else MaterialTheme.colorScheme.outline))
|
||||
}
|
||||
|
||||
@@ -301,7 +302,7 @@ internal fun Nip46StatusDot(online: Boolean) {
|
||||
* pill inside a clickable card doesn't also open the card.
|
||||
*/
|
||||
@Composable
|
||||
internal fun Nip46ReconnectPill(onClick: () -> Unit) {
|
||||
fun Nip46ReconnectPill(onClick: () -> Unit) {
|
||||
Text(
|
||||
stringRes(Res.string.nip46_signer_app_reconnect),
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
@@ -317,7 +318,7 @@ internal fun Nip46ReconnectPill(onClick: () -> Unit) {
|
||||
|
||||
/** A [Nip46StatusDot] + Connected/Offline label showing whether the signer currently reaches an app's relays. */
|
||||
@Composable
|
||||
internal fun Nip46LiveStatus(online: Boolean) {
|
||||
fun Nip46LiveStatus(online: Boolean) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
Nip46StatusDot(online)
|
||||
Text(
|
||||
@@ -334,7 +335,7 @@ internal fun Nip46LiveStatus(online: Boolean) {
|
||||
* `null` when neither resolves to any relay (nothing to show a status for). "Online" means at least
|
||||
* one of the app's relays is in [connectedRelays].
|
||||
*/
|
||||
internal fun nip46AppOnline(
|
||||
fun nip46AppOnline(
|
||||
appRelays: Set<String>,
|
||||
inboxRelays: Set<NormalizedRelayUrl>,
|
||||
connectedRelays: Set<NormalizedRelayUrl>,
|
||||
@@ -352,7 +353,7 @@ internal fun nip46AppOnline(
|
||||
* user's IP (see BrowserIconRegistry).
|
||||
*/
|
||||
@Composable
|
||||
internal fun Nip46AppIcon(
|
||||
fun Nip46AppIcon(
|
||||
image: String?,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
@@ -388,7 +389,7 @@ internal fun Nip46AppIcon(
|
||||
* advertised one, otherwise its npub. Shared by the list card and the detail header so a row and
|
||||
* the screen it opens always agree.
|
||||
*/
|
||||
internal fun nip46ClientSubtitle(
|
||||
fun nip46ClientSubtitle(
|
||||
url: String?,
|
||||
clientPubKey: HexKey,
|
||||
): String {
|
||||
@@ -402,9 +403,12 @@ internal fun nip46ClientSubtitle(
|
||||
return host ?: runCatching { NPub.create(clientPubKey) }.getOrDefault(clientPubKey.take(12) + "…")
|
||||
}
|
||||
|
||||
private suspend fun loadNip46Apps(signerPubKey: HexKey): List<Nip46AppEntry> {
|
||||
val store = Amethyst.instance.signerPermissionStore
|
||||
val clientStore = Amethyst.instance.nip46ClientStore
|
||||
private suspend fun loadNip46Apps(
|
||||
services: AppServices,
|
||||
signerPubKey: HexKey,
|
||||
): List<Nip46AppEntry> {
|
||||
val store = services.signerPermissionStore
|
||||
val clientStore = services.nip46ClientStore
|
||||
// allPolicies() already carries each app's policy — read it from the map instead of a second
|
||||
// loadPolicy() call per app.
|
||||
return store
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip03Timestamp
|
||||
|
||||
/** The public Esplora-compatible block explorers OTS verification can ask. */
|
||||
object BitcoinExplorerUrls {
|
||||
/** Blockstream's API; doesn't accept Tor. */
|
||||
const val BLOCKSTREAM = "https://blockstream.info/api"
|
||||
|
||||
/** mempool.space's API; accepts Tor. */
|
||||
const val MEMPOOL = "https://mempool.space/api/"
|
||||
}
|
||||
+29
@@ -129,3 +129,32 @@ data class NamecoinFallbackPolicy(
|
||||
val NONE = NamecoinFallbackPolicy()
|
||||
}
|
||||
}
|
||||
|
||||
data class RpcProbeResult(
|
||||
val success: Boolean,
|
||||
val elapsedMs: Long,
|
||||
val chain: String? = null,
|
||||
val blocks: Int? = null,
|
||||
val verificationProgress: Double? = null,
|
||||
val initialBlockDownload: Boolean? = null,
|
||||
val error: String? = null,
|
||||
/**
|
||||
* PEM-encoded server leaf certificate captured during the probe.
|
||||
* Only populated when the URL scheme is https and the TLS handshake
|
||||
* succeeded (even if the subsequent HTTP request failed). Used by
|
||||
* the Settings UI to prompt for a TOFU pin.
|
||||
*/
|
||||
val serverCertPem: String? = null,
|
||||
/**
|
||||
* SHA-256 fingerprint of the captured leaf certificate, formatted
|
||||
* as colon-separated uppercase hex bytes (matches
|
||||
* [ServerTestResult.certFingerprint] for ElectrumX).
|
||||
*/
|
||||
val certFingerprint: String? = null,
|
||||
/**
|
||||
* True when the probe failed specifically because the TLS handshake
|
||||
* was rejected (self-signed cert, untrusted CA, hostname mismatch).
|
||||
* Used by the Settings UI to suggest a TOFU pin as the fix.
|
||||
*/
|
||||
val tlsHandshakeFailed: Boolean = false,
|
||||
)
|
||||
|
||||
+3
-2
@@ -21,6 +21,7 @@
|
||||
package com.vitorpamplona.quartz.nip03Timestamp.okhttp
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.jackson.JacksonMapper
|
||||
import com.vitorpamplona.quartz.nip03Timestamp.BitcoinExplorerUrls
|
||||
import com.vitorpamplona.quartz.nip03Timestamp.ots.BitcoinExplorer
|
||||
import com.vitorpamplona.quartz.nip03Timestamp.ots.BlockHeader
|
||||
import com.vitorpamplona.quartz.nip03Timestamp.ots.OtsBlockHeightCache
|
||||
@@ -116,9 +117,9 @@ class OkHttpBitcoinExplorer(
|
||||
|
||||
companion object {
|
||||
// doesn't accept Tor
|
||||
const val BLOCKSTREAM_API_URL = "https://blockstream.info/api"
|
||||
const val BLOCKSTREAM_API_URL = BitcoinExplorerUrls.BLOCKSTREAM
|
||||
|
||||
// accepts Tor
|
||||
const val MEMPOOL_API_URL = "https://mempool.space/api/"
|
||||
const val MEMPOOL_API_URL = BitcoinExplorerUrls.MEMPOOL
|
||||
}
|
||||
}
|
||||
|
||||
-28
@@ -567,31 +567,3 @@ class NamecoinCoreRpcClient(
|
||||
}
|
||||
|
||||
/** Outcome of a `Test RPC` probe in Settings. */
|
||||
data class RpcProbeResult(
|
||||
val success: Boolean,
|
||||
val elapsedMs: Long,
|
||||
val chain: String? = null,
|
||||
val blocks: Int? = null,
|
||||
val verificationProgress: Double? = null,
|
||||
val initialBlockDownload: Boolean? = null,
|
||||
val error: String? = null,
|
||||
/**
|
||||
* PEM-encoded server leaf certificate captured during the probe.
|
||||
* Only populated when the URL scheme is https and the TLS handshake
|
||||
* succeeded (even if the subsequent HTTP request failed). Used by
|
||||
* the Settings UI to prompt for a TOFU pin.
|
||||
*/
|
||||
val serverCertPem: String? = null,
|
||||
/**
|
||||
* SHA-256 fingerprint of the captured leaf certificate, formatted
|
||||
* as colon-separated uppercase hex bytes (matches
|
||||
* [ServerTestResult.certFingerprint] for ElectrumX).
|
||||
*/
|
||||
val certFingerprint: String? = null,
|
||||
/**
|
||||
* True when the probe failed specifically because the TLS handshake
|
||||
* was rejected (self-signed cert, untrusted CA, hostname mismatch).
|
||||
* Used by the Settings UI to suggest a TOFU pin as the fix.
|
||||
*/
|
||||
val tlsHandshakeFailed: Boolean = false,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user