mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-05 19:28:25 +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.polls.results.PollResultsScreen
|
||||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.privacy.PrivacyOptionsScreen
|
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.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.publicChats.PublicChatsScreen
|
||||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.qrcode.ShowQRScreen
|
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.qrcode.ShowQRScreen
|
||||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.redirect.LoadRedirectScreen
|
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.MessagesSettingsScreen
|
||||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.MutedThreadsScreen
|
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.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.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.ProfileUiSettingsScreen
|
||||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.ReactionsSettingsScreen
|
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.SearchEngineSettingsScreen
|
||||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.SecurityFiltersScreen
|
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.SpammingUsersScreen
|
||||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.UpdateZapAmountScreen
|
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.UpdateZapAmountScreen
|
||||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.UserSettingsScreen
|
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.CordnHubScreen
|
||||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.cordn.CordnKeyPackagesScreen
|
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.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.settings.nip46.Nip46SignerScreen
|
||||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.shorts.ShortsScreen
|
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.shorts.ShortsScreen
|
||||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.softwareapps.SoftwareAppDetailScreen
|
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.nests.NestsScreen
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.NotificationScreen
|
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.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.qrcode.ScanQrImageScreen
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.nip86.RelayManagementScreen
|
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.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.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.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.CordnBackupScreen
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.cordn.CordnMigrateScreen
|
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.video.hls.NewHlsVideoScreen
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.webBookmarks.WebBookmarksScreen
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.webBookmarks.WebBookmarksScreen
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.workouts.NewWorkoutScreen
|
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.platform.MAX_VOICE_RECORD_SECONDS
|
||||||
import com.vitorpamplona.amethyst.commons.ui.note.types.EditState
|
import com.vitorpamplona.amethyst.commons.ui.note.types.EditState
|
||||||
import com.vitorpamplona.amethyst.commons.ui.richtext.InLineIconRenderer
|
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.OnchainZapSendDialog
|
||||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.wallet.navigateToReloadMint
|
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.wallet.navigateToReloadMint
|
||||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
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.service.payments.PayToAppAvailability
|
||||||
import com.vitorpamplona.amethyst.ui.actions.uploads.RecordAudioBox
|
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.PaymentTargetsDialog
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.paymentTargetStyleFor
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||||
import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent
|
import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent
|
||||||
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
|
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
|
||||||
|
|||||||
@@ -20,8 +20,6 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.amethyst.ui.note
|
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.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||||
@@ -58,7 +56,6 @@ import androidx.compose.runtime.setValue
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.input.KeyboardCapitalization
|
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
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.unit.sp
|
||||||
import androidx.compose.ui.window.Dialog
|
import androidx.compose.ui.window.Dialog
|
||||||
import androidx.compose.ui.window.DialogProperties
|
import androidx.compose.ui.window.DialogProperties
|
||||||
import androidx.core.net.toUri
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
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.StdHorzSpacer
|
||||||
import com.vitorpamplona.amethyst.commons.ui.theme.ZeroPadding
|
import com.vitorpamplona.amethyst.commons.ui.theme.ZeroPadding
|
||||||
import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText
|
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.util.showAmount
|
||||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||||
import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
|
import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
|
||||||
|
import com.vitorpamplona.quartz.utils.BigDecimal
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.coroutines.CancellationException
|
|
||||||
|
|
||||||
class ZapOptionViewModel : ViewModel() {
|
class ZapOptionViewModel : ViewModel() {
|
||||||
private var account: Account? = null
|
private var account: Account? = null
|
||||||
@@ -266,7 +264,7 @@ fun ZapCustomDialog(
|
|||||||
onClick = {
|
onClick = {
|
||||||
postViewModel.customAmount = TextFieldValue(amount.toString())
|
postViewModel.customAmount = TextFieldValue(amount.toString())
|
||||||
},
|
},
|
||||||
label = { Text("⚡ ${showAmount(amount.toBigDecimal())}") },
|
label = { Text("⚡ ${showAmount(BigDecimal(amount))}") },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -517,7 +515,7 @@ fun DisplayPayable(
|
|||||||
}
|
}
|
||||||
Row {
|
Row {
|
||||||
Text(
|
Text(
|
||||||
text = showAmount((payable.amountMilliSats / 1000.0f).toBigDecimal()),
|
text = showAmount(BigDecimal((payable.amountMilliSats / 1000.0f).toString())),
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
@@ -535,10 +533,10 @@ fun DisplayPayable(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Spacer(modifier = DoubleHorzSpacer)
|
Spacer(modifier = DoubleHorzSpacer)
|
||||||
val context = LocalContext.current
|
val walletLauncher = rememberWalletAppLauncher()
|
||||||
|
|
||||||
PayButton(isActive = !paid.value) {
|
PayButton(isActive = !paid.value) {
|
||||||
payViaIntent(payable.invoice, context, noWalletFoundStr, { paid.value = true }) {
|
walletLauncher.payInvoice(payable.invoice, noWalletFoundStr, { paid.value = true }) {
|
||||||
accountViewModel.toastManager.toast(
|
accountViewModel.toastManager.toast(
|
||||||
Res.string.error_dialog_zap_error,
|
Res.string.error_dialog_zap_error,
|
||||||
UserBasedErrorMessage(it, payable.info.user),
|
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
|
@Composable
|
||||||
fun PayButton(
|
fun PayButton(
|
||||||
isActive: Boolean,
|
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.error_dialog_pay_invoice_error
|
||||||
import com.vitorpamplona.amethyst.commons.resources.pay
|
import com.vitorpamplona.amethyst.commons.resources.pay
|
||||||
import com.vitorpamplona.amethyst.commons.resources.sats
|
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.loadStringRes
|
||||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||||
import com.vitorpamplona.amethyst.commons.ui.navigation.routes.routeFor
|
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.SmallBorder
|
||||||
import com.vitorpamplona.amethyst.commons.ui.theme.StdHorzSpacer
|
import com.vitorpamplona.amethyst.commons.ui.theme.StdHorzSpacer
|
||||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
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.PaymentCard
|
||||||
import com.vitorpamplona.amethyst.ui.components.PaymentCardAmount
|
import com.vitorpamplona.amethyst.ui.components.PaymentCardAmount
|
||||||
import com.vitorpamplona.quartz.experimental.clink.common.SatRange
|
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.commons.viewmodels.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.favorites.FavoriteAppLauncher
|
import com.vitorpamplona.amethyst.favorites.FavoriteAppLauncher
|
||||||
import com.vitorpamplona.amethyst.ui.navigation.topbars.AndroidAroundMeLocationLabel
|
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.amethyst.ui.screen.loggedIn.nests.room.activity.NestActivity
|
||||||
import com.vitorpamplona.quartz.concord.cord02Community.ImagePointer
|
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.commons.ui.navigation.bottombars.AppBottomBar as AppBottomBarImpl
|
||||||
import com.vitorpamplona.amethyst.favorites.rememberManifestIconModel as AppRememberManifestIconModel
|
import com.vitorpamplona.amethyst.favorites.rememberManifestIconModel as AppRememberManifestIconModel
|
||||||
import com.vitorpamplona.amethyst.favorites.rememberNappletIconModel as AppRememberNappletIconModel
|
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.
|
// Per flavour: Play links the hosted policies, F-Droid surfaces none.
|
||||||
override fun legalSettingsCategory(uriHandler: UriHandler): SettingsCategory? = flavorLegalSettingsCategory(uriHandler)
|
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
|
@Composable
|
||||||
override fun AppBottomBar(
|
override fun AppBottomBar(
|
||||||
selectedRoute: Route?,
|
selectedRoute: Route?,
|
||||||
|
|||||||
@@ -23,18 +23,26 @@ package com.vitorpamplona.amethyst.ui.platform
|
|||||||
import com.vitorpamplona.amethyst.Amethyst
|
import com.vitorpamplona.amethyst.Amethyst
|
||||||
import com.vitorpamplona.amethyst.commons.browser.BrowserHistoryRegistry
|
import com.vitorpamplona.amethyst.commons.browser.BrowserHistoryRegistry
|
||||||
import com.vitorpamplona.amethyst.commons.browser.BrowserIconRegistry
|
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.connectedApps.signers.NostrSignerPermissionStore
|
||||||
import com.vitorpamplona.amethyst.commons.favorites.FavoriteAppsRegistry
|
import com.vitorpamplona.amethyst.commons.favorites.FavoriteAppsRegistry
|
||||||
import com.vitorpamplona.amethyst.commons.model.location.DeviceLocation
|
import com.vitorpamplona.amethyst.commons.model.location.DeviceLocation
|
||||||
import com.vitorpamplona.amethyst.commons.model.preferences.AppPreferenceStores
|
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.napplet.permissions.NappletPermissionLedger
|
||||||
import com.vitorpamplona.amethyst.commons.service.AppServices
|
import com.vitorpamplona.amethyst.commons.service.AppServices
|
||||||
import com.vitorpamplona.amethyst.commons.service.BlossomServerFinder
|
import com.vitorpamplona.amethyst.commons.service.BlossomServerFinder
|
||||||
import com.vitorpamplona.amethyst.commons.service.ai.AltTextSuggester
|
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.commons.tor.TorSettingsFlow
|
||||||
import com.vitorpamplona.amethyst.service.ai.MLKitImageLabelService
|
import com.vitorpamplona.amethyst.service.ai.MLKitImageLabelService
|
||||||
import com.vitorpamplona.amethyst.service.location.CachedReversedGeoLocations
|
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.NamecoinNameResolver
|
||||||
|
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.RpcProbeResult
|
||||||
|
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.ServerTestResult
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
|
||||||
@@ -64,6 +72,14 @@ object AndroidAppServices : AppServices {
|
|||||||
|
|
||||||
override val namecoinResolver: NamecoinNameResolver get() = Amethyst.instance.namecoinResolver
|
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 blossomServerFinder: BlossomServerFinder = AndroidBlossomServerFinder
|
||||||
|
|
||||||
override val deviceLocation: DeviceLocation get() = Amethyst.instance.locationManager
|
override val deviceLocation: DeviceLocation get() = Amethyst.instance.locationManager
|
||||||
@@ -85,3 +101,19 @@ private object AndroidBlossomServerFinder : BlossomServerFinder {
|
|||||||
.findServers(blossomUri)
|
.findServers(blossomUri)
|
||||||
?.serverUrl
|
?.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.navigation.topbars.TopBarWithBackButton
|
||||||
import com.vitorpamplona.amethyst.commons.ui.platform.rememberManifestIconModel
|
import com.vitorpamplona.amethyst.commons.ui.platform.rememberManifestIconModel
|
||||||
import com.vitorpamplona.amethyst.commons.ui.platform.rememberWebAppIconModel
|
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.ui.stringRes
|
||||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.napplet.NappletBrokerService
|
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.descriptionRes
|
||||||
import com.vitorpamplona.amethyst.napplet.labelRes
|
import com.vitorpamplona.amethyst.napplet.labelRes
|
||||||
import com.vitorpamplona.amethyst.napplet.resolveNappletMeta
|
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.NormalizedRelayUrl
|
||||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer
|
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer
|
||||||
import com.vitorpamplona.quartz.nip42RelayAuth.RelayAuthEvent
|
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.components.util.setText
|
||||||
import com.vitorpamplona.amethyst.commons.ui.loadStringRes
|
import com.vitorpamplona.amethyst.commons.ui.loadStringRes
|
||||||
import com.vitorpamplona.amethyst.commons.ui.note.ErrorMessageDialog
|
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.screen.loggedIn.qrcode.QrCodeDrawer
|
||||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||||
import com.vitorpamplona.amethyst.commons.ui.theme.Size20Modifier
|
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 androidx.compose.ui.unit.dp
|
||||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||||
import com.vitorpamplona.amethyst.commons.ui.screen.RefresheableFeedView
|
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.commons.viewmodels.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.pinnedNotes.dal.UserProfilePinnedNotesFeedViewModel
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TabPinnedNotes(
|
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,
|
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
|
the live-activity channel, chess, Cordn group creation, the QR share and the compose
|
||||||
settings. 204 of 252 screens shared.
|
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).
|
- `ReactionsRow` (2.7k lines: pay-to-app, voice replies, wallet intents).
|
||||||
- The Nests activity and PiP.
|
- The Nests activity and PiP.
|
||||||
- The call screens.
|
- 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
|
package com.vitorpamplona.amethyst.commons.model.nip03Timestamp
|
||||||
|
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import com.vitorpamplona.quartz.nip03Timestamp.okhttp.OkHttpBitcoinExplorer
|
import com.vitorpamplona.quartz.nip03Timestamp.BitcoinExplorerUrls
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -57,8 +57,8 @@ data class OtsSettings(
|
|||||||
|
|
||||||
val KNOWN_EXPLORERS =
|
val KNOWN_EXPLORERS =
|
||||||
listOf(
|
listOf(
|
||||||
OkHttpBitcoinExplorer.MEMPOOL_API_URL to "mempool.space (Tor-friendly)",
|
BitcoinExplorerUrls.MEMPOOL to "mempool.space (Tor-friendly)",
|
||||||
OkHttpBitcoinExplorer.BLOCKSTREAM_API_URL to "blockstream.info",
|
BitcoinExplorerUrls.BLOCKSTREAM to "blockstream.info",
|
||||||
)
|
)
|
||||||
|
|
||||||
fun isValidUrl(url: String): Boolean {
|
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.BrowserHistoryRegistry
|
||||||
import com.vitorpamplona.amethyst.commons.browser.BrowserIconRegistry
|
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.connectedApps.signers.NostrSignerPermissionStore
|
||||||
import com.vitorpamplona.amethyst.commons.favorites.FavoriteAppsRegistry
|
import com.vitorpamplona.amethyst.commons.favorites.FavoriteAppsRegistry
|
||||||
import com.vitorpamplona.amethyst.commons.model.location.DeviceLocation
|
import com.vitorpamplona.amethyst.commons.model.location.DeviceLocation
|
||||||
import com.vitorpamplona.amethyst.commons.model.preferences.AppPreferenceStores
|
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.napplet.permissions.NappletPermissionLedger
|
||||||
import com.vitorpamplona.amethyst.commons.service.ai.AltTextSuggester
|
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.commons.tor.TorSettingsFlow
|
||||||
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.NamecoinNameResolver
|
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.NamecoinNameResolver
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
@@ -64,6 +68,18 @@ interface AppServices {
|
|||||||
/** Resolves `.bit` names and `d/`/`id/` identifiers over the configured ElectrumX servers. */
|
/** Resolves `.bit` names and `d/`/`id/` identifiers over the configured ElectrumX servers. */
|
||||||
val namecoinResolver: NamecoinNameResolver
|
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). */
|
/** Finds the server behind a `blossom:` URI (BUD-10). */
|
||||||
val blossomServerFinder: BlossomServerFinder get() = BlossomServerFinder.None
|
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
|
* 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.
|
* 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.model.Account
|
||||||
import com.vitorpamplona.amethyst.commons.tor.MoneyOpRelayRouting
|
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.CancellationException
|
||||||
import kotlinx.coroutines.CompletableDeferred
|
import kotlinx.coroutines.CompletableDeferred
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.IO
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.coroutines.withTimeoutOrNull
|
import kotlinx.coroutines.withTimeoutOrNull
|
||||||
|
|
||||||
+6
-7
@@ -18,9 +18,8 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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.foundation.combinedClickable
|
||||||
import androidx.compose.material3.LocalTextStyle
|
import androidx.compose.material3.LocalTextStyle
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
@@ -30,12 +29,12 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalClipboard
|
import androidx.compose.ui.platform.LocalClipboard
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||||
import com.vitorpamplona.amethyst.commons.resources.copied_to_clipboard
|
import com.vitorpamplona.amethyst.commons.resources.copied_to_clipboard
|
||||||
import com.vitorpamplona.amethyst.commons.resources.copy_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.components.util.setText
|
||||||
import com.vitorpamplona.amethyst.commons.ui.loadStringRes
|
import com.vitorpamplona.amethyst.commons.ui.loadStringRes
|
||||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
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
|
* 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].
|
* 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
|
* 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
|
* @param copyValue raw value placed on the clipboard on long-press
|
||||||
* (typically the full, unmodified profile field).
|
* (typically the full, unmodified profile field).
|
||||||
* @param onClick tap action — usually "open the link" or "expand zap UI".
|
* @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
|
* value is placed on the clipboard. Defaults to a generic
|
||||||
* "Copied to clipboard" message.
|
* "Copied to clipboard" message.
|
||||||
* @param onLongClickLabelResId accessibility label exposed to TalkBack for
|
* @param onLongClickLabelResId accessibility label exposed to TalkBack for
|
||||||
@@ -78,7 +77,7 @@ fun LongPressCopyText(
|
|||||||
toastResId: StringResource = Res.string.copied_to_clipboard,
|
toastResId: StringResource = Res.string.copied_to_clipboard,
|
||||||
onLongClickLabelResId: StringResource = Res.string.copy_to_clipboard,
|
onLongClickLabelResId: StringResource = Res.string.copy_to_clipboard,
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val shortNotice = rememberShortNotice()
|
||||||
val clipboard = LocalClipboard.current
|
val clipboard = LocalClipboard.current
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val longClickLabel = stringRes(onLongClickLabelResId)
|
val longClickLabel = stringRes(onLongClickLabelResId)
|
||||||
@@ -96,7 +95,7 @@ fun LongPressCopyText(
|
|||||||
onLongClick = {
|
onLongClick = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
clipboard.setText(copyValue)
|
clipboard.setText(copyValue)
|
||||||
Toast.makeText(context, loadStringRes(toastResId), Toast.LENGTH_SHORT).show()
|
shortNotice.show(loadStringRes(toastResId))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLongClickLabel = longClickLabel,
|
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.ui.settings.SettingsCategory
|
||||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||||
import com.vitorpamplona.quartz.concord.cord02Community.ImagePointer
|
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
|
* 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. */
|
/** The settings screen's legal links, when the build's distribution calls for them. */
|
||||||
fun legalSettingsCategory(uriHandler: UriHandler): SettingsCategory? = null
|
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
|
@Composable
|
||||||
fun AppBottomBar(
|
fun AppBottomBar(
|
||||||
selectedRoute: Route?,
|
selectedRoute: Route?,
|
||||||
@@ -58,6 +67,10 @@ interface AppPlatform {
|
|||||||
@Composable
|
@Composable
|
||||||
fun AroundMeLocationLabel() {}
|
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. */
|
/** A full-screen dialog to pick a place (map, search, "use my location"), as a geohash. */
|
||||||
@Composable
|
@Composable
|
||||||
fun GeohashLocationPickerDialog(
|
fun GeohashLocationPickerDialog(
|
||||||
@@ -135,6 +148,9 @@ fun AppBottomBar(
|
|||||||
@Composable
|
@Composable
|
||||||
fun AroundMeLocationLabel() = LocalAppPlatform.current.AroundMeLocationLabel()
|
fun AroundMeLocationLabel() = LocalAppPlatform.current.AroundMeLocationLabel()
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SpeakNameButton(name: String) = LocalAppPlatform.current.SpeakNameButton(name)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun GeohashLocationPickerDialog(
|
fun GeohashLocationPickerDialog(
|
||||||
initialGeohash: String?,
|
initialGeohash: String?,
|
||||||
|
|||||||
+47
-47
@@ -18,7 +18,7 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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.ScrollState
|
||||||
import androidx.compose.foundation.gestures.scrollBy
|
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.platform.LocalDensity
|
||||||
import androidx.compose.ui.unit.IntSize
|
import androidx.compose.ui.unit.IntSize
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
||||||
import com.vitorpamplona.amethyst.commons.model.User
|
import com.vitorpamplona.amethyst.commons.model.User
|
||||||
import com.vitorpamplona.amethyst.commons.model.cache.LocalCache
|
import com.vitorpamplona.amethyst.commons.model.cache.LocalCache
|
||||||
import com.vitorpamplona.amethyst.commons.model.navigation.Route
|
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.notes
|
||||||
import com.vitorpamplona.amethyst.commons.resources.profile_tab_apps
|
import com.vitorpamplona.amethyst.commons.resources.profile_tab_apps
|
||||||
import com.vitorpamplona.amethyst.commons.resources.replies
|
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.UserBlockedFeed
|
||||||
import com.vitorpamplona.amethyst.commons.ui.feeds.WatchLifecycleAndUpdateModel
|
import com.vitorpamplona.amethyst.commons.ui.feeds.WatchLifecycleAndUpdateModel
|
||||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||||
import com.vitorpamplona.amethyst.commons.ui.platform.AppBottomBar
|
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.stringRes
|
||||||
import com.vitorpamplona.amethyst.commons.ui.theme.DividerThickness
|
import com.vitorpamplona.amethyst.commons.ui.theme.DividerThickness
|
||||||
import com.vitorpamplona.amethyst.commons.ui.theme.Size8dp
|
import com.vitorpamplona.amethyst.commons.ui.theme.Size8dp
|
||||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.commons.viewmodels.UserExternalIdentitiesViewModel
|
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
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -154,7 +154,7 @@ fun PrepareViewModels(
|
|||||||
nav: INav,
|
nav: INav,
|
||||||
) {
|
) {
|
||||||
val followsFeedViewModel: UserProfileFollowsUserFeedViewModel =
|
val followsFeedViewModel: UserProfileFollowsUserFeedViewModel =
|
||||||
viewModel(
|
rememberViewModel(
|
||||||
key = baseUser.pubkeyHex + "UserProfileFollowsUserFeedViewModel",
|
key = baseUser.pubkeyHex + "UserProfileFollowsUserFeedViewModel",
|
||||||
factory =
|
factory =
|
||||||
UserProfileFollowsUserFeedViewModel.Factory(
|
UserProfileFollowsUserFeedViewModel.Factory(
|
||||||
@@ -164,7 +164,7 @@ fun PrepareViewModels(
|
|||||||
)
|
)
|
||||||
|
|
||||||
val galleryFeedViewModel: UserProfileGalleryFeedViewModel =
|
val galleryFeedViewModel: UserProfileGalleryFeedViewModel =
|
||||||
viewModel(
|
rememberViewModel(
|
||||||
key = baseUser.pubkeyHex + "UserGalleryFeedViewModel",
|
key = baseUser.pubkeyHex + "UserGalleryFeedViewModel",
|
||||||
factory =
|
factory =
|
||||||
UserProfileGalleryFeedViewModel.Factory(
|
UserProfileGalleryFeedViewModel.Factory(
|
||||||
@@ -174,7 +174,7 @@ fun PrepareViewModels(
|
|||||||
)
|
)
|
||||||
|
|
||||||
val followersFeedViewModel: UserProfileFollowersUserFeedViewModel =
|
val followersFeedViewModel: UserProfileFollowersUserFeedViewModel =
|
||||||
viewModel(
|
rememberViewModel(
|
||||||
key = baseUser.pubkeyHex + "UserProfileFollowersUserFeedViewModel",
|
key = baseUser.pubkeyHex + "UserProfileFollowersUserFeedViewModel",
|
||||||
factory =
|
factory =
|
||||||
UserProfileFollowersUserFeedViewModel.Factory(
|
UserProfileFollowersUserFeedViewModel.Factory(
|
||||||
@@ -184,26 +184,26 @@ fun PrepareViewModels(
|
|||||||
)
|
)
|
||||||
|
|
||||||
val appRecommendations: UserAppRecommendationsFeedViewModel =
|
val appRecommendations: UserAppRecommendationsFeedViewModel =
|
||||||
viewModel(
|
rememberViewModel(
|
||||||
key = baseUser.pubkeyHex + "UserAppRecommendationsFeedViewModel",
|
key = baseUser.pubkeyHex + "UserAppRecommendationsFeedViewModel",
|
||||||
factory = UserAppRecommendationsFeedViewModel.Factory(baseUser),
|
factory = UserAppRecommendationsFeedViewModel.Factory(baseUser),
|
||||||
)
|
)
|
||||||
|
|
||||||
val externalIdentities: UserExternalIdentitiesViewModel =
|
val externalIdentities: UserExternalIdentitiesViewModel =
|
||||||
viewModel(
|
rememberViewModel(
|
||||||
key = baseUser.pubkeyHex + "UserExternalIdentitiesViewModel",
|
key = baseUser.pubkeyHex + "UserExternalIdentitiesViewModel",
|
||||||
factory = UserExternalIdentitiesViewModel.Factory(baseUser),
|
factory = UserExternalIdentitiesViewModel.Factory(baseUser),
|
||||||
)
|
)
|
||||||
|
|
||||||
val zapFeedViewModel: UserProfileZapsViewModel =
|
val zapFeedViewModel: UserProfileZapsViewModel =
|
||||||
viewModel(
|
rememberViewModel(
|
||||||
key = baseUser.pubkeyHex + "UserProfileZapsFeedViewModel",
|
key = baseUser.pubkeyHex + "UserProfileZapsFeedViewModel",
|
||||||
factory =
|
factory =
|
||||||
UserProfileZapsViewModel.Factory(baseUser, accountViewModel.account),
|
UserProfileZapsViewModel.Factory(baseUser, accountViewModel.account),
|
||||||
)
|
)
|
||||||
|
|
||||||
val threadsViewModel: UserProfileNewThreadsFeedViewModel =
|
val threadsViewModel: UserProfileNewThreadsFeedViewModel =
|
||||||
viewModel(
|
rememberViewModel(
|
||||||
key = baseUser.pubkeyHex + "UserProfileNewThreadsFeedViewModel",
|
key = baseUser.pubkeyHex + "UserProfileNewThreadsFeedViewModel",
|
||||||
factory =
|
factory =
|
||||||
UserProfileNewThreadsFeedViewModel.Factory(
|
UserProfileNewThreadsFeedViewModel.Factory(
|
||||||
@@ -213,7 +213,7 @@ fun PrepareViewModels(
|
|||||||
)
|
)
|
||||||
|
|
||||||
val repliesViewModel: UserProfileConversationsFeedViewModel =
|
val repliesViewModel: UserProfileConversationsFeedViewModel =
|
||||||
viewModel(
|
rememberViewModel(
|
||||||
key = baseUser.pubkeyHex + "UserProfileConversationsFeedViewModel",
|
key = baseUser.pubkeyHex + "UserProfileConversationsFeedViewModel",
|
||||||
factory =
|
factory =
|
||||||
UserProfileConversationsFeedViewModel.Factory(
|
UserProfileConversationsFeedViewModel.Factory(
|
||||||
@@ -223,7 +223,7 @@ fun PrepareViewModels(
|
|||||||
)
|
)
|
||||||
|
|
||||||
val mutualViewModel: UserProfileMutualFeedViewModel =
|
val mutualViewModel: UserProfileMutualFeedViewModel =
|
||||||
viewModel(
|
rememberViewModel(
|
||||||
key = baseUser.pubkeyHex + "UserProfileMutualFeedViewModel",
|
key = baseUser.pubkeyHex + "UserProfileMutualFeedViewModel",
|
||||||
factory =
|
factory =
|
||||||
UserProfileMutualFeedViewModel.Factory(
|
UserProfileMutualFeedViewModel.Factory(
|
||||||
@@ -233,7 +233,7 @@ fun PrepareViewModels(
|
|||||||
)
|
)
|
||||||
|
|
||||||
val bookmarksFeedViewModel: UserProfileBookmarksFeedViewModel =
|
val bookmarksFeedViewModel: UserProfileBookmarksFeedViewModel =
|
||||||
viewModel(
|
rememberViewModel(
|
||||||
key = baseUser.pubkeyHex + "UserProfileBookmarksFeedViewModel",
|
key = baseUser.pubkeyHex + "UserProfileBookmarksFeedViewModel",
|
||||||
factory =
|
factory =
|
||||||
UserProfileBookmarksFeedViewModel.Factory(
|
UserProfileBookmarksFeedViewModel.Factory(
|
||||||
@@ -243,7 +243,7 @@ fun PrepareViewModels(
|
|||||||
)
|
)
|
||||||
|
|
||||||
val pinnedNotesFeedViewModel: UserProfilePinnedNotesFeedViewModel =
|
val pinnedNotesFeedViewModel: UserProfilePinnedNotesFeedViewModel =
|
||||||
viewModel(
|
rememberViewModel(
|
||||||
key = baseUser.pubkeyHex + "UserProfilePinnedNotesFeedViewModel",
|
key = baseUser.pubkeyHex + "UserProfilePinnedNotesFeedViewModel",
|
||||||
factory =
|
factory =
|
||||||
UserProfilePinnedNotesFeedViewModel.Factory(
|
UserProfilePinnedNotesFeedViewModel.Factory(
|
||||||
@@ -253,7 +253,7 @@ fun PrepareViewModels(
|
|||||||
)
|
)
|
||||||
|
|
||||||
val reportsFeedViewModel: UserProfileReportFeedViewModel =
|
val reportsFeedViewModel: UserProfileReportFeedViewModel =
|
||||||
viewModel(
|
rememberViewModel(
|
||||||
key = baseUser.pubkeyHex + "UserProfileReportFeedViewModel",
|
key = baseUser.pubkeyHex + "UserProfileReportFeedViewModel",
|
||||||
factory =
|
factory =
|
||||||
UserProfileReportFeedViewModel.Factory(
|
UserProfileReportFeedViewModel.Factory(
|
||||||
@@ -263,7 +263,7 @@ fun PrepareViewModels(
|
|||||||
)
|
)
|
||||||
|
|
||||||
val appsFeedViewModel: UserProfileAppsFeedViewModel =
|
val appsFeedViewModel: UserProfileAppsFeedViewModel =
|
||||||
viewModel(
|
rememberViewModel(
|
||||||
key = baseUser.pubkeyHex + "UserProfileAppsFeedViewModel",
|
key = baseUser.pubkeyHex + "UserProfileAppsFeedViewModel",
|
||||||
factory =
|
factory =
|
||||||
UserProfileAppsFeedViewModel.Factory(
|
UserProfileAppsFeedViewModel.Factory(
|
||||||
+2
-2
@@ -18,7 +18,7 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
@@ -29,8 +29,8 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||||
import com.vitorpamplona.amethyst.commons.ui.screen.RefresheableFeedView
|
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.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;
|
* 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
|
* 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.
|
* 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.AdditiveFeedFilter
|
||||||
import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams
|
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
|
* 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.
|
* 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.compose.runtime.Stable
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.viewmodel.CreationExtras
|
||||||
import com.vitorpamplona.amethyst.commons.model.Account
|
import com.vitorpamplona.amethyst.commons.model.Account
|
||||||
import com.vitorpamplona.amethyst.commons.model.User
|
import com.vitorpamplona.amethyst.commons.model.User
|
||||||
import com.vitorpamplona.amethyst.commons.ui.screen.AndroidFeedViewModel
|
import com.vitorpamplona.amethyst.commons.ui.screen.AndroidFeedViewModel
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
class UserProfileAppsFeedViewModel(
|
class UserProfileAppsFeedViewModel(
|
||||||
@@ -37,6 +39,9 @@ class UserProfileAppsFeedViewModel(
|
|||||||
val account: Account,
|
val account: Account,
|
||||||
) : ViewModelProvider.Factory {
|
) : ViewModelProvider.Factory {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@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
|
* 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.
|
* 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.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
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
|
* 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.
|
* 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.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
@@ -29,8 +29,8 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||||
import com.vitorpamplona.amethyst.commons.ui.screen.RefresheableFeedView
|
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.commons.viewmodels.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.bookmarks.dal.UserProfileBookmarksFeedViewModel
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TabBookmarks(
|
fun TabBookmarks(
|
||||||
+1
-1
@@ -18,7 +18,7 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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.feeds.FeedFilter
|
||||||
import com.vitorpamplona.amethyst.commons.model.Account
|
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
|
* 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.
|
* 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.compose.runtime.Stable
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.viewmodel.CreationExtras
|
||||||
import com.vitorpamplona.amethyst.commons.model.Account
|
import com.vitorpamplona.amethyst.commons.model.Account
|
||||||
import com.vitorpamplona.amethyst.commons.model.User
|
import com.vitorpamplona.amethyst.commons.model.User
|
||||||
import com.vitorpamplona.amethyst.commons.ui.screen.AndroidFeedViewModel
|
import com.vitorpamplona.amethyst.commons.ui.screen.AndroidFeedViewModel
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
class UserProfileBookmarksFeedViewModel(
|
class UserProfileBookmarksFeedViewModel(
|
||||||
@@ -37,6 +39,9 @@ class UserProfileBookmarksFeedViewModel(
|
|||||||
val account: Account,
|
val account: Account,
|
||||||
) : ViewModelProvider.Factory {
|
) : ViewModelProvider.Factory {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@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
|
* 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.
|
* 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.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
@@ -26,8 +26,8 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||||
import com.vitorpamplona.amethyst.commons.ui.screen.RefresheableFeedView
|
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.commons.viewmodels.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.conversations.dal.UserProfileConversationsFeedViewModel
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TabNotesConversations(
|
fun TabNotesConversations(
|
||||||
+1
-1
@@ -20,7 +20,7 @@
|
|||||||
*/
|
*/
|
||||||
@file:Suppress("DEPRECATION")
|
@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.AdditiveFeedFilter
|
||||||
import com.vitorpamplona.amethyst.commons.feeds.sortedByDefaultFeedOrder
|
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
|
* 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.
|
* 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.compose.runtime.Stable
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.viewmodel.CreationExtras
|
||||||
import com.vitorpamplona.amethyst.commons.model.Account
|
import com.vitorpamplona.amethyst.commons.model.Account
|
||||||
import com.vitorpamplona.amethyst.commons.model.User
|
import com.vitorpamplona.amethyst.commons.model.User
|
||||||
import com.vitorpamplona.amethyst.commons.ui.screen.AndroidFeedViewModel
|
import com.vitorpamplona.amethyst.commons.ui.screen.AndroidFeedViewModel
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
class UserProfileConversationsFeedViewModel(
|
class UserProfileConversationsFeedViewModel(
|
||||||
@@ -37,6 +39,9 @@ class UserProfileConversationsFeedViewModel(
|
|||||||
val account: Account,
|
val account: Account,
|
||||||
) : ViewModelProvider.Factory {
|
) : ViewModelProvider.Factory {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@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
|
* 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.
|
* 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.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
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.relayClient.user.observeUserAssertionsFollowerCount
|
||||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||||
import com.vitorpamplona.amethyst.commons.resources.number_followers
|
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.ui.stringRes
|
||||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.followers.dal.UserProfileFollowersUserFeedViewModel
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun FollowersTabHeader(
|
fun FollowersTabHeader(
|
||||||
+2
-2
@@ -18,7 +18,7 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
@@ -33,10 +33,10 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||||
import com.vitorpamplona.amethyst.commons.ui.note.UserCompose
|
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.DividerThickness
|
||||||
import com.vitorpamplona.amethyst.commons.ui.theme.FeedPadding
|
import com.vitorpamplona.amethyst.commons.ui.theme.FeedPadding
|
||||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.followers.dal.UserProfileFollowersUserFeedViewModel
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TabFollowers(
|
fun TabFollowers(
|
||||||
+8
-2
@@ -18,24 +18,27 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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.compose.runtime.Stable
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import androidx.lifecycle.viewmodel.CreationExtras
|
||||||
import com.vitorpamplona.amethyst.commons.model.Account
|
import com.vitorpamplona.amethyst.commons.model.Account
|
||||||
import com.vitorpamplona.amethyst.commons.model.User
|
import com.vitorpamplona.amethyst.commons.model.User
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||||
import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent
|
import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.IO
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.flowOn
|
import kotlinx.coroutines.flow.flowOn
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.sample
|
import kotlinx.coroutines.flow.sample
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
class UserProfileFollowersUserFeedViewModel(
|
class UserProfileFollowersUserFeedViewModel(
|
||||||
@@ -92,6 +95,9 @@ class UserProfileFollowersUserFeedViewModel(
|
|||||||
val account: Account,
|
val account: Account,
|
||||||
) : ViewModelProvider.Factory {
|
) : ViewModelProvider.Factory {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@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
|
* 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.
|
* 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.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@@ -26,9 +26,9 @@ import androidx.compose.runtime.getValue
|
|||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||||
import com.vitorpamplona.amethyst.commons.resources.number_following
|
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.ui.stringRes
|
||||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.follows.dal.UserProfileFollowsUserFeedViewModel
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun FollowTabHeader(
|
fun FollowTabHeader(
|
||||||
+2
-2
@@ -18,7 +18,7 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
@@ -33,10 +33,10 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||||
import com.vitorpamplona.amethyst.commons.ui.note.UserCompose
|
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.DividerThickness
|
||||||
import com.vitorpamplona.amethyst.commons.ui.theme.FeedPadding
|
import com.vitorpamplona.amethyst.commons.ui.theme.FeedPadding
|
||||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.follows.dal.UserProfileFollowsUserFeedViewModel
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TabFollows(
|
fun TabFollows(
|
||||||
+8
-2
@@ -18,17 +18,19 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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.compose.runtime.Stable
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import androidx.lifecycle.viewmodel.CreationExtras
|
||||||
import com.vitorpamplona.amethyst.commons.model.Account
|
import com.vitorpamplona.amethyst.commons.model.Account
|
||||||
import com.vitorpamplona.amethyst.commons.model.User
|
import com.vitorpamplona.amethyst.commons.model.User
|
||||||
import com.vitorpamplona.amethyst.commons.model.cache.LocalCache
|
import com.vitorpamplona.amethyst.commons.model.cache.LocalCache
|
||||||
import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent
|
import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.IO
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.flowOn
|
import kotlinx.coroutines.flow.flowOn
|
||||||
@@ -36,6 +38,7 @@ import kotlinx.coroutines.flow.map
|
|||||||
import kotlinx.coroutines.flow.onStart
|
import kotlinx.coroutines.flow.onStart
|
||||||
import kotlinx.coroutines.flow.sample
|
import kotlinx.coroutines.flow.sample
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
class UserProfileFollowsUserFeedViewModel(
|
class UserProfileFollowsUserFeedViewModel(
|
||||||
@@ -89,6 +92,9 @@ class UserProfileFollowsUserFeedViewModel(
|
|||||||
val account: Account,
|
val account: Account,
|
||||||
) : ViewModelProvider.Factory {
|
) : ViewModelProvider.Factory {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@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
|
* 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.
|
* 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.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.combinedClickable
|
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
|
* 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.
|
* 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.animation.core.tween
|
||||||
import androidx.compose.foundation.layout.Arrangement.spacedBy
|
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
|
* 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.
|
* 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.Composable
|
||||||
import androidx.compose.runtime.mutableStateOf
|
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
|
* 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.
|
* 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.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
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.feeds.ScrollStateKeys
|
||||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||||
import com.vitorpamplona.amethyst.commons.ui.screen.SaveableGridFeedState
|
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.commons.viewmodels.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.gallery.dal.UserProfileGalleryFeedViewModel
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TabGallery(
|
fun TabGallery(
|
||||||
+1
-1
@@ -18,7 +18,7 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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.AdditiveFeedFilter
|
||||||
import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams
|
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
|
* 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.
|
* 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.compose.runtime.Stable
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.viewmodel.CreationExtras
|
||||||
import com.vitorpamplona.amethyst.commons.model.Account
|
import com.vitorpamplona.amethyst.commons.model.Account
|
||||||
import com.vitorpamplona.amethyst.commons.model.User
|
import com.vitorpamplona.amethyst.commons.model.User
|
||||||
import com.vitorpamplona.amethyst.commons.ui.screen.AndroidFeedViewModel
|
import com.vitorpamplona.amethyst.commons.ui.screen.AndroidFeedViewModel
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
class UserProfileGalleryFeedViewModel(
|
class UserProfileGalleryFeedViewModel(
|
||||||
@@ -37,6 +39,9 @@ class UserProfileGalleryFeedViewModel(
|
|||||||
val account: Account,
|
val account: Account,
|
||||||
) : ViewModelProvider.Factory {
|
) : ViewModelProvider.Factory {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@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
|
* 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.
|
* 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.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
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
|
* 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.
|
* 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.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
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
|
* 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.
|
* 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.Composable
|
||||||
import androidx.compose.runtime.getValue
|
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
|
* 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.
|
* 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.Composable
|
||||||
import androidx.compose.runtime.remember
|
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.error_dialog_payment_error
|
||||||
import com.vitorpamplona.amethyst.commons.resources.no_payment_app_found_for_type
|
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.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.BitcoinOrange
|
||||||
import com.vitorpamplona.amethyst.commons.ui.theme.Size16Modifier
|
import com.vitorpamplona.amethyst.commons.ui.theme.Size16Modifier
|
||||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
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.nipA3PaymentTargets.PaymentTarget
|
||||||
import com.vitorpamplona.quartz.nipBCOnchainZaps.taproot.SegwitAddress
|
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
|
* 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.
|
* 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.Column
|
||||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
@@ -40,16 +39,13 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.ClipEntry
|
|
||||||
import androidx.compose.ui.platform.LocalClipboard
|
import androidx.compose.ui.platform.LocalClipboard
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.platform.LocalUriHandler
|
import androidx.compose.ui.platform.LocalUriHandler
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.vitorpamplona.amethyst.R
|
|
||||||
import com.vitorpamplona.amethyst.commons.buzz.ui.BuzzAgentLabel
|
import com.vitorpamplona.amethyst.commons.buzz.ui.BuzzAgentLabel
|
||||||
import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
|
import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
|
||||||
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
|
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.telegram
|
||||||
import com.vitorpamplona.amethyst.commons.resources.twitter
|
import com.vitorpamplona.amethyst.commons.resources.twitter
|
||||||
import com.vitorpamplona.amethyst.commons.resources.website
|
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.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.navigation.navs.INav
|
||||||
import com.vitorpamplona.amethyst.commons.ui.note.ObserveAndRenderNIP05VerifiedSymbol
|
import com.vitorpamplona.amethyst.commons.ui.note.ObserveAndRenderNIP05VerifiedSymbol
|
||||||
import com.vitorpamplona.amethyst.commons.ui.note.lastSeenSentence
|
import com.vitorpamplona.amethyst.commons.ui.note.lastSeenSentence
|
||||||
import com.vitorpamplona.amethyst.commons.ui.painterRes
|
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.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.stringRes
|
||||||
import com.vitorpamplona.amethyst.commons.ui.theme.Size15Modifier
|
import com.vitorpamplona.amethyst.commons.ui.theme.Size15Modifier
|
||||||
import com.vitorpamplona.amethyst.commons.ui.theme.SpacedBy3dp
|
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.util.toShortDisplay
|
||||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.commons.viewmodels.UserExternalIdentitiesViewModel
|
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.core.Event
|
||||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||||
import com.vitorpamplona.quartz.nip39ExtIdentities.GitHubIdentity
|
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.TelegramIdentity
|
||||||
import com.vitorpamplona.quartz.nip39ExtIdentities.TwitterIdentity
|
import com.vitorpamplona.quartz.nip39ExtIdentities.TwitterIdentity
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.IO
|
||||||
import kotlinx.coroutines.flow.flowOn
|
import kotlinx.coroutines.flow.flowOn
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import org.jetbrains.compose.resources.DrawableResource
|
||||||
import org.jetbrains.compose.resources.StringResource
|
import org.jetbrains.compose.resources.StringResource
|
||||||
|
|
||||||
private const val IDENTITY_ICON_CACHE_KEY = 0
|
private const val IDENTITY_ICON_CACHE_KEY = 0
|
||||||
@@ -151,7 +150,7 @@ fun DrawAdditionalInfo(
|
|||||||
Spacer(StdHorzSpacer)
|
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,
|
// 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),
|
modifier = Modifier.size(23.dp).padding(start = 5.dp),
|
||||||
onClick = {
|
onClick = {
|
||||||
scope.launch {
|
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),
|
modifier = Modifier.size(23.dp).padding(start = 5.dp),
|
||||||
onClick = {
|
onClick = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
clipboard.setClipEntry(ClipEntry(ClipData.newPlainText("nprofile", baseUser.toNProfile())))
|
clipboard.setText(baseUser.toNProfile())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
@@ -271,7 +270,7 @@ fun DrawAdditionalInfo(
|
|||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
Icon(
|
Icon(
|
||||||
tint = Color.Unspecified,
|
tint = Color.Unspecified,
|
||||||
painter = painterRes(resourceId = getIdentityClaimIcon(identity), IDENTITY_ICON_CACHE_KEY),
|
painter = painterRes(getIdentityClaimIcon(identity), IDENTITY_ICON_CACHE_KEY),
|
||||||
contentDescription = stringRes(getIdentityClaimDescription(identity)),
|
contentDescription = stringRes(getIdentityClaimDescription(identity)),
|
||||||
modifier = Modifier.size(18.dp),
|
modifier = Modifier.size(18.dp),
|
||||||
)
|
)
|
||||||
@@ -341,7 +340,6 @@ fun DisplayLastSeen(
|
|||||||
val lastSeen by lastSeenFlow.collectAsStateWithLifecycle(null)
|
val lastSeen by lastSeenFlow.collectAsStateWithLifecycle(null)
|
||||||
|
|
||||||
lastSeen?.let { timestamp ->
|
lastSeen?.let { timestamp ->
|
||||||
val context = LocalContext.current
|
|
||||||
Text(
|
Text(
|
||||||
text = lastSeenSentence(timestamp),
|
text = lastSeenSentence(timestamp),
|
||||||
color = MaterialTheme.colorScheme.placeholderText,
|
color = MaterialTheme.colorScheme.placeholderText,
|
||||||
@@ -384,13 +382,13 @@ fun DisplayNip05ProfileStatus(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getIdentityClaimIcon(identity: IdentityClaimTag): Int =
|
fun getIdentityClaimIcon(identity: IdentityClaimTag): DrawableResource =
|
||||||
when (identity) {
|
when (identity) {
|
||||||
is TwitterIdentity -> R.drawable.x
|
is TwitterIdentity -> Res.drawable.x
|
||||||
is TelegramIdentity -> R.drawable.telegram
|
is TelegramIdentity -> Res.drawable.telegram
|
||||||
is MastodonIdentity -> R.drawable.mastodon
|
is MastodonIdentity -> Res.drawable.mastodon
|
||||||
is GitHubIdentity -> R.drawable.github
|
is GitHubIdentity -> Res.drawable.github
|
||||||
else -> R.drawable.github
|
else -> Res.drawable.github
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getIdentityClaimDescription(identity: IdentityClaimTag): StringResource =
|
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
|
* 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.
|
* 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.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.Image
|
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
|
* 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.
|
* 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.padding
|
||||||
import androidx.compose.foundation.layout.width
|
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
|
* 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.
|
* 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.Composable
|
||||||
import androidx.compose.runtime.derivedStateOf
|
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
|
* 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.
|
* 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.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
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.geometry.Rect
|
||||||
import androidx.compose.ui.layout.boundsInWindow
|
import androidx.compose.ui.layout.boundsInWindow
|
||||||
import androidx.compose.ui.layout.onGloballyPositioned
|
import androidx.compose.ui.layout.onGloballyPositioned
|
||||||
import androidx.compose.ui.platform.ClipEntry
|
|
||||||
import androidx.compose.ui.platform.LocalClipboard
|
import androidx.compose.ui.platform.LocalClipboard
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
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.Icon
|
||||||
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
|
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
|
||||||
import com.vitorpamplona.amethyst.commons.model.User
|
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.NewUserMetadataViewModel
|
||||||
import com.vitorpamplona.amethyst.commons.ui.actions.uploads.GallerySelectSingle
|
import com.vitorpamplona.amethyst.commons.ui.actions.uploads.GallerySelectSingle
|
||||||
import com.vitorpamplona.amethyst.commons.ui.components.LoadingAnimation
|
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.navigation.navs.INav
|
||||||
import com.vitorpamplona.amethyst.commons.ui.note.ClickableUserPicture
|
import com.vitorpamplona.amethyst.commons.ui.note.ClickableUserPicture
|
||||||
import com.vitorpamplona.amethyst.commons.ui.note.platform.ZoomableImageDialog
|
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.stringRes
|
||||||
import com.vitorpamplona.amethyst.commons.ui.theme.Size100dp
|
import com.vitorpamplona.amethyst.commons.ui.theme.Size100dp
|
||||||
import com.vitorpamplona.amethyst.commons.ui.theme.Size35dp
|
import com.vitorpamplona.amethyst.commons.ui.theme.Size35dp
|
||||||
import com.vitorpamplona.amethyst.commons.ui.theme.userProfileBorderModifier
|
import com.vitorpamplona.amethyst.commons.ui.theme.userProfileBorderModifier
|
||||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.commons.viewmodels.UserExternalIdentitiesViewModel
|
import com.vitorpamplona.amethyst.commons.viewmodels.UserExternalIdentitiesViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.apps.UserAppRecommendationsFeedViewModel
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -148,8 +147,7 @@ fun ZoomableUserPicture(
|
|||||||
onLongClick = {
|
onLongClick = {
|
||||||
baseUser.profilePicture()?.let { it1 ->
|
baseUser.profilePicture()?.let { it1 ->
|
||||||
scope.launch {
|
scope.launch {
|
||||||
val clipData = ClipData.newPlainText("profile picture url", it1)
|
clipboardManager.setText(it1)
|
||||||
clipboardManager.setClipEntry(ClipEntry(clipData))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -191,7 +189,7 @@ private fun ProfilePictureUploadButton(
|
|||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
size: Dp,
|
size: Dp,
|
||||||
) {
|
) {
|
||||||
val postViewModel: NewUserMetadataViewModel = viewModel()
|
val postViewModel: NewUserMetadataViewModel = rememberViewModel { NewUserMetadataViewModel() }
|
||||||
postViewModel.init(accountViewModel)
|
postViewModel.init(accountViewModel)
|
||||||
|
|
||||||
var showGallerySelect by remember { mutableStateOf(false) }
|
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
|
* 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.
|
* 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.BorderStroke
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.combinedClickable
|
import androidx.compose.foundation.combinedClickable
|
||||||
@@ -44,7 +43,6 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalClipboard
|
import androidx.compose.ui.platform.LocalClipboard
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
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_cashu
|
||||||
import com.vitorpamplona.amethyst.commons.resources.send_payment_method_lightning
|
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.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.components.util.setText
|
||||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||||
import com.vitorpamplona.amethyst.commons.ui.note.LoadAddressableNote
|
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.Bolt12OffersDialog
|
||||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.profile.header.abbreviateBolt12Offer
|
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.stringRes
|
||||||
import com.vitorpamplona.amethyst.commons.ui.theme.BitcoinOrange
|
import com.vitorpamplona.amethyst.commons.ui.theme.BitcoinOrange
|
||||||
import com.vitorpamplona.amethyst.commons.ui.theme.Size16Modifier
|
import com.vitorpamplona.amethyst.commons.ui.theme.Size16Modifier
|
||||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
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.experimental.clink.pointers.NOffer
|
||||||
import com.vitorpamplona.quartz.nipA3PaymentTargets.PaymentTarget
|
import com.vitorpamplona.quartz.nipA3PaymentTargets.PaymentTarget
|
||||||
import com.vitorpamplona.quartz.nipA3PaymentTargets.PaymentTargetsEvent
|
import com.vitorpamplona.quartz.nipA3PaymentTargets.PaymentTargetsEvent
|
||||||
@@ -315,7 +314,7 @@ fun ProfilePaymentChip(
|
|||||||
copyValue: String? = null,
|
copyValue: String? = null,
|
||||||
icon: @Composable () -> Unit,
|
icon: @Composable () -> Unit,
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val shortNotice = rememberShortNotice()
|
||||||
val clipboard = LocalClipboard.current
|
val clipboard = LocalClipboard.current
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val copyLabel = stringRes(Res.string.copy_to_clipboard)
|
val copyLabel = stringRes(Res.string.copy_to_clipboard)
|
||||||
@@ -328,7 +327,7 @@ fun ProfilePaymentChip(
|
|||||||
onLongClick = {
|
onLongClick = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
clipboard.setText(copyValue)
|
clipboard.setText(copyValue)
|
||||||
Toast.makeText(context, copiedMessage, Toast.LENGTH_SHORT).show()
|
shortNotice.show(copiedMessage)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLongClickLabel = copyLabel,
|
onLongClickLabel = copyLabel,
|
||||||
+1
-1
@@ -18,7 +18,7 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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.padding
|
||||||
import androidx.compose.foundation.layout.size
|
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
|
* 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.
|
* 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.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
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
|
* 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.
|
* 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.Composable
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.ui.platform.LocalClipboard
|
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.icons.symbols.MaterialSymbols
|
||||||
import com.vitorpamplona.amethyst.commons.model.User
|
import com.vitorpamplona.amethyst.commons.model.User
|
||||||
import com.vitorpamplona.amethyst.commons.nip85TrustedAssertions.ui.EditNicknameDialog
|
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.M3ActionDialog
|
||||||
import com.vitorpamplona.amethyst.commons.ui.components.M3ActionRow
|
import com.vitorpamplona.amethyst.commons.ui.components.M3ActionRow
|
||||||
import com.vitorpamplona.amethyst.commons.ui.components.M3ActionSection
|
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.components.util.setText
|
||||||
import com.vitorpamplona.amethyst.commons.ui.note.creators.emojiSuggestions.WatchAndLoadMyEmojiList
|
import com.vitorpamplona.amethyst.commons.ui.note.creators.emojiSuggestions.WatchAndLoadMyEmojiList
|
||||||
import com.vitorpamplona.amethyst.commons.ui.note.externalLinkForUser
|
import com.vitorpamplona.amethyst.commons.ui.note.externalLinkForUser
|
||||||
@@ -85,7 +84,7 @@ fun UserProfileDropDownMenu(
|
|||||||
) {
|
) {
|
||||||
val clipboardManager = LocalClipboard.current
|
val clipboardManager = LocalClipboard.current
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val context = LocalContext.current
|
val textSharer = rememberTextSharer()
|
||||||
|
|
||||||
// Share section
|
// Share section
|
||||||
M3ActionSection {
|
M3ActionSection {
|
||||||
@@ -102,18 +101,7 @@ fun UserProfileDropDownMenu(
|
|||||||
icon = MaterialSymbols.Share,
|
icon = MaterialSymbols.Share,
|
||||||
text = stringRes(Res.string.quick_action_share),
|
text = stringRes(Res.string.quick_action_share),
|
||||||
) {
|
) {
|
||||||
val sendIntent =
|
textSharer.share(externalLinkForUser(user), quickActionShareBrowserLinkStr, quickActionShareStr)
|
||||||
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)
|
|
||||||
onDismiss()
|
onDismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+1
-1
@@ -18,7 +18,7 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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.animation.core.tween
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
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
|
* 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.
|
* 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.compose.runtime.Stable
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.viewmodel.CreationExtras
|
||||||
import com.vitorpamplona.amethyst.commons.model.User
|
import com.vitorpamplona.amethyst.commons.model.User
|
||||||
import com.vitorpamplona.amethyst.commons.ui.screen.AndroidFeedViewModel
|
import com.vitorpamplona.amethyst.commons.ui.screen.AndroidFeedViewModel
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
class UserAppRecommendationsFeedViewModel(
|
class UserAppRecommendationsFeedViewModel(
|
||||||
@@ -34,6 +36,9 @@ class UserAppRecommendationsFeedViewModel(
|
|||||||
val user: User,
|
val user: User,
|
||||||
) : ViewModelProvider.Factory {
|
) : ViewModelProvider.Factory {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@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
|
* 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.
|
* 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.AdditiveFeedFilter
|
||||||
import com.vitorpamplona.amethyst.commons.feeds.sortedByDefaultFeedOrder
|
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
|
* 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.
|
* 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.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
@@ -90,6 +90,7 @@ import kotlinx.collections.immutable.ImmutableList
|
|||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.IO
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.flowOn
|
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
|
* 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.
|
* 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.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
@@ -26,8 +26,8 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||||
import com.vitorpamplona.amethyst.commons.ui.screen.RefresheableFeedView
|
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.commons.viewmodels.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.mutual.dal.UserProfileMutualFeedViewModel
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TabMutualConversations(
|
fun TabMutualConversations(
|
||||||
+1
-1
@@ -18,7 +18,7 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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.AdditiveFeedFilter
|
||||||
import com.vitorpamplona.amethyst.commons.feeds.isRenderableRepost
|
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
|
* 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.
|
* 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.compose.runtime.Stable
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.viewmodel.CreationExtras
|
||||||
import com.vitorpamplona.amethyst.commons.model.Account
|
import com.vitorpamplona.amethyst.commons.model.Account
|
||||||
import com.vitorpamplona.amethyst.commons.model.User
|
import com.vitorpamplona.amethyst.commons.model.User
|
||||||
import com.vitorpamplona.amethyst.commons.ui.screen.AndroidFeedViewModel
|
import com.vitorpamplona.amethyst.commons.ui.screen.AndroidFeedViewModel
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
class UserProfileMutualFeedViewModel(
|
class UserProfileMutualFeedViewModel(
|
||||||
@@ -37,6 +39,9 @@ class UserProfileMutualFeedViewModel(
|
|||||||
val account: Account,
|
val account: Account,
|
||||||
) : ViewModelProvider.Factory {
|
) : ViewModelProvider.Factory {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@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
|
* 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.
|
* 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.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.layout.Column
|
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.feeds.WatchScrollToTop
|
||||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||||
import com.vitorpamplona.amethyst.commons.ui.note.NoteCompose
|
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.DividerThickness
|
||||||
import com.vitorpamplona.amethyst.commons.ui.theme.FeedPadding
|
import com.vitorpamplona.amethyst.commons.ui.theme.FeedPadding
|
||||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
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
|
@Composable
|
||||||
fun TabNotesNewThreads(
|
fun TabNotesNewThreads(
|
||||||
+1
-1
@@ -18,7 +18,7 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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.AdditiveFeedFilter
|
||||||
import com.vitorpamplona.amethyst.commons.feeds.isRenderableRepost
|
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
|
* 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.
|
* 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.compose.runtime.Stable
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.viewmodel.CreationExtras
|
||||||
import com.vitorpamplona.amethyst.commons.model.Account
|
import com.vitorpamplona.amethyst.commons.model.Account
|
||||||
import com.vitorpamplona.amethyst.commons.model.User
|
import com.vitorpamplona.amethyst.commons.model.User
|
||||||
import com.vitorpamplona.amethyst.commons.ui.screen.AndroidFeedViewModel
|
import com.vitorpamplona.amethyst.commons.ui.screen.AndroidFeedViewModel
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
class UserProfileNewThreadsFeedViewModel(
|
class UserProfileNewThreadsFeedViewModel(
|
||||||
@@ -37,6 +39,9 @@ class UserProfileNewThreadsFeedViewModel(
|
|||||||
val account: Account,
|
val account: Account,
|
||||||
) : ViewModelProvider.Factory {
|
) : ViewModelProvider.Factory {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@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
|
* 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.
|
* 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.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
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.ClinkPointerParser
|
||||||
import com.vitorpamplona.quartz.experimental.clink.pointers.NOffer
|
import com.vitorpamplona.quartz.experimental.clink.pointers.NOffer
|
||||||
import com.vitorpamplona.quartz.nip05DnsIdentifiers.Nip05Id
|
import com.vitorpamplona.quartz.nip05DnsIdentifiers.Nip05Id
|
||||||
|
import com.vitorpamplona.quartz.utils.cache.ConcurrentLruCache
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.IO
|
||||||
import kotlinx.coroutines.withContext
|
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
|
* 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
|
* 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
|
* 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(
|
private class ResolvedClinkOffer(
|
||||||
val noffer: NOffer?,
|
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`
|
* 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
|
* 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.
|
* 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.BorderStroke
|
||||||
import androidx.compose.foundation.combinedClickable
|
import androidx.compose.foundation.combinedClickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
@@ -59,7 +58,6 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalClipboard
|
import androidx.compose.ui.platform.LocalClipboard
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.input.KeyboardCapitalization
|
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
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_no_methods
|
||||||
import com.vitorpamplona.amethyst.commons.resources.send_payment_receipt_section
|
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.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.components.util.setText
|
||||||
import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe
|
import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe
|
||||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
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.ui.theme.placeholderText
|
||||||
import com.vitorpamplona.amethyst.commons.util.showAmount
|
import com.vitorpamplona.amethyst.commons.util.showAmount
|
||||||
import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
|
import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
|
||||||
|
import com.vitorpamplona.quartz.utils.BigDecimal
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
@@ -469,7 +469,7 @@ private fun MethodChip(
|
|||||||
enabled: Boolean,
|
enabled: Boolean,
|
||||||
onSelect: () -> Unit,
|
onSelect: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val shortNotice = rememberShortNotice()
|
||||||
val clipboard = LocalClipboard.current
|
val clipboard = LocalClipboard.current
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val copyLabel = stringRes(Res.string.copy_to_clipboard)
|
val copyLabel = stringRes(Res.string.copy_to_clipboard)
|
||||||
@@ -493,7 +493,7 @@ private fun MethodChip(
|
|||||||
{
|
{
|
||||||
scope.launch {
|
scope.launch {
|
||||||
clipboard.setText(value)
|
clipboard.setText(value)
|
||||||
Toast.makeText(context, copiedMessage, Toast.LENGTH_SHORT).show()
|
shortNotice.show(copiedMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -596,7 +596,7 @@ private fun AmountSection(
|
|||||||
SuggestionChip(
|
SuggestionChip(
|
||||||
enabled = enabled,
|
enabled = enabled,
|
||||||
onClick = { onAmountChange(amount.toString()) },
|
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
|
* 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.
|
* 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.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
@@ -40,7 +40,6 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
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_private_explainer
|
||||||
import com.vitorpamplona.amethyst.commons.resources.zap_type_public
|
import com.vitorpamplona.amethyst.commons.resources.zap_type_public
|
||||||
import com.vitorpamplona.amethyst.commons.resources.zap_type_public_explainer
|
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.nwcFailureDetail
|
||||||
import com.vitorpamplona.amethyst.commons.service.nwc.nwcTimeoutMessage
|
import com.vitorpamplona.amethyst.commons.service.nwc.nwcTimeoutMessage
|
||||||
import com.vitorpamplona.amethyst.commons.ui.loadStringRes
|
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.FeeTier
|
||||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.wallet.rateFor
|
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.wallet.rateFor
|
||||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
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.util.showAmount
|
||||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
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.common.SatRange
|
||||||
import com.vitorpamplona.quartz.experimental.clink.offers.OfferErrorCode
|
import com.vitorpamplona.quartz.experimental.clink.offers.OfferErrorCode
|
||||||
import com.vitorpamplona.quartz.experimental.clink.pointers.ClinkPointerParser
|
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.chain.FeeEstimates
|
||||||
import com.vitorpamplona.quartz.nipBCOnchainZaps.taproot.SegwitAddress
|
import com.vitorpamplona.quartz.nipBCOnchainZaps.taproot.SegwitAddress
|
||||||
import com.vitorpamplona.quartz.nipBCOnchainZaps.taproot.TaprootAddress
|
import com.vitorpamplona.quartz.nipBCOnchainZaps.taproot.TaprootAddress
|
||||||
|
import com.vitorpamplona.quartz.utils.BigDecimal
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.IO
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@@ -180,7 +183,7 @@ private fun SendPaymentLoaded(
|
|||||||
nav: INav,
|
nav: INav,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val walletLauncher = rememberWalletAppLauncher()
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
val userInfo by observeUserInfo(user, accountViewModel)
|
val userInfo by observeUserInfo(user, accountViewModel)
|
||||||
@@ -368,7 +371,7 @@ private fun SendPaymentLoaded(
|
|||||||
selectedMethod == ProfilePaymentMethod.CASHU ->
|
selectedMethod == ProfilePaymentMethod.CASHU ->
|
||||||
stringRes(
|
stringRes(
|
||||||
Res.string.send_payment_cashu_balance,
|
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 ->
|
selectedMethod == ProfilePaymentMethod.CLINK && clinkRange?.min != null && clinkRange?.max != null ->
|
||||||
stringRes(Res.string.clink_offer_amount_range, clinkRange?.min.toString(), clinkRange?.max.toString())
|
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
|
// startActivity belongs on Main. sendSats now delivers onNewInvoice
|
||||||
// there already; the hop also covers any other caller of payBolt11.
|
// there already; the hop also covers any other caller of payBolt11.
|
||||||
scope.launch {
|
scope.launch {
|
||||||
payViaIntent(
|
walletLauncher.payInvoice(
|
||||||
invoice,
|
invoice,
|
||||||
context,
|
|
||||||
noWalletFoundStr,
|
noWalletFoundStr,
|
||||||
onPaid = { scope.launch { postStage(PaymentFlowStage.Success(successTitle, sentToWalletLabel)) } },
|
onPaid = { scope.launch { postStage(PaymentFlowStage.Success(successTitle, sentToWalletLabel)) } },
|
||||||
onError = { scope.launch { postStage(PaymentFlowStage.Failure(it)) } },
|
onError = { scope.launch { postStage(PaymentFlowStage.Failure(it)) } },
|
||||||
@@ -664,7 +666,7 @@ private fun SendPaymentLoaded(
|
|||||||
canSend = canSend,
|
canSend = canSend,
|
||||||
sendLabel =
|
sendLabel =
|
||||||
if (amountSats != null && amountSats > 0) {
|
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 {
|
} else {
|
||||||
stringRes(Res.string.send_payment_pay_button_empty)
|
stringRes(Res.string.send_payment_pay_button_empty)
|
||||||
},
|
},
|
||||||
@@ -800,7 +802,7 @@ private fun OnchainFeeSection(
|
|||||||
stringRes(
|
stringRes(
|
||||||
Res.string.onchain_send_fee_tier_label_rate_eta,
|
Res.string.onchain_send_fee_tier_label_rate_eta,
|
||||||
stringRes(tier.labelRes),
|
stringRes(tier.labelRes),
|
||||||
"%.1f".format(rate),
|
DecimalPatternFormatter("0.0").format(rate),
|
||||||
stringRes(tier.etaLabelRes),
|
stringRes(tier.etaLabelRes),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
+1
-1
@@ -18,7 +18,7 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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.feeds.FeedFilter
|
||||||
import com.vitorpamplona.amethyst.commons.model.Account
|
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
|
* 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.
|
* 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.compose.runtime.Stable
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.viewmodel.CreationExtras
|
||||||
import com.vitorpamplona.amethyst.commons.model.Account
|
import com.vitorpamplona.amethyst.commons.model.Account
|
||||||
import com.vitorpamplona.amethyst.commons.model.User
|
import com.vitorpamplona.amethyst.commons.model.User
|
||||||
import com.vitorpamplona.amethyst.commons.ui.screen.AndroidFeedViewModel
|
import com.vitorpamplona.amethyst.commons.ui.screen.AndroidFeedViewModel
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
class UserProfilePinnedNotesFeedViewModel(
|
class UserProfilePinnedNotesFeedViewModel(
|
||||||
@@ -37,6 +39,9 @@ class UserProfilePinnedNotesFeedViewModel(
|
|||||||
val account: Account,
|
val account: Account,
|
||||||
) : ViewModelProvider.Factory {
|
) : ViewModelProvider.Factory {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@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
|
* 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.
|
* 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.PaddingValues
|
||||||
import androidx.compose.foundation.layout.padding
|
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
|
* 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.
|
* 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.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
@@ -28,8 +28,8 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.LifecycleEventObserver
|
import androidx.lifecycle.LifecycleEventObserver
|
||||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
||||||
import com.vitorpamplona.amethyst.commons.model.User
|
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.ui.navigation.navs.INav
|
||||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.commons.viewmodels.RelayFeedViewModel
|
import com.vitorpamplona.amethyst.commons.viewmodels.RelayFeedViewModel
|
||||||
@@ -40,7 +40,7 @@ fun TabRelays(
|
|||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
nav: INav,
|
nav: INav,
|
||||||
) {
|
) {
|
||||||
val feedViewModel: RelayFeedViewModel = viewModel()
|
val feedViewModel: RelayFeedViewModel = rememberViewModel { RelayFeedViewModel() }
|
||||||
|
|
||||||
val lifeCycleOwner = LocalLifecycleOwner.current
|
val lifeCycleOwner = LocalLifecycleOwner.current
|
||||||
|
|
||||||
+2
-2
@@ -18,7 +18,7 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
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.Res
|
||||||
import com.vitorpamplona.amethyst.commons.resources.number_reports
|
import com.vitorpamplona.amethyst.commons.resources.number_reports
|
||||||
import com.vitorpamplona.amethyst.commons.resources.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.ui.stringRes
|
||||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.reports.dal.UserProfileReportFeedViewModel
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ReportsTabHeader(
|
fun ReportsTabHeader(
|
||||||
+2
-2
@@ -18,7 +18,7 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
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.model.User
|
||||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||||
import com.vitorpamplona.amethyst.commons.ui.note.NoteCompose
|
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.DividerThickness
|
||||||
import com.vitorpamplona.amethyst.commons.ui.theme.FeedPadding
|
import com.vitorpamplona.amethyst.commons.ui.theme.FeedPadding
|
||||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.reports.dal.UserProfileReportFeedViewModel
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TabReports(
|
fun TabReports(
|
||||||
+8
-2
@@ -18,22 +18,25 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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.compose.runtime.Stable
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import androidx.lifecycle.viewmodel.CreationExtras
|
||||||
import com.vitorpamplona.amethyst.commons.model.Account
|
import com.vitorpamplona.amethyst.commons.model.Account
|
||||||
import com.vitorpamplona.amethyst.commons.model.Note
|
import com.vitorpamplona.amethyst.commons.model.Note
|
||||||
import com.vitorpamplona.amethyst.commons.model.User
|
import com.vitorpamplona.amethyst.commons.model.User
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.IO
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.flowOn
|
import kotlinx.coroutines.flow.flowOn
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.sample
|
import kotlinx.coroutines.flow.sample
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
class UserProfileReportFeedViewModel(
|
class UserProfileReportFeedViewModel(
|
||||||
@@ -76,6 +79,9 @@ class UserProfileReportFeedViewModel(
|
|||||||
val account: Account,
|
val account: Account,
|
||||||
) : ViewModelProvider.Factory {
|
) : ViewModelProvider.Factory {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@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
|
* 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.
|
* 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.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
@@ -32,10 +32,10 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.vitorpamplona.amethyst.commons.model.User
|
import com.vitorpamplona.amethyst.commons.model.User
|
||||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
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.DividerThickness
|
||||||
import com.vitorpamplona.amethyst.commons.ui.theme.FeedPadding
|
import com.vitorpamplona.amethyst.commons.ui.theme.FeedPadding
|
||||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.zaps.dal.UserProfileZapsViewModel
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TabReceivedZaps(
|
fun TabReceivedZaps(
|
||||||
+3
-3
@@ -18,7 +18,7 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
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.UserActionOptions
|
||||||
import com.vitorpamplona.amethyst.commons.ui.note.UserPicture
|
import com.vitorpamplona.amethyst.commons.ui.note.UserPicture
|
||||||
import com.vitorpamplona.amethyst.commons.ui.note.UsernameDisplay
|
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.BitcoinOrange
|
||||||
import com.vitorpamplona.amethyst.commons.ui.theme.Size55dp
|
import com.vitorpamplona.amethyst.commons.ui.theme.Size55dp
|
||||||
import com.vitorpamplona.amethyst.commons.ui.theme.ThemeComparisonColumn
|
import com.vitorpamplona.amethyst.commons.ui.theme.ThemeComparisonColumn
|
||||||
import com.vitorpamplona.amethyst.commons.util.showAmountInteger
|
import com.vitorpamplona.amethyst.commons.util.showAmountInteger
|
||||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.commons.viewmodels.mockAccountViewModel
|
import com.vitorpamplona.amethyst.commons.viewmodels.mockAccountViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.zaps.dal.ZapAmount
|
|
||||||
import com.vitorpamplona.quartz.utils.BigDecimal
|
import com.vitorpamplona.quartz.utils.BigDecimal
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -77,7 +77,7 @@ fun RenderZapNoteSlimPreview() {
|
|||||||
|
|
||||||
ThemeComparisonColumn {
|
ThemeComparisonColumn {
|
||||||
RenderZapNoteSlim(
|
RenderZapNoteSlim(
|
||||||
ZapAmount(user1, BigDecimal.TEN),
|
ZapAmount(user1, BigDecimal(10)),
|
||||||
accountViewModel,
|
accountViewModel,
|
||||||
EmptyNav(),
|
EmptyNav(),
|
||||||
)
|
)
|
||||||
+2
-2
@@ -18,7 +18,7 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@@ -26,10 +26,10 @@ import androidx.compose.runtime.getValue
|
|||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||||
import com.vitorpamplona.amethyst.commons.resources.zaps
|
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.ui.stringRes
|
||||||
import com.vitorpamplona.amethyst.commons.util.showAmountInteger
|
import com.vitorpamplona.amethyst.commons.util.showAmountInteger
|
||||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.zaps.dal.UserProfileZapsViewModel
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ZapTabHeader(
|
fun ZapTabHeader(
|
||||||
+19
-11
@@ -18,13 +18,14 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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.Immutable
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import androidx.lifecycle.viewmodel.CreationExtras
|
||||||
import com.vitorpamplona.amethyst.commons.model.Account
|
import com.vitorpamplona.amethyst.commons.model.Account
|
||||||
import com.vitorpamplona.amethyst.commons.model.User
|
import com.vitorpamplona.amethyst.commons.model.User
|
||||||
import com.vitorpamplona.amethyst.commons.model.cache.LocalCache
|
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.nipB1Bolt12Zaps.zap.Bolt12ZapEvent
|
||||||
import com.vitorpamplona.quartz.nipBCOnchainZaps.zap.OnchainZapEvent
|
import com.vitorpamplona.quartz.nipBCOnchainZaps.zap.OnchainZapEvent
|
||||||
import com.vitorpamplona.quartz.utils.BigDecimal
|
import com.vitorpamplona.quartz.utils.BigDecimal
|
||||||
|
import com.vitorpamplona.quartz.utils.plus
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.IO
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.flowOn
|
import kotlinx.coroutines.flow.flowOn
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.sample
|
import kotlinx.coroutines.flow.sample
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
data class ZapAmount(
|
data class ZapAmount(
|
||||||
@@ -59,14 +63,15 @@ class UserProfileZapsViewModel(
|
|||||||
tags = mapOf("p" to listOf(user.pubkeyHex)),
|
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> =
|
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? {
|
suspend fun mapRequest(zapEvent: ZapReceiptEvent): ZapAmount? {
|
||||||
val zapRequest =
|
val zapRequest =
|
||||||
zapEvent.zapRequest ?: return ZapAmount(
|
zapEvent.zapRequest ?: return ZapAmount(
|
||||||
LocalCache.getOrCreateUser(zapEvent.pubKey),
|
LocalCache.getOrCreateUser(zapEvent.pubKey),
|
||||||
zapEvent.amount ?: BigDecimal.ZERO,
|
zapEvent.amount ?: BigDecimal(0),
|
||||||
)
|
)
|
||||||
|
|
||||||
return if (zapRequest.isPrivateZap()) {
|
return if (zapRequest.isPrivateZap()) {
|
||||||
@@ -76,24 +81,24 @@ class UserProfileZapsViewModel(
|
|||||||
if (cachedPrivateRequest != null) {
|
if (cachedPrivateRequest != null) {
|
||||||
ZapAmount(
|
ZapAmount(
|
||||||
LocalCache.getOrCreateUser(cachedPrivateRequest.pubKey),
|
LocalCache.getOrCreateUser(cachedPrivateRequest.pubKey),
|
||||||
zapEvent.amount ?: BigDecimal.ZERO,
|
zapEvent.amount ?: BigDecimal(0),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
ZapAmount(
|
ZapAmount(
|
||||||
LocalCache.getOrCreateUser(zapRequest.pubKey),
|
LocalCache.getOrCreateUser(zapRequest.pubKey),
|
||||||
zapEvent.amount ?: BigDecimal.ZERO,
|
zapEvent.amount ?: BigDecimal(0),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ZapAmount(
|
ZapAmount(
|
||||||
LocalCache.getOrCreateUser(zapRequest.pubKey),
|
LocalCache.getOrCreateUser(zapRequest.pubKey),
|
||||||
zapEvent.amount ?: BigDecimal.ZERO,
|
zapEvent.amount ?: BigDecimal(0),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ZapAmount(
|
ZapAmount(
|
||||||
LocalCache.getOrCreateUser(zapRequest.pubKey),
|
LocalCache.getOrCreateUser(zapRequest.pubKey),
|
||||||
zapEvent.amount ?: BigDecimal.ZERO,
|
zapEvent.amount ?: BigDecimal(0),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -128,7 +133,7 @@ class UserProfileZapsViewModel(
|
|||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
if (zapAmount != null) {
|
if (zapAmount != null) {
|
||||||
val existingAmount = results[zapAmount.user] ?: BigDecimal.ZERO
|
val existingAmount = results[zapAmount.user] ?: BigDecimal(0)
|
||||||
results[zapAmount.user] = existingAmount + zapAmount.amount
|
results[zapAmount.user] = existingAmount + zapAmount.amount
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -152,11 +157,11 @@ class UserProfileZapsViewModel(
|
|||||||
|
|
||||||
val totalReceivedZaps =
|
val totalReceivedZaps =
|
||||||
receivedZapAmountsByUser
|
receivedZapAmountsByUser
|
||||||
.map { it.sumOf { it.amount } }
|
.map { amounts -> amounts.fold(BigDecimal(0)) { total, zap -> total + zap.amount } }
|
||||||
.flowOn(Dispatchers.IO)
|
.flowOn(Dispatchers.IO)
|
||||||
.stateIn(
|
.stateIn(
|
||||||
viewModelScope,
|
viewModelScope,
|
||||||
initialValue = BigDecimal.ZERO,
|
initialValue = BigDecimal(0),
|
||||||
started = SharingStarted.Lazily,
|
started = SharingStarted.Lazily,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -165,6 +170,9 @@ class UserProfileZapsViewModel(
|
|||||||
val account: Account,
|
val account: Account,
|
||||||
) : ViewModelProvider.Factory {
|
) : ViewModelProvider.Factory {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@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
|
* 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.
|
* 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.background
|
||||||
import androidx.compose.foundation.border
|
import androidx.compose.foundation.border
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
@@ -53,7 +52,6 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.semantics.contentDescription
|
import androidx.compose.ui.semantics.contentDescription
|
||||||
import androidx.compose.ui.semantics.semantics
|
import androidx.compose.ui.semantics.semantics
|
||||||
import androidx.compose.ui.text.font.FontFamily
|
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.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
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.Icon
|
||||||
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbol
|
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbol
|
||||||
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
|
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.EmptyNav
|
||||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||||
import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.TopBarWithBackButton
|
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.SegmentedChoiceTile
|
||||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.SettingsBlockTile
|
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.SettingsBlockTile
|
||||||
import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings.SettingsControlRow
|
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 com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||||
import kotlinx.collections.immutable.ImmutableMap
|
import kotlinx.collections.immutable.ImmutableMap
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
import kotlinx.collections.immutable.toImmutableMap
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import org.jetbrains.compose.resources.StringResource
|
import org.jetbrains.compose.resources.StringResource
|
||||||
import org.xmlpull.v1.XmlPullParser
|
|
||||||
import org.xmlpull.v1.XmlPullParserException
|
|
||||||
import java.io.IOException
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SettingsScreen(
|
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.
|
// matches the app's other "opens a picker" rows instead of leaving a lone text-field dropdown here.
|
||||||
@Composable
|
@Composable
|
||||||
private fun LanguageTile(sharedPrefs: UiSettingsFlow) {
|
private fun LanguageTile(sharedPrefs: UiSettingsFlow) {
|
||||||
val context = LocalContext.current
|
val languageEntries = LocalAppPlatform.current.rememberAppLanguages()
|
||||||
|
|
||||||
val languageEntries = remember { context.getLangPreferenceDropdownEntries() }
|
|
||||||
val languageList = remember { languageEntries.keys.map { TitleExplainer(it) }.toImmutableList() }
|
val languageList = remember { languageEntries.keys.map { TitleExplainer(it) }.toImmutableList() }
|
||||||
|
|
||||||
val language by sharedPrefs.preferredLanguage.collectAsState()
|
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(
|
fun getLanguageIndex(
|
||||||
languageEntries: ImmutableMap<String, String>,
|
languageEntries: ImmutableMap<String, String>,
|
||||||
language: String?,
|
language: String?,
|
||||||
+10
-17
@@ -18,7 +18,7 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
@@ -33,34 +33,28 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.vitorpamplona.amethyst.Amethyst
|
|
||||||
import com.vitorpamplona.amethyst.commons.model.preferences.NamecoinSettingsStore
|
import com.vitorpamplona.amethyst.commons.model.preferences.NamecoinSettingsStore
|
||||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||||
import com.vitorpamplona.amethyst.commons.resources.namecoin_settings
|
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.navs.INav
|
||||||
import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.TopBarWithBackButton
|
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.amethyst.commons.ui.stringRes
|
||||||
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.ElectrumXClient
|
|
||||||
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.NamecoinCoreRpcClient
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun NamecoinSettingsScreen(nav: INav) {
|
fun NamecoinSettingsScreen(nav: INav) {
|
||||||
NamecoinSettingsScreen(
|
val services = LocalAppServices.current
|
||||||
Amethyst.instance.namecoinPrefs,
|
NamecoinSettingsScreen(services.namecoinSettings, services.namecoinClients, nav)
|
||||||
electrumXClient = { Amethyst.instance.electrumXClient },
|
|
||||||
namecoinCoreRpcClient = { Amethyst.instance.namecoinCoreRpcClient },
|
|
||||||
nav,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun NamecoinSettingsScreen(
|
fun NamecoinSettingsScreen(
|
||||||
namecoinPrefs: NamecoinSettingsStore,
|
namecoinPrefs: NamecoinSettingsStore,
|
||||||
electrumXClient: () -> ElectrumXClient,
|
clients: NamecoinClients,
|
||||||
namecoinCoreRpcClient: () -> NamecoinCoreRpcClient,
|
|
||||||
nav: INav,
|
nav: INav,
|
||||||
) {
|
) {
|
||||||
val namecoinSettings by namecoinPrefs.settings.collectAsStateWithLifecycle()
|
val namecoinSettings by namecoinPrefs.settings.collectAsStateWithLifecycle()
|
||||||
@@ -92,14 +86,13 @@ fun NamecoinSettingsScreen(
|
|||||||
onReset = {
|
onReset = {
|
||||||
scope.launch { namecoinPrefs.reset() }
|
scope.launch { namecoinPrefs.reset() }
|
||||||
},
|
},
|
||||||
onTestServer = { server -> electrumXClient().testServer(server) },
|
onTestServer = { server -> clients.testElectrumxServer(server) },
|
||||||
onPinCert = { pem ->
|
onPinCert = { pem ->
|
||||||
scope.launch {
|
scope.launch {
|
||||||
namecoinPrefs.addPinnedCert(pem)
|
namecoinPrefs.addPinnedCert(pem)
|
||||||
// Share the pinned PEM across both backends so the
|
// Share the pinned PEM across both backends so the
|
||||||
// user only has to confirm a self-signed cert once.
|
// user only has to confirm a self-signed cert once.
|
||||||
electrumXClient().addPinnedCert(pem)
|
clients.addPinnedCert(pem)
|
||||||
namecoinCoreRpcClient().addPinnedCert(pem)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onSetBackend = { backend ->
|
onSetBackend = { backend ->
|
||||||
@@ -108,7 +101,7 @@ fun NamecoinSettingsScreen(
|
|||||||
onSetCoreRpcConfig = { cfg ->
|
onSetCoreRpcConfig = { cfg ->
|
||||||
scope.launch {
|
scope.launch {
|
||||||
namecoinPrefs.setCoreRpcConfig(cfg)
|
namecoinPrefs.setCoreRpcConfig(cfg)
|
||||||
namecoinCoreRpcClient().setConfig(cfg)
|
clients.setCoreRpcConfig(cfg)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onSetFallbackToCustomElectrumx = { enabled ->
|
onSetFallbackToCustomElectrumx = { enabled ->
|
||||||
@@ -117,7 +110,7 @@ fun NamecoinSettingsScreen(
|
|||||||
onSetFallbackToDefaultElectrumx = { enabled ->
|
onSetFallbackToDefaultElectrumx = { enabled ->
|
||||||
scope.launch { namecoinPrefs.setFallbackToDefaultElectrumx(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
|
* 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.
|
* 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.AnimatedVisibility
|
||||||
import androidx.compose.animation.expandVertically
|
import androidx.compose.animation.expandVertically
|
||||||
import androidx.compose.animation.shrinkVertically
|
import androidx.compose.animation.shrinkVertically
|
||||||
@@ -60,7 +59,6 @@ import androidx.compose.runtime.setValue
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||||
import androidx.compose.ui.text.font.FontFamily
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
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.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
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.Icon
|
||||||
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
|
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
|
||||||
import com.vitorpamplona.amethyst.commons.model.nip05DnsIdentifiers.namecoin.NamecoinSettings
|
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.DateTimeStyle
|
||||||
import com.vitorpamplona.amethyst.commons.ui.note.formatDateTime
|
import com.vitorpamplona.amethyst.commons.ui.note.formatDateTime
|
||||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
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.DEFAULT_ELECTRUMX_SERVERS
|
||||||
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.ElectrumxServer
|
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.ElectrumxServer
|
||||||
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.NamecoinBackend
|
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.NamecoinBackend
|
||||||
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.NamecoinCoreRpcConfig
|
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.NamecoinCoreRpcConfig
|
||||||
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.RpcProbeResult
|
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.RpcProbeResult
|
||||||
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.ServerTestResult
|
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.ServerTestResult
|
||||||
|
import com.vitorpamplona.quartz.utils.currentTimeMillis
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.util.Locale
|
import kotlin.math.roundToLong
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Complete settings section for Namecoin ElectrumX server configuration.
|
* Complete settings section for Namecoin ElectrumX server configuration.
|
||||||
@@ -349,7 +350,7 @@ private fun TestConnectionSection(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lastTestTimestamp = System.currentTimeMillis()
|
lastTestTimestamp = currentTimeMillis()
|
||||||
isTesting = false
|
isTesting = false
|
||||||
// Show confirmation dialog for each new cert
|
// Show confirmation dialog for each new cert
|
||||||
if (newCerts.isNotEmpty()) {
|
if (newCerts.isNotEmpty()) {
|
||||||
@@ -512,9 +513,8 @@ private fun DiagnosticCard(
|
|||||||
|
|
||||||
// Last test timestamp
|
// Last test timestamp
|
||||||
if (lastTestTimestamp != null) {
|
if (lastTestTimestamp != null) {
|
||||||
val context = LocalContext.current
|
|
||||||
val formatted =
|
val formatted =
|
||||||
remember(lastTestTimestamp, context) {
|
remember(lastTestTimestamp) {
|
||||||
formatDateTime(lastTestTimestamp, DateTimeStyle.MEDIUM, DateTimeStyle.SHORT)
|
formatDateTime(lastTestTimestamp, DateTimeStyle.MEDIUM, DateTimeStyle.SHORT)
|
||||||
}
|
}
|
||||||
val successCount = testResults.count { it.success }
|
val successCount = testResults.count { it.success }
|
||||||
@@ -535,7 +535,7 @@ private fun DiagnosticCard(
|
|||||||
// Device info
|
// Device info
|
||||||
DiagnosticRow(
|
DiagnosticRow(
|
||||||
label = stringRes(Res.string.namecoin_device_info),
|
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))
|
Spacer(Modifier.height(4.dp))
|
||||||
@@ -1108,13 +1108,7 @@ private fun NamecoinCoreRpcSection(
|
|||||||
!candidate.usePinnedTrustStore
|
!candidate.usePinnedTrustStore
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
val host =
|
val host = OmniboxInput.hostOf(candidate.url) ?: candidate.url
|
||||||
try {
|
|
||||||
java.net.URI(candidate.url).host
|
|
||||||
?: candidate.url
|
|
||||||
} catch (_: Exception) {
|
|
||||||
candidate.url
|
|
||||||
}
|
|
||||||
pendingPin =
|
pendingPin =
|
||||||
PendingCertPin(
|
PendingCertPin(
|
||||||
serverHost = host,
|
serverHost = host,
|
||||||
@@ -1167,7 +1161,7 @@ private fun NamecoinCoreRpcSection(
|
|||||||
val chain = probe.chain ?: "?"
|
val chain = probe.chain ?: "?"
|
||||||
val blocks = probe.blocks?.toString() ?: "?"
|
val blocks = probe.blocks?.toString() ?: "?"
|
||||||
val pct =
|
val pct =
|
||||||
probe.verificationProgress?.let { String.format(Locale.ROOT, "%.2f%%", it * 100) }
|
probe.verificationProgress?.let { percentTwoDecimals(it) }
|
||||||
?: "?"
|
?: "?"
|
||||||
Text(
|
Text(
|
||||||
"chain=$chain height=$blocks sync=$pct" +
|
"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
|
* 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.
|
* 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.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
@@ -33,7 +33,6 @@ import androidx.compose.runtime.getValue
|
|||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.vitorpamplona.amethyst.Amethyst
|
|
||||||
import com.vitorpamplona.amethyst.commons.model.preferences.OtsSettingsStore
|
import com.vitorpamplona.amethyst.commons.model.preferences.OtsSettingsStore
|
||||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||||
import com.vitorpamplona.amethyst.commons.resources.ots_explorer_settings
|
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.tor.TorType
|
||||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||||
import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.TopBarWithBackButton
|
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.amethyst.commons.ui.stringRes
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun OtsSettingsScreen(nav: INav) {
|
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)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
+3
-3
@@ -18,7 +18,7 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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.background
|
||||||
import androidx.compose.foundation.border
|
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.resources.save
|
||||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||||
import com.vitorpamplona.amethyst.commons.ui.theme.BitcoinOrange
|
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.
|
* Settings section for configuring the blockchain explorer used for OTS verification.
|
||||||
@@ -180,7 +180,7 @@ private fun ActiveExplorerDisplay(
|
|||||||
val isCustom = settings.hasCustomExplorer
|
val isCustom = settings.hasCustomExplorer
|
||||||
val activeUrl =
|
val activeUrl =
|
||||||
settings.normalizedUrl()
|
settings.normalizedUrl()
|
||||||
?: if (isTorActive) OkHttpBitcoinExplorer.MEMPOOL_API_URL else OkHttpBitcoinExplorer.BLOCKSTREAM_API_URL
|
?: if (isTorActive) BitcoinExplorerUrls.MEMPOOL else BitcoinExplorerUrls.BLOCKSTREAM
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
Row(
|
Row(
|
||||||
+22
-18
@@ -18,9 +18,8 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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.background
|
||||||
import androidx.compose.foundation.border
|
import androidx.compose.foundation.border
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
@@ -56,14 +55,12 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.text.font.FontFamily
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import coil3.compose.AsyncImage
|
import coil3.compose.AsyncImage
|
||||||
import com.vitorpamplona.amethyst.Amethyst
|
|
||||||
import com.vitorpamplona.amethyst.commons.connectedApps.nip46.Nip46ClientInfo
|
import com.vitorpamplona.amethyst.commons.connectedApps.nip46.Nip46ClientInfo
|
||||||
import com.vitorpamplona.amethyst.commons.connectedApps.nip46.Nip46PermissionAuthorizer
|
import com.vitorpamplona.amethyst.commons.connectedApps.nip46.Nip46PermissionAuthorizer
|
||||||
import com.vitorpamplona.amethyst.commons.connectedApps.signers.AppSignerPolicy
|
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_manage_apps
|
||||||
import com.vitorpamplona.amethyst.commons.resources.nip46_signer_reconnecting
|
import com.vitorpamplona.amethyst.commons.resources.nip46_signer_reconnecting
|
||||||
import com.vitorpamplona.amethyst.commons.resources.nip46_signer_remote_app
|
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.navs.INav
|
||||||
import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.TopBarWithBackButton
|
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.pluralStringRes
|
||||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||||
import com.vitorpamplona.amethyst.commons.util.toTimeAgo
|
|
||||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||||
@@ -125,7 +125,8 @@ fun Nip46ConnectedAppsScreen(
|
|||||||
val reconnectingStr = stringRes(Res.string.nip46_signer_reconnecting)
|
val reconnectingStr = stringRes(Res.string.nip46_signer_reconnecting)
|
||||||
val account = accountViewModel.account
|
val account = accountViewModel.account
|
||||||
val signerPubKey = remember { account.signer.pubKey }
|
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
|
// 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.
|
// 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) }
|
var refreshKey by remember { mutableIntStateOf(0) }
|
||||||
|
|
||||||
LaunchedEffect(refreshKey) {
|
LaunchedEffect(refreshKey) {
|
||||||
items = withContext(Dispatchers.Default) { loadNip46Apps(signerPubKey) }
|
items = withContext(Dispatchers.Default) { loadNip46Apps(services, signerPubKey) }
|
||||||
}
|
}
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
@@ -187,7 +188,7 @@ fun Nip46ConnectedAppsScreen(
|
|||||||
online = online,
|
online = online,
|
||||||
onReconnect = {
|
onReconnect = {
|
||||||
account.client.reconnect(ignoreRetryDelays = true)
|
account.client.reconnect(ignoreRetryDelays = true)
|
||||||
Toast.makeText(context, reconnectingStr, Toast.LENGTH_SHORT).show()
|
shortNotice.show(reconnectingStr)
|
||||||
},
|
},
|
||||||
onClick = { nav.nav(Route.ConnectedAppDetail(entry.coordinate)) },
|
onClick = { nav.nav(Route.ConnectedAppDetail(entry.coordinate)) },
|
||||||
)
|
)
|
||||||
@@ -241,7 +242,7 @@ private fun Nip46AppCard(
|
|||||||
val meta =
|
val meta =
|
||||||
buildList {
|
buildList {
|
||||||
if (relayCount > 0) add(pluralStringRes(Res.plurals.nip46_signer_app_relay_count, relayCount, relayCount))
|
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(" · ")
|
}.joinToString(" · ")
|
||||||
if (meta.isNotEmpty()) {
|
if (meta.isNotEmpty()) {
|
||||||
Text(
|
Text(
|
||||||
@@ -291,7 +292,7 @@ private val LiveGreen = Color(0xFF3DDC84)
|
|||||||
|
|
||||||
/** A colored dot: green when the signer currently reaches the relay(s), muted otherwise. */
|
/** A colored dot: green when the signer currently reaches the relay(s), muted otherwise. */
|
||||||
@Composable
|
@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))
|
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.
|
* pill inside a clickable card doesn't also open the card.
|
||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
internal fun Nip46ReconnectPill(onClick: () -> Unit) {
|
fun Nip46ReconnectPill(onClick: () -> Unit) {
|
||||||
Text(
|
Text(
|
||||||
stringRes(Res.string.nip46_signer_app_reconnect),
|
stringRes(Res.string.nip46_signer_app_reconnect),
|
||||||
style = MaterialTheme.typography.labelSmall,
|
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. */
|
/** A [Nip46StatusDot] + Connected/Offline label showing whether the signer currently reaches an app's relays. */
|
||||||
@Composable
|
@Composable
|
||||||
internal fun Nip46LiveStatus(online: Boolean) {
|
fun Nip46LiveStatus(online: Boolean) {
|
||||||
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(6.dp)) {
|
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||||
Nip46StatusDot(online)
|
Nip46StatusDot(online)
|
||||||
Text(
|
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
|
* `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].
|
* one of the app's relays is in [connectedRelays].
|
||||||
*/
|
*/
|
||||||
internal fun nip46AppOnline(
|
fun nip46AppOnline(
|
||||||
appRelays: Set<String>,
|
appRelays: Set<String>,
|
||||||
inboxRelays: Set<NormalizedRelayUrl>,
|
inboxRelays: Set<NormalizedRelayUrl>,
|
||||||
connectedRelays: Set<NormalizedRelayUrl>,
|
connectedRelays: Set<NormalizedRelayUrl>,
|
||||||
@@ -352,7 +353,7 @@ internal fun nip46AppOnline(
|
|||||||
* user's IP (see BrowserIconRegistry).
|
* user's IP (see BrowserIconRegistry).
|
||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
internal fun Nip46AppIcon(
|
fun Nip46AppIcon(
|
||||||
image: String?,
|
image: String?,
|
||||||
modifier: Modifier = Modifier,
|
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
|
* advertised one, otherwise its npub. Shared by the list card and the detail header so a row and
|
||||||
* the screen it opens always agree.
|
* the screen it opens always agree.
|
||||||
*/
|
*/
|
||||||
internal fun nip46ClientSubtitle(
|
fun nip46ClientSubtitle(
|
||||||
url: String?,
|
url: String?,
|
||||||
clientPubKey: HexKey,
|
clientPubKey: HexKey,
|
||||||
): String {
|
): String {
|
||||||
@@ -402,9 +403,12 @@ internal fun nip46ClientSubtitle(
|
|||||||
return host ?: runCatching { NPub.create(clientPubKey) }.getOrDefault(clientPubKey.take(12) + "…")
|
return host ?: runCatching { NPub.create(clientPubKey) }.getOrDefault(clientPubKey.take(12) + "…")
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun loadNip46Apps(signerPubKey: HexKey): List<Nip46AppEntry> {
|
private suspend fun loadNip46Apps(
|
||||||
val store = Amethyst.instance.signerPermissionStore
|
services: AppServices,
|
||||||
val clientStore = Amethyst.instance.nip46ClientStore
|
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
|
// allPolicies() already carries each app's policy — read it from the map instead of a second
|
||||||
// loadPolicy() call per app.
|
// loadPolicy() call per app.
|
||||||
return store
|
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()
|
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
|
package com.vitorpamplona.quartz.nip03Timestamp.okhttp
|
||||||
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.jackson.JacksonMapper
|
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.BitcoinExplorer
|
||||||
import com.vitorpamplona.quartz.nip03Timestamp.ots.BlockHeader
|
import com.vitorpamplona.quartz.nip03Timestamp.ots.BlockHeader
|
||||||
import com.vitorpamplona.quartz.nip03Timestamp.ots.OtsBlockHeightCache
|
import com.vitorpamplona.quartz.nip03Timestamp.ots.OtsBlockHeightCache
|
||||||
@@ -116,9 +117,9 @@ class OkHttpBitcoinExplorer(
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
// doesn't accept Tor
|
// doesn't accept Tor
|
||||||
const val BLOCKSTREAM_API_URL = "https://blockstream.info/api"
|
const val BLOCKSTREAM_API_URL = BitcoinExplorerUrls.BLOCKSTREAM
|
||||||
|
|
||||||
// accepts Tor
|
// 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. */
|
/** 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