diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/notifyCommand/compose/NotifyRequestDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/notifyCommand/compose/NotifyRequestDialog.kt index e268a9d483..a496e38c19 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/notifyCommand/compose/NotifyRequestDialog.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/notifyCommand/compose/NotifyRequestDialog.kt @@ -44,11 +44,11 @@ import com.vitorpamplona.amethyst.commons.resources.error_dialog_button_ok import com.vitorpamplona.amethyst.commons.resources.notify_block_relay import com.vitorpamplona.amethyst.commons.ui.components.TranslatableRichTextViewer import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav +import com.vitorpamplona.amethyst.commons.ui.navigation.navs.rememberExtendedNav import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.ui.theme.Size16dp import com.vitorpamplona.amethyst.commons.ui.theme.StdHorzSpacer import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel -import com.vitorpamplona.amethyst.ui.navigation.navs.rememberExtendedNav @Composable fun NotifyRequestDialog( diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/navs/RememberNavs.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/navs/RememberNavs.kt index a9133a7c43..061581ce95 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/navs/RememberNavs.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/navs/RememberNavs.kt @@ -24,8 +24,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.navigation.compose.rememberNavController -import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav -import com.vitorpamplona.amethyst.commons.ui.navigation.navs.ObservableNav @Composable fun rememberNav(): Nav { @@ -37,14 +35,3 @@ fun rememberNav(): Nav { Nav(navController, scope, ime) } } - -@Composable -fun rememberExtendedNav( - nav: INav, - onBeforeNavigate: () -> Unit, -): INav { - val scope = rememberCoroutineScope() - return remember(nav, scope) { - ObservableNav(nav, scope, onBeforeNavigate) - } -} diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt index 88beb7be95..9c2cbc2076 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt @@ -66,7 +66,6 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.CardDefaults import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ElevatedCard -import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ProgressIndicatorDefaults import androidx.compose.material3.Surface @@ -77,7 +76,6 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState import androidx.compose.runtime.SideEffect import androidx.compose.runtime.State -import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableLongStateOf @@ -95,7 +93,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.platform.LocalClipboard import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity @@ -124,7 +121,6 @@ import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.ReactionRowAction import com.vitorpamplona.amethyst.commons.model.ReactionRowItem import com.vitorpamplona.amethyst.commons.model.User -import com.vitorpamplona.amethyst.commons.model.ZapraiserStatus import com.vitorpamplona.amethyst.commons.model.navigation.Route import com.vitorpamplona.amethyst.commons.model.nip29RelayGroups.RelayGroupChannel import com.vitorpamplona.amethyst.commons.model.payments.PaymentTargetTypes @@ -138,6 +134,7 @@ import com.vitorpamplona.amethyst.commons.relayClient.event.observeNoteReposts import com.vitorpamplona.amethyst.commons.relayClient.event.observeNoteRepostsBy import com.vitorpamplona.amethyst.commons.relayClient.event.observeNoteZaps import com.vitorpamplona.amethyst.commons.relayClient.reqCommand.event.EventFinderFilterAssemblerSubscription +import com.vitorpamplona.amethyst.commons.relayClient.reqCommand.nwc.NWCFinderFilterAssemblerSubscription import com.vitorpamplona.amethyst.commons.relayClient.user.observeUserInfo import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.boost @@ -164,7 +161,6 @@ import com.vitorpamplona.amethyst.commons.resources.quick_zap_amounts import com.vitorpamplona.amethyst.commons.resources.quote import com.vitorpamplona.amethyst.commons.resources.read_only_user import com.vitorpamplona.amethyst.commons.resources.reload_mint_title -import com.vitorpamplona.amethyst.commons.resources.sats_to_complete import com.vitorpamplona.amethyst.commons.service.ZapPaymentHandler import com.vitorpamplona.amethyst.commons.ui.components.AnimatedBorderTextCornerRadius import com.vitorpamplona.amethyst.commons.ui.components.ClickableBox @@ -181,11 +177,13 @@ import com.vitorpamplona.amethyst.commons.ui.note.ExpandMoreIcon import com.vitorpamplona.amethyst.commons.ui.note.LikeIcon import com.vitorpamplona.amethyst.commons.ui.note.LikedIcon import com.vitorpamplona.amethyst.commons.ui.note.LoadAddressableNote +import com.vitorpamplona.amethyst.commons.ui.note.ObserveZapAmountText import com.vitorpamplona.amethyst.commons.ui.note.OutlinedZapIcon import com.vitorpamplona.amethyst.commons.ui.note.RenderBoostGallery import com.vitorpamplona.amethyst.commons.ui.note.RenderLikeGallery import com.vitorpamplona.amethyst.commons.ui.note.RenderReaction import com.vitorpamplona.amethyst.commons.ui.note.RenderZapGallery +import com.vitorpamplona.amethyst.commons.ui.note.RenderZapRaiser import com.vitorpamplona.amethyst.commons.ui.note.RepostIcon import com.vitorpamplona.amethyst.commons.ui.note.RepostedIcon import com.vitorpamplona.amethyst.commons.ui.note.ShareIcon @@ -203,10 +201,7 @@ import com.vitorpamplona.amethyst.commons.ui.theme.ButtonBorder import com.vitorpamplona.amethyst.commons.ui.theme.Font14SP import com.vitorpamplona.amethyst.commons.ui.theme.HalfDoubleVertSpacer import com.vitorpamplona.amethyst.commons.ui.theme.HalfPadding -import com.vitorpamplona.amethyst.commons.ui.theme.Height24dpFilledModifier -import com.vitorpamplona.amethyst.commons.ui.theme.Height4dpFilledModifier import com.vitorpamplona.amethyst.commons.ui.theme.ModifierWidth3dp -import com.vitorpamplona.amethyst.commons.ui.theme.NoSoTinyBorders import com.vitorpamplona.amethyst.commons.ui.theme.ReactionRowExpandButton import com.vitorpamplona.amethyst.commons.ui.theme.ReactionRowHeight import com.vitorpamplona.amethyst.commons.ui.theme.ReactionRowHeightWithPadding @@ -223,11 +218,9 @@ import com.vitorpamplona.amethyst.commons.ui.theme.Size22Modifier import com.vitorpamplona.amethyst.commons.ui.theme.Size28Modifier import com.vitorpamplona.amethyst.commons.ui.theme.SmallBorder import com.vitorpamplona.amethyst.commons.ui.theme.ThemeComparisonColumn -import com.vitorpamplona.amethyst.commons.ui.theme.TinyBorders import com.vitorpamplona.amethyst.commons.ui.theme.defaultTweenDuration import com.vitorpamplona.amethyst.commons.ui.theme.defaultTweenFloatSpec import com.vitorpamplona.amethyst.commons.ui.theme.defaultTweenIntOffsetSpec -import com.vitorpamplona.amethyst.commons.ui.theme.fundraiserProgressColor import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText import com.vitorpamplona.amethyst.commons.ui.theme.reactionBox import com.vitorpamplona.amethyst.commons.ui.theme.ripple24dp @@ -239,7 +232,6 @@ import com.vitorpamplona.amethyst.model.zap.CashuRailStatus import com.vitorpamplona.amethyst.model.zap.RailCapability import com.vitorpamplona.amethyst.model.zap.RailCapabilityResolver import com.vitorpamplona.amethyst.service.payments.PayToAppAvailability -import com.vitorpamplona.amethyst.service.relayClient.reqCommand.nwc.NWCFinderFilterAssemblerSubscription import com.vitorpamplona.amethyst.ui.actions.uploads.MAX_VOICE_RECORD_SECONDS import com.vitorpamplona.amethyst.ui.actions.uploads.RecordAudioBox import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.PaymentTargetsDialog @@ -268,7 +260,6 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import kotlinx.serialization.json.Json -import kotlin.math.roundToInt import kotlin.uuid.ExperimentalUuidApi import kotlin.uuid.Uuid import androidx.compose.material3.Icon as Material3Icon @@ -524,57 +515,6 @@ fun LoadAndDisplayZapraiser( } } -@Composable -fun RenderZapRaiser( - baseNote: Note, - zapraiserAmount: Long, - details: Boolean, - accountViewModel: AccountViewModel, -) { - val zapsState by observeNoteZaps(baseNote, accountViewModel) - - var zapraiserStatus by remember { mutableStateOf(ZapraiserStatus(0F, "$zapraiserAmount")) } - - LaunchedEffect(key1 = zapsState) { - zapsState?.note?.let { - val newStatus = accountViewModel.calculateZapraiser(baseNote) - if (zapraiserStatus != newStatus) { - zapraiserStatus = newStatus - } - } - } - - LinearProgressIndicator( - modifier = if (details) Height24dpFilledModifier else Height4dpFilledModifier, - color = MaterialTheme.colorScheme.fundraiserProgressColor, - progress = { zapraiserStatus.progress }, - gapSize = 0.dp, - strokeCap = StrokeCap.Square, - drawStopIndicator = {}, - ) - - if (details) { - Box( - contentAlignment = Center, - modifier = TinyBorders, - ) { - val totalPercentage by - remember(zapraiserStatus) { - derivedStateOf { "${(zapraiserStatus.progress * 100).roundToInt()}%" } - } - - Text( - text = - stringRes(id = Res.string.sats_to_complete, totalPercentage, zapraiserStatus.left), - modifier = NoSoTinyBorders, - // color = MaterialTheme.colorScheme.placeholderText, - fontSize = Font14SP, - maxLines = 1, - ) - } - } -} - @Composable private fun WatchReactionsZapsBoostsAndDisplayIfExists( baseNote: Note, @@ -1668,45 +1608,6 @@ fun ObserveZapIcon( inner(wasZappedByLoggedInUser) } -@Composable -fun ObserveZapAmountText( - baseNote: Note, - accountViewModel: AccountViewModel, - inner: @Composable (String) -> Unit, -) { - val zapsState by observeNoteZaps(baseNote, accountViewModel) - - if (zapsState?.note?.zapPayments?.isNotEmpty() == true) { - zapsState?.note?.zapPayments?.forEach { - if (it.value == null) { - NWCFinderFilterAssemblerSubscription(it.key, accountViewModel) - } - } - - @Suppress("ProduceStateDoesNotAssignValue") - val zapAmountTxt by - produceState(initialValue = showAmount(baseNote.zapsAmount), key1 = zapsState) { - zapsState?.note?.let { - val newZapAmount = accountViewModel.calculateZapAmount(it) - if (value != newZapAmount) { - value = newZapAmount - } - } - } - - inner(zapAmountTxt) - } else { - // Include the signed-in user's own pending onchain zaps so - // the counter reflects the optimistic value the gallery shows. - val ownPubKey = accountViewModel.account.userProfile().pubkeyHex - val note = zapsState?.note - val total = - (note?.zapsAmount ?: java.math.BigDecimal(0)) + - java.math.BigDecimal(note?.extraOwnPendingOnchainSats(ownPubKey) ?: 0L) - inner(showAmount(total)) - } -} - @Composable private fun BoostTypeChoicePopup( baseNote: Note, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/nip22Comments/GenericCommentPostScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/nip22Comments/GenericCommentPostScreen.kt index 484b7aa166..167e17b837 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/nip22Comments/GenericCommentPostScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/nip22Comments/GenericCommentPostScreen.kt @@ -83,6 +83,7 @@ import com.vitorpamplona.amethyst.commons.ui.actions.uploads.SelectFromFiles import com.vitorpamplona.amethyst.commons.ui.actions.uploads.SelectFromGallery import com.vitorpamplona.amethyst.commons.ui.components.PlatformBackHandler import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe +import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.PostingTopBar import com.vitorpamplona.amethyst.commons.ui.note.BaseUserPicture import com.vitorpamplona.amethyst.commons.ui.note.NoteCompose @@ -114,7 +115,6 @@ import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel import com.vitorpamplona.amethyst.ui.actions.uploads.TakePictureButton import com.vitorpamplona.amethyst.ui.actions.uploads.TakeVideoButton import com.vitorpamplona.amethyst.ui.components.getActivity -import com.vitorpamplona.amethyst.ui.navigation.navs.Nav import com.vitorpamplona.amethyst.ui.note.creators.emojiSuggestions.WatchAndLoadMyEmojiList import com.vitorpamplona.amethyst.ui.note.creators.expiration.ExpirationDatePicker import com.vitorpamplona.amethyst.ui.note.creators.invoice.InvoiceRequest @@ -144,7 +144,7 @@ fun ReplyCommentPostScreen( quoteId: HexKey? = null, draftId: HexKey? = null, accountViewModel: AccountViewModel, - nav: Nav, + nav: INav, ) { val postViewModel: CommentPostViewModel = viewModel() postViewModel.init(accountViewModel) @@ -184,7 +184,7 @@ fun ReplyCommentPostScreen( fun GenericCommentPostScreen( postViewModel: CommentPostViewModel, accountViewModel: AccountViewModel, - nav: Nav, + nav: INav, ) { WatchAndLoadMyEmojiList(accountViewModel) @@ -279,7 +279,7 @@ fun GenericCommentPostScreen( private fun GenericCommentPostBody( postViewModel: CommentPostViewModel, accountViewModel: AccountViewModel, - nav: Nav, + nav: INav, ) { val scrollState = rememberScrollState() val audienceLists = rememberAudienceLists(accountViewModel) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/platform/AndroidNotePlatform.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/platform/AndroidNotePlatform.kt index a7b311234e..88f9dd929c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/platform/AndroidNotePlatform.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/platform/AndroidNotePlatform.kt @@ -83,6 +83,7 @@ import com.vitorpamplona.amethyst.ui.note.types.RenderNamedSiteEvent as AppRende import com.vitorpamplona.amethyst.ui.note.types.RenderRootNappletEvent as AppRenderRootNappletEvent import com.vitorpamplona.amethyst.ui.note.types.RenderRootSiteEvent as AppRenderRootSiteEvent import com.vitorpamplona.amethyst.ui.note.types.RenderVoiceTrack as AppRenderVoiceTrack +import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.feed.QuickZapAmountRow as AppQuickZapAmountRow /** Android's [NotePlatform]: the app's own media, map, audio, chess, git, napplet and zap composables. */ object AndroidNotePlatform : NotePlatform { @@ -621,6 +622,15 @@ object AndroidNotePlatform : NotePlatform { accountViewModel = accountViewModel, ) + @Composable + override fun QuickZapAmountRow( + note: Note, + onDismiss: () -> Unit, + onOnchainRequest: (Long?) -> Unit, + accountViewModel: AccountViewModel, + nav: INav, + ) = AppQuickZapAmountRow(note, onDismiss, onOnchainRequest, accountViewModel, nav) + @Composable override fun EditPostView( onClose: () -> Unit, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/platform/AndroidAppServices.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/platform/AndroidAppServices.kt index b31a64e385..6d66399b4a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/platform/AndroidAppServices.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/platform/AndroidAppServices.kt @@ -27,6 +27,7 @@ import com.vitorpamplona.amethyst.commons.connectedApps.signers.NostrSignerPermi import com.vitorpamplona.amethyst.commons.favorites.FavoriteAppsRegistry import com.vitorpamplona.amethyst.commons.napplet.permissions.NappletPermissionLedger import com.vitorpamplona.amethyst.commons.service.AppServices +import com.vitorpamplona.amethyst.commons.service.BlossomServerFinder import com.vitorpamplona.amethyst.commons.tor.TorSettingsFlow import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.NamecoinNameResolver @@ -49,4 +50,19 @@ object AndroidAppServices : AppServices { override val torSettings: TorSettingsFlow get() = Amethyst.instance.torPrefs.value override val namecoinResolver: NamecoinNameResolver get() = Amethyst.instance.namecoinResolver + + override val blossomServerFinder: BlossomServerFinder = AndroidBlossomServerFinder +} + +/** [BlossomServerFinder] over the app's BUD-10 resolver, read lazily (main process only). */ +private object AndroidBlossomServerFinder : BlossomServerFinder { + override fun cachedServerUrl(blossomUri: String): String? = + Amethyst.instance.blossomResolver + .cachedFindServer(blossomUri) + ?.serverUrl + + override suspend fun findServerUrl(blossomUri: String): String? = + Amethyst.instance.blossomResolver + .findServers(blossomUri) + ?.serverUrl } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/ChatMessageActionSheet.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/ChatMessageActionSheet.kt index 70fd734ab7..63f08bdc70 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/ChatMessageActionSheet.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/ChatMessageActionSheet.kt @@ -49,7 +49,6 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp @@ -59,7 +58,6 @@ import com.vitorpamplona.amethyst.commons.icons.symbols.Icon import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbol import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols import com.vitorpamplona.amethyst.commons.model.Note -import com.vitorpamplona.amethyst.commons.model.User import com.vitorpamplona.amethyst.commons.model.concord.ConcordChannel import com.vitorpamplona.amethyst.commons.model.navigation.Route import com.vitorpamplona.amethyst.commons.model.nip29RelayGroups.RelayGroupChannel @@ -69,16 +67,13 @@ import com.vitorpamplona.amethyst.commons.resources.buzz_edit_message import com.vitorpamplona.amethyst.commons.resources.chat_delivery_details_title import com.vitorpamplona.amethyst.commons.resources.edit_draft import com.vitorpamplona.amethyst.commons.resources.edit_message -import com.vitorpamplona.amethyst.commons.resources.error_dialog_zap_error import com.vitorpamplona.amethyst.commons.resources.more_options -import com.vitorpamplona.amethyst.commons.resources.no_wallet_found import com.vitorpamplona.amethyst.commons.resources.quick_action_delete_dialog_btn import com.vitorpamplona.amethyst.commons.resources.quick_action_request_deletion_alert_title import com.vitorpamplona.amethyst.commons.resources.relay_group_pin_message import com.vitorpamplona.amethyst.commons.resources.relay_group_unpin_message import com.vitorpamplona.amethyst.commons.resources.reply_description import com.vitorpamplona.amethyst.commons.resources.show_less -import com.vitorpamplona.amethyst.commons.service.ZapPaymentHandler import com.vitorpamplona.amethyst.commons.ui.components.ClickableBox import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.note.ChangeReactionIcon @@ -93,9 +88,9 @@ import com.vitorpamplona.amethyst.commons.ui.note.elements.ShareOptionsBottomShe import com.vitorpamplona.amethyst.commons.ui.note.elements.noteActionSections import com.vitorpamplona.amethyst.commons.ui.note.elements.observeBookmarksFollowsAndAccount import com.vitorpamplona.amethyst.commons.ui.note.platform.EditPostView +import com.vitorpamplona.amethyst.commons.ui.note.platform.QuickZapAmountRow import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.report.ReportNoteDialog import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.wallet.OnchainZapSendDialog -import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.wallet.navigateToReloadMint import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.ui.theme.DividerThickness import com.vitorpamplona.amethyst.commons.ui.theme.Size28Modifier @@ -104,17 +99,11 @@ import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText import com.vitorpamplona.amethyst.commons.ui.theme.reactionBox import com.vitorpamplona.amethyst.commons.ui.theme.selectedReactionBoxModifier import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel -import com.vitorpamplona.amethyst.ui.note.ZapAmountChoiceGrid -import com.vitorpamplona.amethyst.ui.note.observeZapRailCapability -import com.vitorpamplona.amethyst.ui.note.payViaIntentOrManualSplit import com.vitorpamplona.quartz.buzz.stream.StreamMessageV2Event import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip22Comments.CommentEvent import com.vitorpamplona.quartz.nipC7Chats.ChatEvent -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableSet -import kotlin.uuid.ExperimentalUuidApi // null amount = open the on-chain dialog with no prefill. @Immutable @@ -622,98 +611,3 @@ private fun QuickReactionRow( } } } - -/** - * The user's zap presets unpacked as rail-aware amount chips (same grid the zap - * amount popup shows), firing directly from the sheet. Lightning/cashu zaps - * dismiss immediately — errors surface as toasts and the receipt lands on the - * bubble's sats chip; on-chain amounts hand off to the dialog hosted by the sheet. - */ -@OptIn(ExperimentalUuidApi::class) -@Composable -private fun QuickZapAmountRow( - note: Note, - onDismiss: () -> Unit, - onOnchainRequest: (Long?) -> Unit, - accountViewModel: AccountViewModel, - nav: INav, -) { - val noWalletFoundStr = stringRes(Res.string.no_wallet_found) - val zapAmountChoices by - accountViewModel.account.settings.syncedSettings.zaps.zapAmountChoices - .collectAsStateWithLifecycle() - - val amountChoices = remember(zapAmountChoices) { zapAmountChoices.distinct().toImmutableList() } - if (amountChoices.isEmpty()) return - - val railCapability = - observeZapRailCapability( - baseNote = note, - accountViewModel = accountViewModel, - // Onchain zap events are public and would e-tag the rumor id. - onchainSupported = !note.isPrivateRumor(), - ) - - val context = LocalContext.current - - val onError = { _: String, message: String, user: User? -> - // Payment failed — drop the optimistic "zapping" indicator on the bubble. - accountViewModel.endZapInFlight(note.idHex) - accountViewModel.toastManager.toast(Res.string.error_dialog_zap_error, message, user) - } - - val onPayViaIntent = { payables: ImmutableList -> - // Handoff to an external wallet: we can't observe whether it completes, so clear - // the optimistic indicator rather than leave it spinning forever. - accountViewModel.endZapInFlight(note.idHex) - payViaIntentOrManualSplit(payables, context, noWalletFoundStr, accountViewModel, nav) - } - - Row( - modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp), - horizontalArrangement = Arrangement.Center, - ) { - ZapAmountChoiceGrid( - amountChoices = amountChoices, - railCapability = railCapability, - onLightningZap = { amountInSats -> - // Show a pending zap chip on the bubble immediately; it settles into the - // real sats chip once the receipt lands (or clears on error/timeout). - accountViewModel.markZapInFlight(note.idHex) - accountViewModel.zap( - note, - amountInSats * 1000, - null, - "", - true, - onError, - { }, - onPayViaIntent, - ) - onDismiss() - }, - onNutzap = { amountInSats -> - accountViewModel.markZapInFlight(note.idHex) - accountViewModel.sendNutzap( - baseNote = note, - amountSats = amountInSats, - message = "", - onError = onError, - onProgress = { }, - ) - onDismiss() - }, - onOnchainAmount = onOnchainRequest, - onReloadNutzap = { amount -> - navigateToReloadMint(accountViewModel, nav, note, amount) - onDismiss() - }, - onChangeAmount = { - nav.nav(Route.UpdateZapAmount()) - onDismiss() - }, - // Hands off to another app; the sheet must not stay stacked behind it. - onHandedOff = onDismiss, - ) - } -} diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/ChatMessageCompose.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/ChatMessageCompose.kt index bb2cf0cb60..8117997bf7 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/ChatMessageCompose.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/ChatMessageCompose.kt @@ -56,6 +56,7 @@ import com.vitorpamplona.amethyst.commons.model.navigation.routeFor import com.vitorpamplona.amethyst.commons.ui.components.LocalInlineQuoteRenderer import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.routes.routeFor +import com.vitorpamplona.amethyst.commons.ui.note.RenderZapRaiser import com.vitorpamplona.amethyst.commons.ui.note.WatchBlockAndReport import com.vitorpamplona.amethyst.commons.ui.note.WatchNoteEvent import com.vitorpamplona.amethyst.commons.ui.note.creators.zapsplits.DisplayZapSplits @@ -66,7 +67,6 @@ import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.chats.feed.types.Re import com.vitorpamplona.amethyst.commons.ui.theme.ReactionRowZapraiser import com.vitorpamplona.amethyst.commons.ui.theme.StdVertSpacer import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel -import com.vitorpamplona.amethyst.ui.note.RenderZapRaiser import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.feed.types.RenderBuzzActivityRow import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.feed.types.RenderBuzzDiff import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.feed.types.RenderBuzzEditedNote diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/ChatReactionChips.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/ChatReactionChips.kt index 9ffea11b26..6524f49c9f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/ChatReactionChips.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/ChatReactionChips.kt @@ -66,6 +66,7 @@ import com.vitorpamplona.amethyst.commons.resources.chat_minichat_reply_count import com.vitorpamplona.amethyst.commons.ui.components.AnimatedBorderTextCornerRadius import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.note.LikedIcon +import com.vitorpamplona.amethyst.commons.ui.note.ObserveZapAmountText import com.vitorpamplona.amethyst.commons.ui.note.ZappedIcon import com.vitorpamplona.amethyst.commons.ui.pluralStringRes import com.vitorpamplona.amethyst.commons.ui.richtext.InLineIconRenderer @@ -76,7 +77,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.bitcoinColor import com.vitorpamplona.amethyst.commons.ui.theme.grayText import com.vitorpamplona.amethyst.commons.ui.theme.subtleBorder import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel -import com.vitorpamplona.amethyst.ui.note.ObserveZapAmountText import com.vitorpamplona.quartz.nip30CustomEmoji.CustomEmoji import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/QuickZapAmountRow.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/QuickZapAmountRow.kt new file mode 100644 index 0000000000..00e43f4b4a --- /dev/null +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/QuickZapAmountRow.kt @@ -0,0 +1,145 @@ +/* + * 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.screen.loggedIn.chats.feed + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.vitorpamplona.amethyst.commons.model.Note +import com.vitorpamplona.amethyst.commons.model.User +import com.vitorpamplona.amethyst.commons.model.navigation.Route +import com.vitorpamplona.amethyst.commons.resources.Res +import com.vitorpamplona.amethyst.commons.resources.error_dialog_zap_error +import com.vitorpamplona.amethyst.commons.resources.no_wallet_found +import com.vitorpamplona.amethyst.commons.service.ZapPaymentHandler +import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav +import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.wallet.navigateToReloadMint +import com.vitorpamplona.amethyst.commons.ui.stringRes +import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel +import com.vitorpamplona.amethyst.ui.note.ZapAmountChoiceGrid +import com.vitorpamplona.amethyst.ui.note.observeZapRailCapability +import com.vitorpamplona.amethyst.ui.note.payViaIntentOrManualSplit +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.toImmutableList +import kotlin.uuid.ExperimentalUuidApi + +/** + * The user's zap presets unpacked as rail-aware amount chips (same grid the zap + * amount popup shows), firing directly from the sheet. Lightning/cashu zaps + * dismiss immediately — errors surface as toasts and the receipt lands on the + * bubble's sats chip; on-chain amounts hand off to the dialog hosted by the sheet. + */ +@OptIn(ExperimentalUuidApi::class) +@Composable +fun QuickZapAmountRow( + note: Note, + onDismiss: () -> Unit, + onOnchainRequest: (Long?) -> Unit, + accountViewModel: AccountViewModel, + nav: INav, +) { + val noWalletFoundStr = stringRes(Res.string.no_wallet_found) + val zapAmountChoices by + accountViewModel.account.settings.syncedSettings.zaps.zapAmountChoices + .collectAsStateWithLifecycle() + + val amountChoices = remember(zapAmountChoices) { zapAmountChoices.distinct().toImmutableList() } + if (amountChoices.isEmpty()) return + + val railCapability = + observeZapRailCapability( + baseNote = note, + accountViewModel = accountViewModel, + // Onchain zap events are public and would e-tag the rumor id. + onchainSupported = !note.isPrivateRumor(), + ) + + val context = LocalContext.current + + val onError = { _: String, message: String, user: User? -> + // Payment failed — drop the optimistic "zapping" indicator on the bubble. + accountViewModel.endZapInFlight(note.idHex) + accountViewModel.toastManager.toast(Res.string.error_dialog_zap_error, message, user) + } + + val onPayViaIntent = { payables: ImmutableList -> + // Handoff to an external wallet: we can't observe whether it completes, so clear + // the optimistic indicator rather than leave it spinning forever. + accountViewModel.endZapInFlight(note.idHex) + payViaIntentOrManualSplit(payables, context, noWalletFoundStr, accountViewModel, nav) + } + + Row( + modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp), + horizontalArrangement = Arrangement.Center, + ) { + ZapAmountChoiceGrid( + amountChoices = amountChoices, + railCapability = railCapability, + onLightningZap = { amountInSats -> + // Show a pending zap chip on the bubble immediately; it settles into the + // real sats chip once the receipt lands (or clears on error/timeout). + accountViewModel.markZapInFlight(note.idHex) + accountViewModel.zap( + note, + amountInSats * 1000, + null, + "", + true, + onError, + { }, + onPayViaIntent, + ) + onDismiss() + }, + onNutzap = { amountInSats -> + accountViewModel.markZapInFlight(note.idHex) + accountViewModel.sendNutzap( + baseNote = note, + amountSats = amountInSats, + message = "", + onError = onError, + onProgress = { }, + ) + onDismiss() + }, + onOnchainAmount = onOnchainRequest, + onReloadNutzap = { amount -> + navigateToReloadMint(accountViewModel, nav, note, amount) + onDismiss() + }, + onChangeAmount = { + nav.nav(Route.UpdateZapAmount()) + onDismiss() + }, + // Hands off to another app; the sheet must not stay stacked behind it. + onHandedOff = onDismiss, + ) + } +} diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/marmotGroup/MarmotGroupIconDisplay.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/marmotGroup/MarmotGroupIconDisplay.kt index 7707996ad9..ba1c9dd33f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/marmotGroup/MarmotGroupIconDisplay.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/marmotGroup/MarmotGroupIconDisplay.kt @@ -24,9 +24,10 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.produceState import androidx.compose.runtime.remember -import com.vitorpamplona.amethyst.Amethyst import com.vitorpamplona.amethyst.commons.model.marmotGroups.MarmotGroupImage import com.vitorpamplona.amethyst.commons.model.nip11RelayInfo.loadRelayInfo +import com.vitorpamplona.amethyst.commons.service.BlossomServerFinder +import com.vitorpamplona.amethyst.commons.ui.platform.LocalAppServices import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel import com.vitorpamplona.quartz.marmot.appComponents.GroupAvatarUrlV1 import com.vitorpamplona.quartz.marmot.appComponents.MarmotWebUrl @@ -43,7 +44,7 @@ import com.vitorpamplona.quartz.nipB7Blossom.BlossomUri * * The blob is content-addressed on Blossom by [MarmotGroupImage.hash]; the canonical * scheme stores only the hash, and the icon usually lives on the *uploader's* Blossom - * server rather than the viewer's. So we resolve it through [Amethyst.blossomResolver], + * server rather than the viewer's. So we resolve it through the app's BUD-10 resolver ([BlossomServerFinder]), * which probes the viewer's default server (passed as a first-try `xs` hint) and the * group admins' configured servers (via their pubkeys as `as` authors, BUD-03). While * that async probe runs, we optimistically load from the viewer's default server so the @@ -77,9 +78,9 @@ fun rememberMarmotGroupIconUrl( ).toUriString() } - val resolver = Amethyst.instance.blossomResolver - val url by produceState(resolver.cachedFindServer(blossomUri)?.serverUrl ?: fallbackUrl, blossomUri) { - value = resolver.findServers(blossomUri)?.serverUrl ?: fallbackUrl + val finder = LocalAppServices.current.blossomServerFinder + val url by produceState(finder.cachedServerUrl(blossomUri) ?: fallbackUrl, blossomUri) { + value = finder.findServerUrl(blossomUri) ?: fallbackUrl } val cipher = remember(image) { MarmotGroupImageCipher(image.key, image.nonce) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/NewGroupDMScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/NewGroupDMScreen.kt index d1011cd08b..d930043923 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/NewGroupDMScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/NewGroupDMScreen.kt @@ -125,7 +125,6 @@ import com.vitorpamplona.amethyst.ui.actions.UrlUserTagOutputTransformation import com.vitorpamplona.amethyst.ui.actions.uploads.TakePictureButton import com.vitorpamplona.amethyst.ui.actions.uploads.TakeVideoButton import com.vitorpamplona.amethyst.ui.components.ThinPaddingTextField -import com.vitorpamplona.amethyst.ui.navigation.navs.Nav import com.vitorpamplona.amethyst.ui.note.creators.emojiSuggestions.WatchAndLoadMyEmojiList import com.vitorpamplona.amethyst.ui.note.creators.expiration.ExpirationDatePicker import com.vitorpamplona.amethyst.ui.note.creators.invoice.NewPostInvoiceRequest @@ -148,7 +147,7 @@ fun NewGroupDMScreen( message: String? = null, attachment: String? = null, accountViewModel: AccountViewModel, - nav: Nav, + nav: INav, ) { NewGroupDMScreen(message, attachment?.ifBlank { null }?.toUri(), accountViewModel, nav) } @@ -159,7 +158,7 @@ fun NewGroupDMScreen( message: String? = null, attachment: Uri? = null, accountViewModel: AccountViewModel, - nav: Nav, + nav: INav, ) { val postViewModel: ChatNewMessageViewModel = viewModel() postViewModel.init(accountViewModel) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/ChannelNewMessageViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/ChannelNewMessageViewModel.kt index 68ae70fdf0..da332f094c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/ChannelNewMessageViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/ChannelNewMessageViewModel.kt @@ -136,6 +136,8 @@ import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import kotlin.coroutines.cancellation.CancellationException +import kotlin.time.Duration.Companion.seconds +import kotlin.time.TimeSource @Stable open class ChannelNewMessageViewModel : @@ -465,10 +467,9 @@ open class ChannelNewMessageViewModel : val mined = withContext(Dispatchers.Default) { // Bitchat mines 8 bits by default; cap the effort so a slow device still sends. - val deadline = System.nanoTime() + 2_000_000_000L - val threads = Runtime.getRuntime().availableProcessors().coerceAtLeast(1) + val started = TimeSource.Monotonic.markNow() runCatching { - PoWMiner.mine(template, pubKeyHex, 8, threads, isActive = { System.nanoTime() < deadline }) + PoWMiner.mine(template, pubKeyHex, 8, account.powMinerWorkers(), isActive = { started.elapsedNow() < 2.seconds }) }.getOrDefault(template) } @@ -1051,7 +1052,7 @@ open class ChannelNewMessageViewModel : } override fun onCleared() { - Log.d("Init") { "OnCleared: ${this.javaClass.simpleName}" } + Log.d("Init") { "OnCleared: ${this::class.simpleName}" } } fun updateZapPercentage( diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip23LongForm/LongFormPostScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip23LongForm/LongFormPostScreen.kt index 14a038c7db..d7427acbc0 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip23LongForm/LongFormPostScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip23LongForm/LongFormPostScreen.kt @@ -107,6 +107,7 @@ import com.vitorpamplona.amethyst.commons.ui.actions.uploads.SelectFromGallery import com.vitorpamplona.amethyst.commons.ui.components.MyAsyncImage import com.vitorpamplona.amethyst.commons.ui.components.PlatformBackHandler import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe +import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.PostingTopBar import com.vitorpamplona.amethyst.commons.ui.note.creators.contentWarning.ContentSensitivityExplainer import com.vitorpamplona.amethyst.commons.ui.note.creators.contentWarning.MarkAsSensitiveButton @@ -133,7 +134,6 @@ import com.vitorpamplona.amethyst.ui.actions.uploads.TakePictureButton import com.vitorpamplona.amethyst.ui.actions.uploads.TakeVideoButton import com.vitorpamplona.amethyst.ui.components.ThinPaddingTextField import com.vitorpamplona.amethyst.ui.components.markdown.RenderContentAsMarkdown -import com.vitorpamplona.amethyst.ui.navigation.navs.Nav import com.vitorpamplona.amethyst.ui.note.creators.emojiSuggestions.WatchAndLoadMyEmojiList import com.vitorpamplona.amethyst.ui.note.creators.expiration.ExpirationDatePicker import com.vitorpamplona.amethyst.ui.note.creators.invoice.InvoiceRequest @@ -150,7 +150,7 @@ fun LongFormPostScreen( draftId: HexKey? = null, versionId: HexKey? = null, accountViewModel: AccountViewModel, - nav: Nav, + nav: INav, ) { val postViewModel: LongFormPostViewModel = viewModel() postViewModel.init(accountViewModel) @@ -214,7 +214,7 @@ fun LongFormPostScreen( private fun MarkdownPostScreenBody( postViewModel: LongFormPostViewModel, accountViewModel: AccountViewModel, - nav: Nav, + nav: INav, ) { val scrollState = rememberScrollState() Column( diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip99Classifieds/NewProductScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip99Classifieds/NewProductScreen.kt index b06be8a622..ca063ad739 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip99Classifieds/NewProductScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip99Classifieds/NewProductScreen.kt @@ -80,7 +80,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.SuggestionListDefaultHeightPa import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel import com.vitorpamplona.amethyst.ui.actions.uploads.TakePictureButton import com.vitorpamplona.amethyst.ui.actions.uploads.TakeVideoButton -import com.vitorpamplona.amethyst.ui.navigation.navs.Nav import com.vitorpamplona.amethyst.ui.note.creators.emojiSuggestions.WatchAndLoadMyEmojiList import com.vitorpamplona.amethyst.ui.note.creators.expiration.ExpirationDatePicker import com.vitorpamplona.amethyst.ui.note.creators.invoice.InvoiceRequest @@ -103,7 +102,7 @@ fun NewProductScreen( quoteId: HexKey? = null, draftId: HexKey? = null, accountViewModel: AccountViewModel, - nav: Nav, + nav: INav, ) { val postViewModel: NewProductViewModel = viewModel() postViewModel.init(accountViewModel) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geohash/GeoHashPostScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geohash/GeoHashPostScreen.kt index c5f9554506..1f552025d4 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geohash/GeoHashPostScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geohash/GeoHashPostScreen.kt @@ -28,8 +28,8 @@ import androidx.core.net.toUri import androidx.lifecycle.viewmodel.compose.viewModel import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.service.uploads.SelectedMedia +import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel -import com.vitorpamplona.amethyst.ui.navigation.navs.Nav import com.vitorpamplona.amethyst.ui.note.nip22Comments.CommentPostViewModel import com.vitorpamplona.amethyst.ui.note.nip22Comments.GenericCommentPostScreen import com.vitorpamplona.quartz.nip01Core.core.HexKey @@ -47,7 +47,7 @@ fun GeoHashPostScreen( quoteId: HexKey? = null, draftId: HexKey? = null, accountViewModel: AccountViewModel, - nav: Nav, + nav: INav, ) { val postViewModel: CommentPostViewModel = viewModel() postViewModel.init(accountViewModel) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/hashtag/HashtagPostScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/hashtag/HashtagPostScreen.kt index f82ffcca8b..680e6808d6 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/hashtag/HashtagPostScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/hashtag/HashtagPostScreen.kt @@ -28,8 +28,8 @@ import androidx.core.net.toUri import androidx.lifecycle.viewmodel.compose.viewModel import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.service.uploads.SelectedMedia +import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel -import com.vitorpamplona.amethyst.ui.navigation.navs.Nav import com.vitorpamplona.amethyst.ui.note.nip22Comments.CommentPostViewModel import com.vitorpamplona.amethyst.ui.note.nip22Comments.GenericCommentPostScreen import com.vitorpamplona.quartz.nip01Core.core.HexKey @@ -47,7 +47,7 @@ fun HashtagPostScreen( quoteId: HexKey? = null, draftId: HexKey? = null, accountViewModel: AccountViewModel, - nav: Nav, + nav: INav, ) { val postViewModel: CommentPostViewModel = viewModel() postViewModel.init(accountViewModel) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/highlights/NewHighlightScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/highlights/NewHighlightScreen.kt index 8358b47b80..fbc32c1b3f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/highlights/NewHighlightScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/highlights/NewHighlightScreen.kt @@ -72,6 +72,7 @@ import com.vitorpamplona.amethyst.commons.resources.new_highlight_passage_placeh import com.vitorpamplona.amethyst.commons.resources.new_highlight_source_label import com.vitorpamplona.amethyst.commons.resources.new_highlight_title import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe +import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.PostingTopBar import com.vitorpamplona.amethyst.commons.ui.note.NoteCompose import com.vitorpamplona.amethyst.commons.ui.note.creators.userSuggestions.ShowUserSuggestionList @@ -80,7 +81,6 @@ import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.ui.theme.SuggestionListDefaultHeightPage import com.vitorpamplona.amethyst.commons.ui.theme.replyModifier import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel -import com.vitorpamplona.amethyst.ui.navigation.navs.Nav import com.vitorpamplona.amethyst.ui.note.creators.emojiSuggestions.WatchAndLoadMyEmojiList import com.vitorpamplona.amethyst.ui.note.creators.messagefield.MessageField @@ -111,7 +111,7 @@ fun NewHighlightScreen( sourceEventId: String? = null, author: String? = null, accountViewModel: AccountViewModel, - nav: Nav, + nav: INav, ) { val postViewModel: NewHighlightPostViewModel = viewModel() postViewModel.init(accountViewModel) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostScreen.kt index de50091951..e8d6202d5f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostScreen.kt @@ -114,6 +114,7 @@ import com.vitorpamplona.amethyst.commons.ui.actions.uploads.SelectFromFiles import com.vitorpamplona.amethyst.commons.ui.actions.uploads.SelectFromGallery import com.vitorpamplona.amethyst.commons.ui.components.PlatformBackHandler import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe +import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.PostingTopBar import com.vitorpamplona.amethyst.commons.ui.note.BaseUserPicture import com.vitorpamplona.amethyst.commons.ui.note.NoteCompose @@ -156,7 +157,6 @@ import com.vitorpamplona.amethyst.ui.actions.uploads.VoiceAnonymizationSection import com.vitorpamplona.amethyst.ui.actions.uploads.VoiceMessagePreview import com.vitorpamplona.amethyst.ui.components.ThinPaddingTextField import com.vitorpamplona.amethyst.ui.components.getActivity -import com.vitorpamplona.amethyst.ui.navigation.navs.Nav import com.vitorpamplona.amethyst.ui.note.creators.emojiSuggestions.WatchAndLoadMyEmojiList import com.vitorpamplona.amethyst.ui.note.creators.expiration.ExpirationDatePicker import com.vitorpamplona.amethyst.ui.note.creators.invoice.InvoiceRequest @@ -195,7 +195,7 @@ fun ShortNotePostScreen( groupThreadId: HexKey? = null, groupThreadRelayUrl: String? = null, accountViewModel: AccountViewModel, - nav: Nav, + nav: INav, ) { val postViewModel: ShortNotePostViewModel = viewModel() postViewModel.init(accountViewModel) @@ -269,7 +269,7 @@ fun ShortNotePostScreen( internal fun NewPostScreenInner( postViewModel: ShortNotePostViewModel, accountViewModel: AccountViewModel, - nav: Nav, + nav: INav, ) { WatchAndLoadMyEmojiList(accountViewModel) @@ -324,7 +324,7 @@ internal fun NewPostScreenInner( private fun NewPostScreenBody( postViewModel: ShortNotePostViewModel, accountViewModel: AccountViewModel, - nav: Nav, + nav: INav, ) { val scrollState = rememberScrollState() val audienceLists = rememberAudienceLists(accountViewModel) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/VoiceReplyScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/VoiceReplyScreen.kt index f9b5232f26..43185481ab 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/VoiceReplyScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/VoiceReplyScreen.kt @@ -40,6 +40,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import com.vitorpamplona.amethyst.commons.ui.components.PlatformBackHandler +import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.PostingTopBar import com.vitorpamplona.amethyst.commons.ui.note.NoteCompose import com.vitorpamplona.amethyst.commons.ui.note.types.ReplyRenderType @@ -51,7 +52,6 @@ import com.vitorpamplona.amethyst.ui.actions.mediaServers.FileServerSelectionRow import com.vitorpamplona.amethyst.ui.actions.uploads.UploadProgressIndicator import com.vitorpamplona.amethyst.ui.actions.uploads.VoiceAnonymizationSection import com.vitorpamplona.amethyst.ui.actions.uploads.VoiceMessagePreview -import com.vitorpamplona.amethyst.ui.navigation.navs.Nav @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -62,7 +62,7 @@ fun VoiceReplyScreen( duration: Int, amplitudesJson: String, accountViewModel: AccountViewModel, - nav: Nav, + nav: INav, ) { val viewModel: VoiceReplyViewModel = viewModel() @@ -107,7 +107,7 @@ fun VoiceReplyScreen( private fun VoiceReplyScreenBody( viewModel: VoiceReplyViewModel, accountViewModel: AccountViewModel, - nav: Nav, + nav: INav, ) { val scrollState = rememberScrollState() diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/nip75Goals/NewGoalScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/nip75Goals/NewGoalScreen.kt index fe49ecb469..8c7ca1af77 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/nip75Goals/NewGoalScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/nip75Goals/NewGoalScreen.kt @@ -64,13 +64,12 @@ import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.PostingTopBar import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel -import com.vitorpamplona.amethyst.ui.navigation.navs.Nav @OptIn(ExperimentalMaterial3Api::class) @Composable fun NewGoalScreen( accountViewModel: AccountViewModel, - nav: Nav, + nav: INav, ) { val goalViewModel: NewGoalViewModel = viewModel() goalViewModel.init(accountViewModel) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/publicMessages/NewPublicMessageScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/publicMessages/NewPublicMessageScreen.kt index b3b9bce7ae..6fb130a3de 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/publicMessages/NewPublicMessageScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/publicMessages/NewPublicMessageScreen.kt @@ -94,7 +94,6 @@ import com.vitorpamplona.amethyst.ui.actions.UrlUserTagOutputTransformation import com.vitorpamplona.amethyst.ui.actions.uploads.TakePictureButton import com.vitorpamplona.amethyst.ui.actions.uploads.TakeVideoButton import com.vitorpamplona.amethyst.ui.components.ThinPaddingTextField -import com.vitorpamplona.amethyst.ui.navigation.navs.Nav import com.vitorpamplona.amethyst.ui.note.creators.emojiSuggestions.WatchAndLoadMyEmojiList import com.vitorpamplona.amethyst.ui.note.creators.expiration.ExpirationDatePicker import com.vitorpamplona.amethyst.ui.note.creators.invoice.NewPostInvoiceRequest @@ -118,7 +117,7 @@ fun NewPublicMessageScreen( replyId: HexKey? = null, draftId: HexKey? = null, accountViewModel: AccountViewModel, - nav: Nav, + nav: INav, ) { val postViewModel: NewPublicMessageViewModel = viewModel() postViewModel.init(accountViewModel) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/polls/PollPostScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/polls/PollPostScreen.kt index 40f7184fbc..adecdff72b 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/polls/PollPostScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/polls/PollPostScreen.kt @@ -26,8 +26,8 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.lifecycle.viewmodel.compose.viewModel import com.vitorpamplona.amethyst.commons.model.cache.LocalCache +import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel -import com.vitorpamplona.amethyst.ui.navigation.navs.Nav import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.NewPostScreenInner import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.ShortNotePostViewModel import com.vitorpamplona.quartz.nip01Core.core.HexKey @@ -39,7 +39,7 @@ fun PollPostScreen( message: String? = null, draftId: HexKey? = null, accountViewModel: AccountViewModel, - nav: Nav, + nav: INav, ) { val postViewModel: ShortNotePostViewModel = viewModel() postViewModel.init(accountViewModel) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/AllRelayListScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/AllRelayListScreen.kt index abbf158297..9a4eba42dc 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/AllRelayListScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/AllRelayListScreen.kt @@ -40,7 +40,6 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import com.vitorpamplona.amethyst.commons.defaults.DefaultDMRelayList @@ -87,9 +86,14 @@ import com.vitorpamplona.amethyst.commons.resources.trusted_section_explainer import com.vitorpamplona.amethyst.commons.ui.components.M3ActionDialog import com.vitorpamplona.amethyst.commons.ui.components.M3ActionRow import com.vitorpamplona.amethyst.commons.ui.components.M3ActionSection +import com.vitorpamplona.amethyst.commons.ui.components.rememberFileSharer +import com.vitorpamplona.amethyst.commons.ui.components.rememberTextSharer import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.SavingTopBar +import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.RelayExporter +import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.RelayListCollection +import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.RelayZipExporter import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.relaySetupInfoBuilder import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.ui.theme.FeedPadding @@ -102,9 +106,6 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.blocked.BlockedRelay import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.blocked.renderBlockedItems import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.broadcast.BroadcastRelayListViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.broadcast.renderBroadcastItems -import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.RelayExporter -import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.RelayListCollection -import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.RelayZipExporter import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.connected.ConnectedRelayListViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.connected.renderConnectedItems import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.dm.DMRelayListViewModel @@ -621,7 +622,8 @@ fun SettingsCategoryWithButton( @Composable fun ExportDropdownMenu(collection: () -> RelayListCollection) { var expanded by remember { mutableStateOf(false) } - val context = LocalContext.current + val textSharer = rememberTextSharer() + val fileSharer = rememberFileSharer() val scope = rememberCoroutineScope() IconButton(onClick = { expanded = true }) { @@ -642,14 +644,14 @@ fun ExportDropdownMenu(collection: () -> RelayListCollection) { text = stringRes(Res.string.export_as_text), ) { expanded = false - scope.launch { RelayExporter(context).export(collection()) } + scope.launch { RelayExporter(textSharer).export(collection()) } } M3ActionRow( icon = MaterialSymbols.FolderZip, text = stringRes(Res.string.export_as_zip), ) { expanded = false - scope.launch { RelayZipExporter(context).export(collection()) } + scope.launch { RelayZipExporter(fileSharer).export(collection()) } } } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/blocked/BlockedRelayListView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/blocked/BlockedRelayListView.kt index 976150bbe1..860c331302 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/blocked/BlockedRelayListView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/blocked/BlockedRelayListView.kt @@ -33,6 +33,7 @@ import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.relays.ui.RelayDragState import com.vitorpamplona.amethyst.commons.relays.ui.rememberRelayDragState import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav +import com.vitorpamplona.amethyst.commons.ui.navigation.navs.rememberExtendedNav import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.BasicRelaySetupInfo import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.RelayUrlEditField import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.relaySetupInfoBuilder @@ -41,7 +42,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.HorzHalfVertPadding import com.vitorpamplona.amethyst.commons.ui.theme.HorzPadding import com.vitorpamplona.amethyst.commons.ui.theme.StdVertSpacer import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel -import com.vitorpamplona.amethyst.ui.navigation.navs.rememberExtendedNav import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfoDialog @Composable diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/broadcast/BroadcastRelayListView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/broadcast/BroadcastRelayListView.kt index 127c8df427..1fed5e4fbc 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/broadcast/BroadcastRelayListView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/broadcast/BroadcastRelayListView.kt @@ -33,6 +33,7 @@ import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.relays.ui.RelayDragState import com.vitorpamplona.amethyst.commons.relays.ui.rememberRelayDragState import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav +import com.vitorpamplona.amethyst.commons.ui.navigation.navs.rememberExtendedNav import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.BasicRelaySetupInfo import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.RelayUrlEditField import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.relaySetupInfoBuilder @@ -41,7 +42,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.HorzHalfVertPadding import com.vitorpamplona.amethyst.commons.ui.theme.HorzPadding import com.vitorpamplona.amethyst.commons.ui.theme.StdVertSpacer import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel -import com.vitorpamplona.amethyst.ui.navigation.navs.rememberExtendedNav import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfoDialog @Composable diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/connected/ConnectedRelayListView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/connected/ConnectedRelayListView.kt index b926c58619..a2bbc82c0d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/connected/ConnectedRelayListView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/connected/ConnectedRelayListView.kt @@ -41,12 +41,12 @@ import com.vitorpamplona.amethyst.commons.model.navigation.Route import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.active_subs_title import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav +import com.vitorpamplona.amethyst.commons.ui.navigation.navs.rememberExtendedNav import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.BasicRelaySetupInfo import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.ui.theme.FeedPadding import com.vitorpamplona.amethyst.commons.ui.theme.HorzHalfVertPadding import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel -import com.vitorpamplona.amethyst.ui.navigation.navs.rememberExtendedNav import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfoDialog @Composable diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/dm/DMRelayListView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/dm/DMRelayListView.kt index 8b5680b539..44ce8b212d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/dm/DMRelayListView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/dm/DMRelayListView.kt @@ -34,6 +34,7 @@ import com.vitorpamplona.amethyst.commons.relays.ui.RelayCountResult import com.vitorpamplona.amethyst.commons.relays.ui.RelayDragState import com.vitorpamplona.amethyst.commons.relays.ui.rememberRelayDragState import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav +import com.vitorpamplona.amethyst.commons.ui.navigation.navs.rememberExtendedNav import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.BasicRelaySetupInfo import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.RelayUrlEditField import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.relaySetupInfoBuilder @@ -42,7 +43,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.HorzHalfVertPadding import com.vitorpamplona.amethyst.commons.ui.theme.HorzPadding import com.vitorpamplona.amethyst.commons.ui.theme.StdVertSpacer import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel -import com.vitorpamplona.amethyst.ui.navigation.navs.rememberExtendedNav import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfoDialog import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/feeds/RelayFeedsListView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/feeds/RelayFeedsListView.kt index 85949dcc17..3c29b4ddba 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/feeds/RelayFeedsListView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/feeds/RelayFeedsListView.kt @@ -33,6 +33,7 @@ import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.relays.ui.RelayDragState import com.vitorpamplona.amethyst.commons.relays.ui.rememberRelayDragState import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav +import com.vitorpamplona.amethyst.commons.ui.navigation.navs.rememberExtendedNav import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.BasicRelaySetupInfo import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.RelayUrlEditField import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.relaySetupInfoBuilder @@ -41,7 +42,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.HorzHalfVertPadding import com.vitorpamplona.amethyst.commons.ui.theme.HorzPadding import com.vitorpamplona.amethyst.commons.ui.theme.StdVertSpacer import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel -import com.vitorpamplona.amethyst.ui.navigation.navs.rememberExtendedNav import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfoDialog @Composable diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/indexer/IndexerRelayListView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/indexer/IndexerRelayListView.kt index 5360bd9692..09314f3b30 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/indexer/IndexerRelayListView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/indexer/IndexerRelayListView.kt @@ -34,6 +34,7 @@ import com.vitorpamplona.amethyst.commons.relays.ui.RelayCountResult import com.vitorpamplona.amethyst.commons.relays.ui.RelayDragState import com.vitorpamplona.amethyst.commons.relays.ui.rememberRelayDragState import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav +import com.vitorpamplona.amethyst.commons.ui.navigation.navs.rememberExtendedNav import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.BasicRelaySetupInfo import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.RelayUrlEditField import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.relaySetupInfoBuilder @@ -42,7 +43,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.HorzHalfVertPadding import com.vitorpamplona.amethyst.commons.ui.theme.HorzPadding import com.vitorpamplona.amethyst.commons.ui.theme.StdVertSpacer import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel -import com.vitorpamplona.amethyst.ui.navigation.navs.rememberExtendedNav import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfoDialog import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/keyPackage/KeyPackageRelayListView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/keyPackage/KeyPackageRelayListView.kt index 8b6e3ad4a5..29357db979 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/keyPackage/KeyPackageRelayListView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/keyPackage/KeyPackageRelayListView.kt @@ -34,6 +34,7 @@ import com.vitorpamplona.amethyst.commons.relays.ui.RelayCountResult import com.vitorpamplona.amethyst.commons.relays.ui.RelayDragState import com.vitorpamplona.amethyst.commons.relays.ui.rememberRelayDragState import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav +import com.vitorpamplona.amethyst.commons.ui.navigation.navs.rememberExtendedNav import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.BasicRelaySetupInfo import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.RelayUrlEditField import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.relaySetupInfoBuilder @@ -42,7 +43,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.HorzHalfVertPadding import com.vitorpamplona.amethyst.commons.ui.theme.HorzPadding import com.vitorpamplona.amethyst.commons.ui.theme.StdVertSpacer import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel -import com.vitorpamplona.amethyst.ui.navigation.navs.rememberExtendedNav import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfoDialog import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/local/LocalRelayListView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/local/LocalRelayListView.kt index 3580d9c981..1b9fdab81a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/local/LocalRelayListView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/local/LocalRelayListView.kt @@ -37,6 +37,7 @@ import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.send_kind0_to_local_relay_description import com.vitorpamplona.amethyst.commons.resources.send_kind0_to_local_relay_title import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav +import com.vitorpamplona.amethyst.commons.ui.navigation.navs.rememberExtendedNav import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.BasicRelaySetupInfo import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.RelayUrlEditField import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.relaySetupInfoBuilder @@ -47,7 +48,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.HorzPadding import com.vitorpamplona.amethyst.commons.ui.theme.SettingsCategorySpacingWithHorzBorderModifier import com.vitorpamplona.amethyst.commons.ui.theme.StdVertSpacer import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel -import com.vitorpamplona.amethyst.ui.navigation.navs.rememberExtendedNav import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfoDialog @Composable diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/nip37/PrivateOutboxRelayListView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/nip37/PrivateOutboxRelayListView.kt index 05e0fa25a9..283f6e4ed2 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/nip37/PrivateOutboxRelayListView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/nip37/PrivateOutboxRelayListView.kt @@ -34,6 +34,7 @@ import com.vitorpamplona.amethyst.commons.relays.ui.RelayCountResult import com.vitorpamplona.amethyst.commons.relays.ui.RelayDragState import com.vitorpamplona.amethyst.commons.relays.ui.rememberRelayDragState import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav +import com.vitorpamplona.amethyst.commons.ui.navigation.navs.rememberExtendedNav import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.BasicRelaySetupInfo import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.RelayUrlEditField import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.relaySetupInfoBuilder @@ -42,7 +43,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.HorzHalfVertPadding import com.vitorpamplona.amethyst.commons.ui.theme.HorzPadding import com.vitorpamplona.amethyst.commons.ui.theme.StdVertSpacer import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel -import com.vitorpamplona.amethyst.ui.navigation.navs.rememberExtendedNav import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfoDialog import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/nip65/Nip65RelayListView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/nip65/Nip65RelayListView.kt index 36e17be0e1..1c5f851d10 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/nip65/Nip65RelayListView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/nip65/Nip65RelayListView.kt @@ -34,6 +34,7 @@ import com.vitorpamplona.amethyst.commons.relays.ui.RelayCountResult import com.vitorpamplona.amethyst.commons.relays.ui.RelayDragState import com.vitorpamplona.amethyst.commons.relays.ui.rememberRelayDragState import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav +import com.vitorpamplona.amethyst.commons.ui.navigation.navs.rememberExtendedNav import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.BasicRelaySetupInfo import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.RelayUrlEditField import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.relaySetupInfoBuilder @@ -42,7 +43,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.HorzHalfVertPadding import com.vitorpamplona.amethyst.commons.ui.theme.HorzPadding import com.vitorpamplona.amethyst.commons.ui.theme.StdVertSpacer import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel -import com.vitorpamplona.amethyst.ui.navigation.navs.rememberExtendedNav import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfoDialog import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/proxy/ProxyRelayListView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/proxy/ProxyRelayListView.kt index bb6f268bb5..bbfc8e3108 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/proxy/ProxyRelayListView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/proxy/ProxyRelayListView.kt @@ -34,6 +34,7 @@ import com.vitorpamplona.amethyst.commons.relays.ui.RelayCountResult import com.vitorpamplona.amethyst.commons.relays.ui.RelayDragState import com.vitorpamplona.amethyst.commons.relays.ui.rememberRelayDragState import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav +import com.vitorpamplona.amethyst.commons.ui.navigation.navs.rememberExtendedNav import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.BasicRelaySetupInfo import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.RelayUrlEditField import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.relaySetupInfoBuilder @@ -42,7 +43,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.HorzHalfVertPadding import com.vitorpamplona.amethyst.commons.ui.theme.HorzPadding import com.vitorpamplona.amethyst.commons.ui.theme.StdVertSpacer import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel -import com.vitorpamplona.amethyst.ui.navigation.navs.rememberExtendedNav import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfoDialog import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/search/SearchRelayListView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/search/SearchRelayListView.kt index 0b7cdd27a1..9e4355857b 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/search/SearchRelayListView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/search/SearchRelayListView.kt @@ -34,6 +34,7 @@ import com.vitorpamplona.amethyst.commons.relays.ui.RelayCountResult import com.vitorpamplona.amethyst.commons.relays.ui.RelayDragState import com.vitorpamplona.amethyst.commons.relays.ui.rememberRelayDragState import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav +import com.vitorpamplona.amethyst.commons.ui.navigation.navs.rememberExtendedNav import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.BasicRelaySetupInfo import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.RelayUrlEditField import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.relaySetupInfoBuilder @@ -42,7 +43,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.HorzHalfVertPadding import com.vitorpamplona.amethyst.commons.ui.theme.HorzPadding import com.vitorpamplona.amethyst.commons.ui.theme.StdVertSpacer import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel -import com.vitorpamplona.amethyst.ui.navigation.navs.rememberExtendedNav import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfoDialog import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/trusted/TrustedRelayListView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/trusted/TrustedRelayListView.kt index c96d2c8707..4f3a197c56 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/trusted/TrustedRelayListView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/trusted/TrustedRelayListView.kt @@ -33,6 +33,7 @@ import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.relays.ui.RelayDragState import com.vitorpamplona.amethyst.commons.relays.ui.rememberRelayDragState import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav +import com.vitorpamplona.amethyst.commons.ui.navigation.navs.rememberExtendedNav import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.BasicRelaySetupInfo import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.RelayUrlEditField import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.relaySetupInfoBuilder @@ -41,7 +42,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.HorzHalfVertPadding import com.vitorpamplona.amethyst.commons.ui.theme.HorzPadding import com.vitorpamplona.amethyst.commons.ui.theme.StdVertSpacer import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel -import com.vitorpamplona.amethyst.ui.navigation.navs.rememberExtendedNav import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfoDialog @Composable diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/url/UrlPostScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/url/UrlPostScreen.kt index 1d10a9eab3..03bd66c9cc 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/url/UrlPostScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/url/UrlPostScreen.kt @@ -28,8 +28,8 @@ import androidx.core.net.toUri import androidx.lifecycle.viewmodel.compose.viewModel import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.service.uploads.SelectedMedia +import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel -import com.vitorpamplona.amethyst.ui.navigation.navs.Nav import com.vitorpamplona.amethyst.ui.note.nip22Comments.CommentPostViewModel import com.vitorpamplona.amethyst.ui.note.nip22Comments.GenericCommentPostScreen import com.vitorpamplona.quartz.nip01Core.core.HexKey @@ -47,7 +47,7 @@ fun UrlPostScreen( quoteId: HexKey? = null, draftId: HexKey? = null, accountViewModel: AccountViewModel, - nav: Nav, + nav: INav, ) { val postViewModel: CommentPostViewModel = viewModel() postViewModel.init(accountViewModel) diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/service/AppServices.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/service/AppServices.kt index 37ca48c1a4..d13b4f0e0e 100644 --- a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/service/AppServices.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/service/AppServices.kt @@ -53,4 +53,7 @@ interface AppServices { /** Resolves `.bit` names and `d/`/`id/` identifiers over the configured ElectrumX servers. */ val namecoinResolver: NamecoinNameResolver + + /** Finds the server behind a `blossom:` URI (BUD-10). */ + val blossomServerFinder: BlossomServerFinder get() = BlossomServerFinder.None } diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/service/BlossomServerFinder.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/service/BlossomServerFinder.kt new file mode 100644 index 0000000000..8e74f719a9 --- /dev/null +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/service/BlossomServerFinder.kt @@ -0,0 +1,40 @@ +/* + * 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 + +/** + * Resolves a BUD-10 `blossom:` URI to the URL of a server that holds the blob, probing the + * author's (and the viewer's) Blossom servers. + */ +interface BlossomServerFinder { + /** The server URL already resolved for [blossomUri], without probing. */ + fun cachedServerUrl(blossomUri: String): String? + + /** Probes until a server answers for [blossomUri]; null when none does. */ + suspend fun findServerUrl(blossomUri: String): String? + + /** Resolves nothing: front ends without a Blossom client yet. */ + object None : BlossomServerFinder { + override fun cachedServerUrl(blossomUri: String): String? = null + + override suspend fun findServerUrl(blossomUri: String): String? = null + } +} diff --git a/commonsUI/src/androidMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/FileSharer.android.kt b/commonsUI/src/androidMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/FileSharer.android.kt new file mode 100644 index 0000000000..6066b0f73c --- /dev/null +++ b/commonsUI/src/androidMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/FileSharer.android.kt @@ -0,0 +1,61 @@ +/* + * 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.ui.components + +import android.content.Intent +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.platform.LocalContext +import androidx.core.content.FileProvider +import java.io.File +import java.io.FileOutputStream +import java.util.zip.ZipEntry +import java.util.zip.ZipOutputStream + +@Composable +actual fun rememberFileSharer(): FileSharer { + val context = LocalContext.current + return remember(context) { + FileSharer { zipName, files, title -> + val zipFile = File(context.cacheDir, zipName) + ZipOutputStream(FileOutputStream(zipFile)).use { zip -> + files.forEach { (name, content) -> + zip.putNextEntry(ZipEntry(name)) + zip.write(content.toByteArray()) + zip.closeEntry() + } + } + + val uri = FileProvider.getUriForFile(context, "${context.packageName}.provider", zipFile) + + val sendIntent = + Intent().apply { + action = Intent.ACTION_SEND + type = "application/zip" + putExtra(Intent.EXTRA_STREAM, uri) + putExtra(Intent.EXTRA_TITLE, title) + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + } + + context.startActivity(Intent.createChooser(sendIntent, title)) + } + } +} diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/nwc/NWCFinderFilterAssemblerSubscription.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/reqCommand/nwc/NWCFinderFilterAssemblerSubscription.kt similarity index 97% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/nwc/NWCFinderFilterAssemblerSubscription.kt rename to commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/reqCommand/nwc/NWCFinderFilterAssemblerSubscription.kt index e0b580ef74..ea54209973 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/nwc/NWCFinderFilterAssemblerSubscription.kt +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/reqCommand/nwc/NWCFinderFilterAssemblerSubscription.kt @@ -18,7 +18,7 @@ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.vitorpamplona.amethyst.service.relayClient.reqCommand.nwc +package com.vitorpamplona.amethyst.commons.relayClient.reqCommand.nwc import androidx.compose.runtime.Composable import androidx.compose.runtime.remember diff --git a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/FileSharer.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/FileSharer.kt new file mode 100644 index 0000000000..544103e9cc --- /dev/null +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/FileSharer.kt @@ -0,0 +1,43 @@ +/* + * 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.ui.components + +import androidx.compose.runtime.Composable + +/** Hands generated files to the platform's share sheet. */ +fun interface FileSharer { + /** + * Packs [files] (name to text content) into a zip named [zipName] and shares it, behind a + * chooser titled [title] where the platform has one. + */ + fun shareTextFilesAsZip( + zipName: String, + files: List>, + title: String, + ) +} + +/** + * The platform's file sharing: Android writes the zip to its cache and shares it through the + * app's `FileProvider`. Desktop and iOS have no share sheet wired yet and do nothing. + */ +@Composable +expect fun rememberFileSharer(): FileSharer diff --git a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/navigation/navs/RememberExtendedNav.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/navigation/navs/RememberExtendedNav.kt new file mode 100644 index 0000000000..9fafd83a95 --- /dev/null +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/navigation/navs/RememberExtendedNav.kt @@ -0,0 +1,37 @@ +/* + * 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.ui.navigation.navs + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope + +/** [nav], calling [onBeforeNavigate] first on every navigation (to close a sheet or dialog). */ +@Composable +fun rememberExtendedNav( + nav: INav, + onBeforeNavigate: () -> Unit, +): INav { + val scope = rememberCoroutineScope() + return remember(nav, scope) { + ObservableNav(nav, scope, onBeforeNavigate) + } +} diff --git a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/note/ZapAmountText.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/note/ZapAmountText.kt new file mode 100644 index 0000000000..4c5e02f31e --- /dev/null +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/note/ZapAmountText.kt @@ -0,0 +1,146 @@ +/* + * 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.ui.note + +import androidx.compose.foundation.layout.Box +import androidx.compose.material3.LinearProgressIndicator +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.produceState +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment.Companion.Center +import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.unit.dp +import com.vitorpamplona.amethyst.commons.model.Note +import com.vitorpamplona.amethyst.commons.model.ZapraiserStatus +import com.vitorpamplona.amethyst.commons.relayClient.event.observeNoteZaps +import com.vitorpamplona.amethyst.commons.relayClient.reqCommand.nwc.NWCFinderFilterAssemblerSubscription +import com.vitorpamplona.amethyst.commons.resources.Res +import com.vitorpamplona.amethyst.commons.resources.sats_to_complete +import com.vitorpamplona.amethyst.commons.ui.stringRes +import com.vitorpamplona.amethyst.commons.ui.theme.Font14SP +import com.vitorpamplona.amethyst.commons.ui.theme.Height24dpFilledModifier +import com.vitorpamplona.amethyst.commons.ui.theme.Height4dpFilledModifier +import com.vitorpamplona.amethyst.commons.ui.theme.NoSoTinyBorders +import com.vitorpamplona.amethyst.commons.ui.theme.TinyBorders +import com.vitorpamplona.amethyst.commons.ui.theme.fundraiserProgressColor +import com.vitorpamplona.amethyst.commons.util.showAmount +import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel +import com.vitorpamplona.quartz.nip57Zaps.zapraiser.zapraiserAmount +import com.vitorpamplona.quartz.utils.BigDecimal +import com.vitorpamplona.quartz.utils.plus +import kotlin.math.roundToInt + +@Composable +fun ObserveZapAmountText( + baseNote: Note, + accountViewModel: AccountViewModel, + inner: @Composable (String) -> Unit, +) { + val zapsState by observeNoteZaps(baseNote, accountViewModel) + + if (zapsState?.note?.zapPayments?.isNotEmpty() == true) { + zapsState?.note?.zapPayments?.forEach { + if (it.value == null) { + NWCFinderFilterAssemblerSubscription(it.key, accountViewModel) + } + } + + @Suppress("ProduceStateDoesNotAssignValue") + val zapAmountTxt by + produceState(initialValue = showAmount(baseNote.zapsAmount), key1 = zapsState) { + zapsState?.note?.let { + val newZapAmount = accountViewModel.calculateZapAmount(it) + if (value != newZapAmount) { + value = newZapAmount + } + } + } + + inner(zapAmountTxt) + } else { + // Include the signed-in user's own pending onchain zaps so + // the counter reflects the optimistic value the gallery shows. + val ownPubKey = accountViewModel.account.userProfile().pubkeyHex + val note = zapsState?.note + val total = + (note?.zapsAmount ?: BigDecimal(0)) + + BigDecimal(note?.extraOwnPendingOnchainSats(ownPubKey) ?: 0L) + inner(showAmount(total)) + } +} + +@Composable +fun RenderZapRaiser( + baseNote: Note, + zapraiserAmount: Long, + details: Boolean, + accountViewModel: AccountViewModel, +) { + val zapsState by observeNoteZaps(baseNote, accountViewModel) + + var zapraiserStatus by remember { mutableStateOf(ZapraiserStatus(0F, "$zapraiserAmount")) } + + LaunchedEffect(key1 = zapsState) { + zapsState?.note?.let { + val newStatus = accountViewModel.calculateZapraiser(baseNote) + if (zapraiserStatus != newStatus) { + zapraiserStatus = newStatus + } + } + } + + LinearProgressIndicator( + modifier = if (details) Height24dpFilledModifier else Height4dpFilledModifier, + color = MaterialTheme.colorScheme.fundraiserProgressColor, + progress = { zapraiserStatus.progress }, + gapSize = 0.dp, + strokeCap = StrokeCap.Square, + drawStopIndicator = {}, + ) + + if (details) { + Box( + contentAlignment = Center, + modifier = TinyBorders, + ) { + val totalPercentage by + remember(zapraiserStatus) { + derivedStateOf { "${(zapraiserStatus.progress * 100).roundToInt()}%" } + } + + Text( + text = + stringRes(id = Res.string.sats_to_complete, totalPercentage, zapraiserStatus.left), + modifier = NoSoTinyBorders, + // color = MaterialTheme.colorScheme.placeholderText, + fontSize = Font14SP, + maxLines = 1, + ) + } + } +} diff --git a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/note/platform/NotePlatform.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/note/platform/NotePlatform.kt index 70db109d20..a6a0f8cb73 100644 --- a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/note/platform/NotePlatform.kt +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/note/platform/NotePlatform.kt @@ -426,6 +426,19 @@ interface NotePlatform { callbacks: ZapButtonCallbacks, ) {} + /** + * The user's zap presets as rail-aware amount chips that zap [note] straight away (the chat + * long-press sheet). [onOnchainRequest] hands an on-chain amount to the caller's dialog. + */ + @Composable + fun QuickZapAmountRow( + note: Note, + onDismiss: () -> Unit, + onOnchainRequest: (Long?) -> Unit, + accountViewModel: AccountViewModel, + nav: INav, + ) {} + /** The editor for a new version of [edit], shown while [versionLookingAt] is on screen. */ @Composable fun EditPostView( diff --git a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/note/platform/NotePlatformCalls.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/note/platform/NotePlatformCalls.kt index a900b9e992..32c38eb268 100644 --- a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/note/platform/NotePlatformCalls.kt +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/note/platform/NotePlatformCalls.kt @@ -559,6 +559,15 @@ fun ReusableZapButton( callbacks: ZapButtonCallbacks = ZapButtonCallbacks(), ) = LocalNotePlatform.current.ReusableZapButton(baseNote, accountViewModel, nav, config, callbacks) +@Composable +fun QuickZapAmountRow( + note: Note, + onDismiss: () -> Unit, + onOnchainRequest: (Long?) -> Unit, + accountViewModel: AccountViewModel, + nav: INav, +) = LocalNotePlatform.current.QuickZapAmountRow(note, onDismiss, onOnchainRequest, accountViewModel, nav) + @Composable fun EditPostView( onClose: () -> Unit, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/RelayExporter.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/screen/loggedIn/relays/common/RelayExporter.kt similarity index 76% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/RelayExporter.kt rename to commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/screen/loggedIn/relays/common/RelayExporter.kt index 0b05718e60..de849afe4d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/RelayExporter.kt +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/screen/loggedIn/relays/common/RelayExporter.kt @@ -18,35 +18,20 @@ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common +package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common -import android.content.Context -import android.content.Intent import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.export_relay_settings import com.vitorpamplona.amethyst.commons.resources.relay_settings +import com.vitorpamplona.amethyst.commons.ui.components.TextSharer import com.vitorpamplona.amethyst.commons.ui.loadStringRes class RelayExporter( - val context: Context, + val sharer: TextSharer, ) { suspend fun export(collection: RelayListCollection) { - val text = buildExportText(collection) - - val sendIntent = - Intent().apply { - action = Intent.ACTION_SEND - type = "text/plain" - putExtra(Intent.EXTRA_TEXT, text) - putExtra(Intent.EXTRA_TITLE, loadStringRes(Res.string.export_relay_settings)) - } - - val shareIntent = - Intent.createChooser( - sendIntent, - loadStringRes(Res.string.export_relay_settings), - ) - context.startActivity(shareIntent) + val title = loadStringRes(Res.string.export_relay_settings) + sharer.share(buildExportText(collection), title, title) } suspend fun buildExportText(collection: RelayListCollection): String { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/RelayListCollection.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/screen/loggedIn/relays/common/RelayListCollection.kt similarity index 97% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/RelayListCollection.kt rename to commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/screen/loggedIn/relays/common/RelayListCollection.kt index e8e986423e..1e525bbae6 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/RelayListCollection.kt +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/screen/loggedIn/relays/common/RelayListCollection.kt @@ -18,7 +18,7 @@ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common +package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.blocked_section @@ -45,7 +45,6 @@ import com.vitorpamplona.amethyst.commons.resources.search_section import com.vitorpamplona.amethyst.commons.resources.search_section_explainer import com.vitorpamplona.amethyst.commons.resources.trusted_section import com.vitorpamplona.amethyst.commons.resources.trusted_section_explainer -import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.BasicRelaySetupInfo import org.jetbrains.compose.resources.StringResource data class RelayListCollection( diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/RelayZipExporter.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/screen/loggedIn/relays/common/RelayZipExporter.kt similarity index 50% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/RelayZipExporter.kt rename to commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/screen/loggedIn/relays/common/RelayZipExporter.kt index 5bb043eb06..b98af43c39 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/RelayZipExporter.kt +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/screen/loggedIn/relays/common/RelayZipExporter.kt @@ -18,67 +18,30 @@ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common +package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common -import android.content.Context -import android.content.Intent -import androidx.core.content.FileProvider import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.export_relay_settings +import com.vitorpamplona.amethyst.commons.ui.components.FileSharer import com.vitorpamplona.amethyst.commons.ui.loadStringRes -import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.relays.common.BasicRelaySetupInfo -import java.io.File -import java.io.FileOutputStream -import java.util.zip.ZipEntry -import java.util.zip.ZipOutputStream +/** Exports each non-empty relay section as `
.json` (an array of URLs) in one zip. */ class RelayZipExporter( - val context: Context, + val sharer: FileSharer, ) { suspend fun export(collection: RelayListCollection) { - val zipFile = buildZipFile(collection) - - val uri = - FileProvider.getUriForFile( - context, - "${context.packageName}.provider", - zipFile, - ) - - val sendIntent = - Intent().apply { - action = Intent.ACTION_SEND - type = "application/zip" - putExtra(Intent.EXTRA_STREAM, uri) - putExtra(Intent.EXTRA_TITLE, loadStringRes(Res.string.export_relay_settings)) - addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - } - - val shareIntent = - Intent.createChooser( - sendIntent, - loadStringRes(Res.string.export_relay_settings), - ) - context.startActivity(shareIntent) + sharer.shareTextFilesAsZip( + zipName = "relay_settings.zip", + files = buildFiles(collection), + title = loadStringRes(Res.string.export_relay_settings), + ) } - fun buildZipFile(collection: RelayListCollection): File { - val zipFile = File(context.cacheDir, "relay_settings.zip") - - ZipOutputStream(FileOutputStream(zipFile)).use { zip -> - collection.sections().forEach { section -> - if (section.relays.isNotEmpty()) { - val json = buildJsonArray(section.relays) - zip.putNextEntry(ZipEntry("${section.fileName}.json")) - zip.write(json.toByteArray()) - zip.closeEntry() - } - } + fun buildFiles(collection: RelayListCollection): List> = + collection.sections().mapNotNull { section -> + if (section.relays.isEmpty()) null else "${section.fileName}.json" to buildJsonArray(section.relays) } - return zipFile - } - private fun buildJsonArray(relays: List): String { val builder = StringBuilder() builder.appendLine("[") diff --git a/commonsUI/src/iosMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/FileSharer.ios.kt b/commonsUI/src/iosMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/FileSharer.ios.kt new file mode 100644 index 0000000000..34b5b9ff77 --- /dev/null +++ b/commonsUI/src/iosMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/FileSharer.ios.kt @@ -0,0 +1,29 @@ +/* + * 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.ui.components + +import androidx.compose.runtime.Composable + +private val NoFileSharer = FileSharer { _, _, _ -> } + +/** No share sheet wired yet. */ +@Composable +actual fun rememberFileSharer(): FileSharer = NoFileSharer diff --git a/commonsUI/src/jvmMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/FileSharer.jvm.kt b/commonsUI/src/jvmMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/FileSharer.jvm.kt new file mode 100644 index 0000000000..34b5b9ff77 --- /dev/null +++ b/commonsUI/src/jvmMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/FileSharer.jvm.kt @@ -0,0 +1,29 @@ +/* + * 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.ui.components + +import androidx.compose.runtime.Composable + +private val NoFileSharer = FileSharer { _, _, _ -> } + +/** No share sheet wired yet. */ +@Composable +actual fun rememberFileSharer(): FileSharer = NoFileSharer