Merge remote-tracking branch 'origin/main' into claude/happy-mccarthy-kdhg2s

# Conflicts:
#	amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedOff/login/LoginScreen.kt
#	amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedOff/signup/SignUpScreen.kt
#	amethyst/src/main/java/com/vitorpamplona/amethyst/ui/theme/Theme.kt
This commit is contained in:
Claude
2026-09-24 15:20:51 +00:00
310 changed files with 1534 additions and 1233 deletions
+2
View File
@@ -28,6 +28,8 @@ TASKS=(
:quartz:jvmTest
:commons:jvmTest
:commonsUI:jvmTest
# Android-only commonsUI code (e.g. SafeImeInsets) is tested here, not in jvmTest.
:commonsUI:testAndroidHostTest
:nestsClient:jvmTest
:quic:jvmTest
:marmotQuic:jvmTest
+1
View File
@@ -388,6 +388,7 @@ jobs:
:quartz:jvmTest \
:commons:jvmTest \
:commonsUI:jvmTest \
:commonsUI:testAndroidHostTest \
:nestsClient:jvmTest \
:amethyst:testFdroidDebugUnitTest \
:amethyst:testPlayDebugUnitTest \
@@ -61,11 +61,11 @@ import org.junit.Test
* [aCancelledImeAnimationMustNotWedgeTheAnimatedInset] FAILS on every version from 1.4.0 on, so it
* is [Ignore]d to keep CI green. It is not a test of Amethyst code — it is the upstream repro we
* attached to the bug. **Re-run it by hand after every Compose upgrade**: when it passes, the
* upstream fix has landed and [com.vitorpamplona.amethyst.ui.insets.SafeImeInsets] can be retired.
* upstream fix has landed and [com.vitorpamplona.amethyst.commons.ui.insets.SafeImeInsets] can be retired.
*
* [theAnimationTargetSurvivesTheWedge] documents the asymmetry that makes a workaround possible
* and is expected to PASS — `updateImeAnimationTarget` is called outside the guard. It stays
* enabled, because it guards the premise [com.vitorpamplona.amethyst.ui.insets.SafeImeInsets]
* enabled, because it guards the premise [com.vitorpamplona.amethyst.commons.ui.insets.SafeImeInsets]
* depends on: if a future Compose release stopped keeping `imeAnimationTarget` current, our
* fallback would silently start reading a dead value too.
*/
@@ -45,6 +45,7 @@ import androidx.media3.common.Player
import androidx.media3.common.util.UnstableApi
import androidx.media3.ui.compose.ContentFrame
import androidx.media3.ui.compose.SURFACE_TYPE_TEXTURE_VIEW
import com.vitorpamplona.amethyst.commons.ui.components.getDialogWindow
import com.vitorpamplona.amethyst.service.playback.composable.controls.BottomGradientOverlay
import com.vitorpamplona.amethyst.service.playback.composable.controls.FullscreenSwipeControlsState
import com.vitorpamplona.amethyst.service.playback.composable.controls.FullscreenSwipeLevelIndicator
@@ -62,7 +63,6 @@ import com.vitorpamplona.amethyst.service.playback.composable.mediaitem.LoadedMe
import com.vitorpamplona.amethyst.service.playback.composable.mediaitem.isHlsMedia
import com.vitorpamplona.amethyst.service.playback.composable.wavefront.AudioPlayingAnimation
import com.vitorpamplona.amethyst.service.playback.composable.wavefront.rememberIsAudioTrack
import com.vitorpamplona.amethyst.ui.components.getDialogWindow
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
internal const val SKIP_SECONDS = 10
@@ -30,6 +30,7 @@ import com.vitorpamplona.amethyst.Amethyst
import com.vitorpamplona.amethyst.commons.actions.ConcordActions
import com.vitorpamplona.amethyst.commons.model.cache.LocalCache
import com.vitorpamplona.amethyst.commons.model.navigation.Route
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
import com.vitorpamplona.amethyst.commons.richtext.RichTextParser
import com.vitorpamplona.amethyst.debugState
import com.vitorpamplona.amethyst.model.Account
@@ -78,6 +78,7 @@ import com.vitorpamplona.amethyst.commons.resources.lightning_create_and_add_inv
import com.vitorpamplona.amethyst.commons.resources.lightning_invoice
import com.vitorpamplona.amethyst.commons.resources.what_s_on_your_mind
import com.vitorpamplona.amethyst.commons.richtext.RichTextParser
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.invoice.AddLnInvoiceButton
@@ -98,7 +99,6 @@ import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromGallery
import com.vitorpamplona.amethyst.ui.components.BechLink
import com.vitorpamplona.amethyst.ui.components.LoadUrlPreview
import com.vitorpamplona.amethyst.ui.components.OutlinedThinPaddingTextField
import com.vitorpamplona.amethyst.ui.insets.imePaddingSafe
import com.vitorpamplona.amethyst.ui.note.NoteCompose
import com.vitorpamplona.amethyst.ui.note.creators.invoice.InvoiceRequest
import com.vitorpamplona.amethyst.ui.note.creators.uploads.ImageVideoDescription
@@ -45,12 +45,12 @@ import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
import com.vitorpamplona.amethyst.commons.resources.Res
import com.vitorpamplona.amethyst.commons.resources.copy_stack_to_clipboard
import com.vitorpamplona.amethyst.commons.resources.error_dialog_button_ok
import com.vitorpamplona.amethyst.commons.ui.components.util.setText
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.ui.components.toasts.ThrowableToastMsg
import com.vitorpamplona.amethyst.ui.components.toasts.ThrowableToastMsg2
import com.vitorpamplona.amethyst.ui.components.util.setText
import kotlinx.coroutines.launch
import java.io.PrintWriter
import java.io.StringWriter
@@ -73,6 +73,7 @@ import com.vitorpamplona.amethyst.commons.resources.video_codec_h265_description
import com.vitorpamplona.amethyst.commons.resources.video_codec_h265_label
import com.vitorpamplona.amethyst.commons.ui.components.TextSpinner
import com.vitorpamplona.amethyst.commons.ui.components.TitleExplainer
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.SettingSwitchItem
@@ -82,7 +83,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia
import com.vitorpamplona.amethyst.ui.actions.uploads.ShowImageUploadGallery
import com.vitorpamplona.amethyst.ui.components.SetDialogToEdgeToEdge
import com.vitorpamplona.amethyst.ui.insets.imePaddingSafe
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.SettingsRow
import kotlinx.collections.immutable.ImmutableList
@@ -75,12 +75,12 @@ import com.vitorpamplona.amethyst.commons.resources.twitter
import com.vitorpamplona.amethyst.commons.resources.twitter_proof_url_template
import com.vitorpamplona.amethyst.commons.resources.username
import com.vitorpamplona.amethyst.commons.resources.website_url
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.stringRes
import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectSingleFromGallery
import com.vitorpamplona.amethyst.ui.insets.imePaddingSafe
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@OptIn(ExperimentalMaterial3Api::class)
@@ -63,6 +63,7 @@ import com.vitorpamplona.amethyst.commons.resources.bolt12_offers_section_explai
import com.vitorpamplona.amethyst.commons.resources.delete_bolt12_offer
import com.vitorpamplona.amethyst.commons.resources.invalid_bolt12_offer
import com.vitorpamplona.amethyst.commons.resources.no_bolt12_offers_message
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.stringRes
@@ -73,7 +74,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.Size10dp
import com.vitorpamplona.amethyst.commons.ui.theme.StdVertSpacer
import com.vitorpamplona.amethyst.commons.ui.theme.grayText
import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText
import com.vitorpamplona.amethyst.ui.insets.imePaddingSafe
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.abbreviateBolt12Offer
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.SettingsCategory
@@ -57,12 +57,12 @@ import com.vitorpamplona.amethyst.commons.resources.local_blossom_cache_profile_
import com.vitorpamplona.amethyst.commons.resources.media_servers
import com.vitorpamplona.amethyst.commons.resources.use_local_blossom_cache
import com.vitorpamplona.amethyst.commons.resources.use_local_blossom_cache_caption
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.TopBarWithBackButton
import com.vitorpamplona.amethyst.commons.ui.stringRes
import com.vitorpamplona.amethyst.commons.ui.theme.allGoodColor
import com.vitorpamplona.amethyst.commons.ui.theme.grayText
import com.vitorpamplona.amethyst.ui.insets.imePaddingSafe
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@Composable
@@ -128,6 +128,7 @@ import com.vitorpamplona.amethyst.commons.resources.manage_stored_files_empty
import com.vitorpamplona.amethyst.commons.resources.my_blossom_data
import com.vitorpamplona.amethyst.commons.resources.quick_action_share
import com.vitorpamplona.amethyst.commons.resources.retry
import com.vitorpamplona.amethyst.commons.ui.components.util.setText
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.TopBarExtensibleWithBackButton
import com.vitorpamplona.amethyst.commons.ui.pluralStringRes
@@ -135,7 +136,6 @@ import com.vitorpamplona.amethyst.commons.ui.stringRes
import com.vitorpamplona.amethyst.commons.ui.theme.allGoodColor
import com.vitorpamplona.amethyst.commons.ui.theme.grayText
import com.vitorpamplona.amethyst.service.playback.composable.VideoViewInner
import com.vitorpamplona.amethyst.ui.components.util.setText
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip56Reports.ReportType
@@ -69,6 +69,7 @@ import com.vitorpamplona.amethyst.commons.resources.nests_servers_relay_label
import com.vitorpamplona.amethyst.commons.resources.nests_servers_remove
import com.vitorpamplona.amethyst.commons.resources.nests_servers_title
import com.vitorpamplona.amethyst.commons.resources.nests_servers_use_defaults
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.stringRes
@@ -82,7 +83,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.StdVertSpacer
import com.vitorpamplona.amethyst.commons.ui.theme.grayText
import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText
import com.vitorpamplona.amethyst.ui.actions.mediaServers.AllMediaServersScreen
import com.vitorpamplona.amethyst.ui.insets.imePaddingSafe
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.SettingsCategory
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.SettingsCategoryWithButton
@@ -61,6 +61,7 @@ import com.vitorpamplona.amethyst.commons.resources.payment_target_type
import com.vitorpamplona.amethyst.commons.resources.payment_targets
import com.vitorpamplona.amethyst.commons.resources.payment_targets_explainer
import com.vitorpamplona.amethyst.commons.resources.payment_targets_section_explainer
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.stringRes
@@ -71,7 +72,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.Size10dp
import com.vitorpamplona.amethyst.commons.ui.theme.StdVertSpacer
import com.vitorpamplona.amethyst.commons.ui.theme.grayText
import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText
import com.vitorpamplona.amethyst.ui.insets.imePaddingSafe
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.SettingsCategory
import com.vitorpamplona.quartz.experimental.nipA3.PaymentTarget
@@ -57,6 +57,7 @@ import com.vitorpamplona.amethyst.commons.resources.cashu_mint_label
import com.vitorpamplona.amethyst.commons.resources.cashu_no_wallet_found
import com.vitorpamplona.amethyst.commons.resources.cashu_redeem
import com.vitorpamplona.amethyst.commons.resources.sats
import com.vitorpamplona.amethyst.commons.ui.components.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.commons.ui.components.GenericLoadable
import com.vitorpamplona.amethyst.commons.ui.components.LoadingAnimation
import com.vitorpamplona.amethyst.commons.ui.note.OpenInNewIcon
@@ -68,7 +69,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.Size20Modifier
import com.vitorpamplona.amethyst.commons.ui.theme.StdHorzSpacer
import com.vitorpamplona.amethyst.commons.ui.theme.ThemeComparisonColumn
import com.vitorpamplona.amethyst.service.cashu.CachedCashuParser
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip60Cashu.token.CashuToken
import kotlinx.collections.immutable.ImmutableList
@@ -93,7 +93,7 @@ fun CashuPreview(
}
}
CrossfadeIfEnabled(targetState = cashuData, label = "CashuPreview", accountViewModel = accountViewModel) {
CrossfadeIfEnabled(targetState = cashuData, label = "CashuPreview") {
when (it) {
is GenericLoadable.Loaded<ImmutableList<CashuToken>> -> {
CashuPreview(it.loaded, accountViewModel)
@@ -30,8 +30,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalClipboard
import androidx.compose.ui.text.style.TextOverflow
import com.vitorpamplona.amethyst.commons.model.navigation.Route
import com.vitorpamplona.amethyst.commons.ui.components.util.setText
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.components.util.setText
import com.vitorpamplona.quartz.buzz.invite.BuzzInviteLink
import kotlinx.coroutines.launch
@@ -31,8 +31,8 @@ import androidx.compose.ui.platform.LocalClipboard
import androidx.compose.ui.text.style.TextOverflow
import com.vitorpamplona.amethyst.commons.actions.ConcordActions
import com.vitorpamplona.amethyst.commons.model.navigation.Route
import com.vitorpamplona.amethyst.commons.ui.components.util.setText
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.components.util.setText
import kotlinx.coroutines.launch
/**
@@ -30,8 +30,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalClipboard
import androidx.compose.ui.text.style.TextOverflow
import com.vitorpamplona.amethyst.commons.model.navigation.Route
import com.vitorpamplona.amethyst.commons.ui.components.util.setText
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.components.util.setText
import com.vitorpamplona.quartz.nip29RelayGroups.GroupInviteLink
import kotlinx.coroutines.launch
@@ -30,8 +30,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalClipboard
import androidx.compose.ui.text.style.TextOverflow
import com.vitorpamplona.amethyst.commons.model.navigation.Route
import com.vitorpamplona.amethyst.commons.ui.components.util.setText
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.components.util.setText
import kotlinx.coroutines.launch
@Composable
@@ -20,12 +20,6 @@
*/
package com.vitorpamplona.amethyst.ui.components
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.InlineTextContent
import androidx.compose.foundation.text.appendInlineContent
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@@ -34,34 +28,29 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.takeOrElse
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.text.LinkAnnotation
import androidx.compose.ui.text.Placeholder
import androidx.compose.ui.text.PlaceholderVerticalAlign
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextLinkStyles
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.text.withLink
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import coil3.compose.AsyncImage
import com.vitorpamplona.amethyst.commons.model.EmptyTagList
import com.vitorpamplona.amethyst.commons.model.ImmutableListOfLists
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.model.navigation.routeFor
import com.vitorpamplona.amethyst.commons.resources.Res
import com.vitorpamplona.amethyst.commons.resources.concord_invite_naddr_label
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.commons.ui.richtext.ClickableInLineIconRenderer
import com.vitorpamplona.amethyst.commons.ui.richtext.CreateClickableTextWithEmoji
import com.vitorpamplona.amethyst.commons.ui.richtext.CustomEmojiChecker
import com.vitorpamplona.amethyst.commons.ui.stringRes
import com.vitorpamplona.amethyst.commons.util.njumpLink
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNote
@@ -84,9 +73,6 @@ import com.vitorpamplona.quartz.nip19Bech32.entities.NSec
import com.vitorpamplona.quartz.nip19Bech32.toNIP19
import com.vitorpamplona.quartz.nip29RelayGroups.GroupNAddrInvite
import com.vitorpamplona.quartz.nip29RelayGroups.metadata.GroupMetadataEvent
import com.vitorpamplona.quartz.nip30CustomEmoji.CustomEmoji
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.ImmutableMap
@Composable
fun ClickableRoute(
@@ -406,208 +392,6 @@ fun CreateClickableText(
)
}
@Composable
fun CustomEmojiChecker(
text: String,
tags: ImmutableListOfLists<String>?,
onRegularText: @Composable (String) -> Unit,
onEmojiText: @Composable (ImmutableList<CustomEmoji.Renderable>) -> Unit,
) {
val mayContainEmoji by remember(text, tags) {
mutableStateOf(CustomEmoji.fastMightContainEmoji(text, tags?.lists))
}
if (mayContainEmoji) {
var emojiList by
remember(text, tags) {
mutableStateOf<ImmutableList<CustomEmoji.Renderable>?>(null)
}
LaunchedEffect(text, tags) {
val newEmojiList = CustomEmoji.assembleAnnotatedList(text, tags?.lists)
if (newEmojiList != null) {
emojiList = newEmojiList
}
}
emojiList?.let {
onEmojiText(it)
} ?: run {
onRegularText(text)
}
} else {
onRegularText(text)
}
}
@Composable
fun CustomEmojiChecker(
text: String,
emojis: ImmutableMap<String, String>,
onRegularText: @Composable (String) -> Unit,
onEmojiText: @Composable (ImmutableList<CustomEmoji.Renderable>) -> Unit,
) {
val mayContainEmoji by remember(text, emojis) {
mutableStateOf(CustomEmoji.fastMightContainEmoji(text, emojis))
}
if (mayContainEmoji) {
var emojiList by
remember(text, emojis) {
mutableStateOf<ImmutableList<CustomEmoji.Renderable>?>(null)
}
LaunchedEffect(text, emojis) {
val newEmojiList = CustomEmoji.assembleAnnotatedList(text, emojis)
if (newEmojiList != null) {
emojiList = newEmojiList
}
}
emojiList?.let {
onEmojiText(it)
} ?: run {
onRegularText(text)
}
} else {
onRegularText(text)
}
}
@Composable
fun CreateTextWithEmoji(
text: String,
tags: ImmutableListOfLists<String>?,
color: Color = Color.Unspecified,
textAlign: TextAlign? = null,
fontWeight: FontWeight? = null,
fontSize: TextUnit = TextUnit.Unspecified,
maxLines: Int = Int.MAX_VALUE,
overflow: TextOverflow = TextOverflow.Clip,
modifier: Modifier = Modifier,
) {
CustomEmojiChecker(
text,
tags,
onEmojiText = {
val textColor =
color.takeOrElse { LocalTextStyle.current.color.takeOrElse { LocalContentColor.current } }
val style =
LocalTextStyle.current
.merge(
TextStyle(
color = textColor,
textAlign = TextAlign.Unspecified,
fontWeight = fontWeight,
fontSize = fontSize,
),
).toSpanStyle()
InLineIconRenderer(it, style, fontSize, maxLines, overflow, modifier)
},
onRegularText = {
val textColor =
color.takeOrElse { LocalTextStyle.current.color.takeOrElse { LocalContentColor.current } }
Text(
text = it,
color = textColor,
textAlign = textAlign,
fontWeight = fontWeight,
fontSize = fontSize,
maxLines = maxLines,
overflow = overflow,
modifier = modifier,
)
},
)
}
@Composable
fun CreateTextWithEmoji(
text: String,
emojis: ImmutableMap<String, String>,
color: Color = Color.Unspecified,
textAlign: TextAlign? = null,
fontWeight: FontWeight? = null,
fontSize: TextUnit = TextUnit.Unspecified,
maxLines: Int = Int.MAX_VALUE,
overflow: TextOverflow = TextOverflow.Clip,
modifier: Modifier = Modifier,
) {
val textColor =
color.takeOrElse { LocalTextStyle.current.color.takeOrElse { LocalContentColor.current } }
CustomEmojiChecker(
text,
emojis,
onEmojiText = {
val currentStyle = LocalTextStyle.current
val style =
remember(currentStyle) {
currentStyle
.merge(
TextStyle(
color = textColor,
textAlign = TextAlign.Unspecified,
fontWeight = fontWeight,
fontSize = fontSize,
),
).toSpanStyle()
}
InLineIconRenderer(it, style, fontSize, maxLines, overflow, modifier)
},
onRegularText = {
Text(
text = it,
color = textColor,
textAlign = textAlign,
fontWeight = fontWeight,
fontSize = fontSize,
maxLines = maxLines,
overflow = overflow,
modifier = modifier,
)
},
)
}
@Composable
fun CreateClickableTextWithEmoji(
clickablePart: String,
maxLines: Int = Int.MAX_VALUE,
tags: ImmutableListOfLists<String>?,
style: TextStyle,
onClick: () -> Unit,
) {
CustomEmojiChecker(
text = clickablePart,
tags = tags,
onRegularText = {
Text(
text =
buildAnnotatedString {
withLink(
LinkAnnotation.Clickable(
"me",
TextLinkStyles(style = style.toSpanStyle()),
) {
onClick()
},
) {
append(clickablePart)
}
},
style = style,
maxLines = maxLines,
)
},
onEmojiText = {
ClickableInLineIconRenderer(it, maxLines, style.toSpanStyle(), onClick = onClick)
},
)
}
@Composable
fun CreateClickableTextWithEmoji(
clickablePart: String,
@@ -653,144 +437,3 @@ fun CreateClickableTextWithEmoji(
},
)
}
@Composable
fun ClickableInLineIconRenderer(
wordsInOrder: ImmutableList<CustomEmoji.Renderable>,
maxLines: Int = Int.MAX_VALUE,
style: SpanStyle,
suffix: String? = null,
nonClickableStype: SpanStyle? = null,
onClick: () -> Unit,
) {
val placeholderSize =
remember(style) {
if (style.fontSize == TextUnit.Unspecified) 22.sp else style.fontSize.times(1.1f)
}
val inlineContent =
wordsInOrder
.mapIndexedNotNull { idx, value ->
if (value is CustomEmoji.ImageUrlType) {
Pair(
"inlineContent$idx",
InlineTextContent(
Placeholder(
width = placeholderSize,
height = placeholderSize,
placeholderVerticalAlign = PlaceholderVerticalAlign.Center,
),
) {
AsyncImage(
model = value.url,
contentDescription = null,
modifier =
Modifier
.fillMaxSize()
.padding(1.dp),
)
},
)
} else {
null
}
}.associate { it.first to it.second }
val annotatedText =
buildAnnotatedString {
wordsInOrder.forEachIndexed { idx, value ->
withLink(
LinkAnnotation.Clickable("link", TextLinkStyles(style)) {
onClick()
},
) {
if (value is CustomEmoji.TextType) {
append(value.text)
} else if (value is CustomEmoji.ImageUrlType) {
appendInlineContent("inlineContent$idx", "[icon]")
}
}
}
if (suffix != null && nonClickableStype != null) {
withStyle(nonClickableStype) {
append(suffix)
}
}
}
Text(
text = annotatedText,
inlineContent = inlineContent,
maxLines = maxLines,
)
}
@Composable
fun InLineIconRenderer(
wordsInOrder: ImmutableList<CustomEmoji.Renderable>,
style: SpanStyle,
fontSize: TextUnit = TextUnit.Unspecified,
maxLines: Int = Int.MAX_VALUE,
overflow: TextOverflow = TextOverflow.Clip,
modifier: Modifier = Modifier,
) {
val placeholderSize =
remember(fontSize) {
if (fontSize == TextUnit.Unspecified) 22.sp else fontSize.times(1.1f)
}
val inlineContent =
wordsInOrder
.mapIndexedNotNull { idx, value ->
if (value is CustomEmoji.ImageUrlType) {
Pair(
"inlineContent$idx",
InlineTextContent(
Placeholder(
width = placeholderSize,
height = placeholderSize,
placeholderVerticalAlign = PlaceholderVerticalAlign.Center,
),
) {
AsyncImage(
model = value.url,
contentDescription = null,
modifier =
Modifier
.fillMaxSize()
.padding(horizontal = 0.dp),
)
},
)
} else {
null
}
}.associate { it.first to it.second }
val annotatedText =
remember {
buildAnnotatedString {
wordsInOrder.forEachIndexed { idx, value ->
withStyle(
style,
) {
if (value is CustomEmoji.TextType) {
append(value.text)
} else if (value is CustomEmoji.ImageUrlType) {
appendInlineContent("inlineContent$idx", "[icon]")
}
}
}
}
}
Text(
text = annotatedText,
inlineContent = inlineContent,
fontSize = fontSize,
maxLines = maxLines,
overflow = overflow,
modifier = modifier,
)
}
@@ -34,8 +34,8 @@ import com.vitorpamplona.amethyst.commons.resources.Res
import com.vitorpamplona.amethyst.commons.resources.error_dialog_pay_withdraw_error
import com.vitorpamplona.amethyst.commons.resources.no_wallet_found
import com.vitorpamplona.amethyst.commons.ui.components.ClickableTextPrimary
import com.vitorpamplona.amethyst.commons.ui.components.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.commons.ui.stringRes
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.ui.note.ErrorMessageDialog
import com.vitorpamplona.amethyst.ui.note.payViaIntent
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@@ -54,7 +54,7 @@ fun MayBeWithdrawal(
launch(Dispatchers.IO) { lnWithdrawal = LnWithdrawalUtil.findWithdrawal(lnurlWord) }
}
CrossfadeIfEnabled(targetState = lnWithdrawal, accountViewModel = accountViewModel) {
CrossfadeIfEnabled(targetState = lnWithdrawal) {
if (it != null) {
ClickableWithdrawal(withdrawalString = it)
} else {
@@ -29,11 +29,11 @@ import com.vitorpamplona.amethyst.commons.model.navigation.Route
import com.vitorpamplona.amethyst.commons.richtext.MediaUrlImage
import com.vitorpamplona.amethyst.commons.richtext.MediaUrlPdf
import com.vitorpamplona.amethyst.commons.richtext.MediaUrlVideo
import com.vitorpamplona.amethyst.commons.ui.components.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.commons.ui.components.UrlPreviewState
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.commons.ui.theme.HalfVertPadding
import com.vitorpamplona.amethyst.model.UrlCachedPreviewer
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@Composable
@@ -82,7 +82,6 @@ fun LoadUrlPreviewDirect(
CrossfadeIfEnabled(
targetState = urlPreviewState,
label = "UrlPreview",
accountViewModel = accountViewModel,
) { state ->
when (state) {
is UrlPreviewState.Loaded -> {
@@ -38,13 +38,13 @@ import coil3.compose.AsyncImagePainter
import coil3.compose.SubcomposeAsyncImage
import coil3.compose.SubcomposeAsyncImageContent
import com.vitorpamplona.amethyst.commons.richtext.isAnimatedMediaUrl
import com.vitorpamplona.amethyst.commons.ui.components.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.commons.ui.components.LoadingAnimation
import com.vitorpamplona.amethyst.commons.ui.note.DownloadForOfflineIcon
import com.vitorpamplona.amethyst.commons.ui.theme.Size40dp
import com.vitorpamplona.amethyst.commons.ui.theme.Size6dp
import com.vitorpamplona.amethyst.commons.ui.theme.Size75dp
import com.vitorpamplona.amethyst.model.MediaAspectRatioCache
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@Composable
@@ -74,7 +74,7 @@ fun MyAsyncImage(
val ratio = MediaAspectRatioCache.get(imageUrl)
val showImage = remember { mutableStateOf(accountViewModel.settings.showImages()) }
CrossfadeIfEnabled(targetState = showImage.value, contentAlignment = Alignment.Center, accountViewModel = accountViewModel) {
CrossfadeIfEnabled(targetState = showImage.value, contentAlignment = Alignment.Center) {
if (it) {
SubcomposeAsyncImage(
model = imageUrl,
@@ -50,10 +50,10 @@ import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
import com.vitorpamplona.amethyst.commons.resources.Res
import com.vitorpamplona.amethyst.commons.resources.copied_to_clipboard
import com.vitorpamplona.amethyst.commons.resources.copy_to_clipboard
import com.vitorpamplona.amethyst.commons.ui.components.util.setText
import com.vitorpamplona.amethyst.commons.ui.stringRes
import com.vitorpamplona.amethyst.commons.ui.theme.QuoteBorder
import com.vitorpamplona.amethyst.commons.ui.theme.Size18Modifier
import com.vitorpamplona.amethyst.ui.components.util.setText
import kotlinx.coroutines.launch
/**
@@ -56,6 +56,7 @@ import com.vitorpamplona.amethyst.commons.resources.login_with_a_private_key_to_
import com.vitorpamplona.amethyst.commons.resources.no_wallet_found
import com.vitorpamplona.amethyst.commons.resources.no_zap_amount_setup_long_press_to_change
import com.vitorpamplona.amethyst.commons.resources.thank_you
import com.vitorpamplona.amethyst.commons.ui.components.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.commons.ui.note.ZapIcon
import com.vitorpamplona.amethyst.commons.ui.note.ZappedIcon
@@ -66,7 +67,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.Size20Modifier
import com.vitorpamplona.amethyst.commons.ui.theme.Size35dp
import com.vitorpamplona.amethyst.service.ZapPaymentHandler
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.UserFinderFilterAssemblerSubscription
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.ui.components.toasts.multiline.UserBasedErrorMessage
import com.vitorpamplona.amethyst.ui.note.ObserveZapIcon
import com.vitorpamplona.amethyst.ui.note.ZapAmountChoicePopup
@@ -254,7 +254,6 @@ fun ReusableZapButton(
CrossfadeIfEnabled(
targetState = wasZappedByLoggedInUser.value,
label = "ZapIcon",
accountViewModel = accountViewModel,
) {
if (it) {
ZappedIcon(config.iconSizeModifier)
@@ -286,7 +285,6 @@ fun ReusableZapButton(
CrossfadeIfEnabled(
targetState = wasZappedByLoggedInUser.value,
label = "ZapIcon",
accountViewModel = accountViewModel,
) {
if (it) {
ZappedIcon(config.iconSizeModifier)
@@ -70,6 +70,7 @@ import com.vitorpamplona.amethyst.commons.model.Note
import com.vitorpamplona.amethyst.commons.model.User
import com.vitorpamplona.amethyst.commons.model.cache.LocalCache
import com.vitorpamplona.amethyst.commons.model.navigation.Route
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
import com.vitorpamplona.amethyst.commons.richtext.Base64Segment
import com.vitorpamplona.amethyst.commons.richtext.BechSegment
import com.vitorpamplona.amethyst.commons.richtext.BlossomUriSegment
@@ -102,9 +103,12 @@ import com.vitorpamplona.amethyst.commons.richtext.VideoSegment
import com.vitorpamplona.amethyst.commons.richtext.WithdrawSegment
import com.vitorpamplona.amethyst.commons.ui.components.AnimatedBorderTextCornerRadius
import com.vitorpamplona.amethyst.commons.ui.components.ClickableTextPrimary
import com.vitorpamplona.amethyst.commons.ui.components.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.commons.ui.components.NowhereLinkCard
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.EmptyNav
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.commons.ui.richtext.CreateClickableTextWithEmoji
import com.vitorpamplona.amethyst.commons.ui.richtext.CreateTextWithEmoji
import com.vitorpamplona.amethyst.commons.ui.richtext.HashtagIcon
import com.vitorpamplona.amethyst.commons.ui.richtext.checkForHashtagWithIcon
import com.vitorpamplona.amethyst.commons.ui.state.produceCachedState
@@ -116,7 +120,6 @@ import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.UserFinder
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserInfo
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserNickname
import com.vitorpamplona.amethyst.service.uploads.blossom.bud10.openBlossomUriAsIntent
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor
import com.vitorpamplona.amethyst.ui.note.creators.invoice.ClinkOfferPreview
import com.vitorpamplona.amethyst.ui.note.creators.invoice.MayBeInvoicePreview
@@ -977,7 +980,7 @@ private fun DisplayUserFromTag(
val nickname by observeUserNickname(baseUser, accountViewModel)
val petName = nickname?.petName
CrossfadeIfEnabled(targetState = meta, label = "DisplayUserFromTag", accountViewModel = accountViewModel) {
CrossfadeIfEnabled(targetState = meta, label = "DisplayUserFromTag") {
Row {
CreateClickableTextWithEmoji(
clickablePart = remember(meta, petName) { petName ?: it?.info?.bestName() ?: baseUser.pubkeyDisplayHex() },
@@ -67,12 +67,12 @@ import com.vitorpamplona.amethyst.commons.resources.content_warning_explanation
import com.vitorpamplona.amethyst.commons.resources.content_warning_with_reason
import com.vitorpamplona.amethyst.commons.resources.show_anyway
import com.vitorpamplona.amethyst.commons.richtext.MediaUrlImage
import com.vitorpamplona.amethyst.commons.ui.components.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.commons.ui.stringRes
import com.vitorpamplona.amethyst.commons.ui.theme.ButtonBorder
import com.vitorpamplona.amethyst.commons.ui.theme.ButtonPadding
import com.vitorpamplona.amethyst.commons.ui.theme.PaddingHorizontal12Modifier
import com.vitorpamplona.amethyst.commons.ui.theme.ThemeComparisonColumn
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip36SensitiveContent.ContentWarningTag
@@ -154,7 +154,7 @@ fun ContentWarningGate(
}
}
CrossfadeIfEnabled(targetState = showContentWarningNote, accountViewModel = accountViewModel) {
CrossfadeIfEnabled(targetState = showContentWarningNote) {
if (it) {
if (backdrop != null) {
Box(modifier = modifier.clipToBounds()) {
@@ -180,7 +180,7 @@ fun ObserveSensitivityWarning(
var showContentWarningNote by remember(accountState) { mutableStateOf(accountState.value != true) }
CrossfadeIfEnabled(targetState = showContentWarningNote, accountViewModel = accountViewModel) {
CrossfadeIfEnabled(targetState = showContentWarningNote) {
if (it) {
ContentWarningNote(reason) { showContentWarningNote = false }
} else {
@@ -29,6 +29,7 @@ import androidx.compose.runtime.SideEffect
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.window.DialogWindowProvider
import androidx.core.view.WindowCompat
import com.vitorpamplona.amethyst.commons.ui.components.getActivityWindow
import com.vitorpamplona.amethyst.commons.ui.theme.isLight
@Composable
@@ -51,13 +51,13 @@ import com.vitorpamplona.amethyst.commons.resources.url_preview_open_in_browser
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.util.setText
import com.vitorpamplona.amethyst.commons.ui.stringRes
import com.vitorpamplona.amethyst.commons.ui.theme.DoubleVertSpacer
import com.vitorpamplona.amethyst.commons.ui.theme.MaxWidthWithHorzPadding
import com.vitorpamplona.amethyst.commons.ui.theme.Size14Modifier
import com.vitorpamplona.amethyst.commons.ui.theme.innerPostModifier
import com.vitorpamplona.amethyst.commons.ui.theme.previewCardImageModifier
import com.vitorpamplona.amethyst.ui.components.util.setText
import kotlinx.coroutines.launch
@OptIn(ExperimentalFoundationApi::class)
@@ -20,31 +20,13 @@
*/
package com.vitorpamplona.amethyst.ui.components
import android.app.Activity
import android.content.Context
import android.content.ContextWrapper
import android.view.Window
import androidx.activity.ComponentActivity
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.window.DialogWindowProvider
import com.vitorpamplona.amethyst.Amethyst
// Window utils
@Composable
fun getDialogWindow(): Window? = (LocalView.current.parent as? DialogWindowProvider)?.window
@Composable
fun getActivityWindow(): Window? = LocalView.current.context.getActivityWindow()
private tailrec fun Context.getActivityWindow(): Window? =
when (this) {
is Activity -> window
is ContextWrapper -> baseContext.getActivityWindow()
else -> null
}
tailrec fun Context.getActivity(): ComponentActivity =
when (this) {
is ComponentActivity -> this
@@ -80,6 +80,8 @@ import com.vitorpamplona.amethyst.commons.richtext.MediaUrlContent
import com.vitorpamplona.amethyst.commons.richtext.MediaUrlImage
import com.vitorpamplona.amethyst.commons.richtext.MediaUrlPdf
import com.vitorpamplona.amethyst.commons.richtext.MediaUrlVideo
import com.vitorpamplona.amethyst.commons.ui.components.getActivityWindow
import com.vitorpamplona.amethyst.commons.ui.components.getDialogWindow
import com.vitorpamplona.amethyst.commons.ui.loadStringRes
import com.vitorpamplona.amethyst.commons.ui.theme.imageModifier
import com.vitorpamplona.amethyst.model.MediaAspectRatioCache
@@ -106,10 +106,12 @@ import com.vitorpamplona.amethyst.commons.richtext.MediaUrlVideo
import com.vitorpamplona.amethyst.commons.richtext.RichTextParser
import com.vitorpamplona.amethyst.commons.richtext.isAnimatedMediaUrl
import com.vitorpamplona.amethyst.commons.service.image.placeholderModel
import com.vitorpamplona.amethyst.commons.ui.components.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.commons.ui.components.LoadingAnimation
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.util.setText
import com.vitorpamplona.amethyst.commons.ui.loadStringRes
import com.vitorpamplona.amethyst.commons.ui.note.DownloadForOfflineIcon
import com.vitorpamplona.amethyst.commons.ui.stringRes
@@ -125,11 +127,9 @@ import com.vitorpamplona.amethyst.model.MediaAspectRatioCache
import com.vitorpamplona.amethyst.service.images.BlossomFetcher
import com.vitorpamplona.amethyst.service.playback.composable.VideoView
import com.vitorpamplona.amethyst.service.uploads.blossom.bud10.openBlossomUriAsIntent
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.ui.actions.InformationDialog
import com.vitorpamplona.amethyst.ui.components.pdf.PdfPreviewCard
import com.vitorpamplona.amethyst.ui.components.pdf.PdfViewerDialog
import com.vitorpamplona.amethyst.ui.components.util.setText
import com.vitorpamplona.amethyst.ui.note.BlankNote
import com.vitorpamplona.amethyst.ui.painterRes
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@@ -414,7 +414,7 @@ fun LocalImageView(
} else {
content.localFile
}
CrossfadeIfEnabled(targetState = showImage.value, contentAlignment = Alignment.Center, accountViewModel = accountViewModel) { imageVisible ->
CrossfadeIfEnabled(targetState = showImage.value, contentAlignment = Alignment.Center) { imageVisible ->
if (imageVisible) {
SubcomposeAsyncImage(
model = imageModel,
@@ -547,7 +547,7 @@ fun UrlImageView(
content.url
}
CrossfadeIfEnabled(targetState = showImage.value, contentAlignment = Alignment.Center, accountViewModel = accountViewModel) {
CrossfadeIfEnabled(targetState = showImage.value, contentAlignment = Alignment.Center) {
if (it) {
SubcomposeAsyncImage(
model = imageModel,
@@ -62,6 +62,7 @@ import androidx.compose.ui.window.DialogProperties
import androidx.core.graphics.createBitmap
import coil3.disk.DiskCache
import com.vitorpamplona.amethyst.commons.richtext.MediaUrlPdf
import com.vitorpamplona.amethyst.commons.ui.components.getDialogWindow
import com.vitorpamplona.amethyst.commons.ui.theme.Size10dp
import com.vitorpamplona.amethyst.commons.ui.theme.Size5dp
import com.vitorpamplona.amethyst.ui.components.ImmersiveSystemBarsEffect
@@ -69,7 +70,6 @@ import com.vitorpamplona.amethyst.ui.components.ViewerBackButton
import com.vitorpamplona.amethyst.ui.components.ViewerControlsRow
import com.vitorpamplona.amethyst.ui.components.ViewerSaveToGalleryButton
import com.vitorpamplona.amethyst.ui.components.ViewerShareButton
import com.vitorpamplona.amethyst.ui.components.getDialogWindow
import com.vitorpamplona.amethyst.ui.components.rememberViewerControlsVisibility
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.utils.Log
@@ -36,6 +36,7 @@ import androidx.compose.ui.text.style.TextOverflow
import com.vitorpamplona.amethyst.commons.resources.Res
import com.vitorpamplona.amethyst.commons.resources.copied_to_clipboard
import com.vitorpamplona.amethyst.commons.resources.copy_to_clipboard
import com.vitorpamplona.amethyst.commons.ui.components.util.setText
import com.vitorpamplona.amethyst.commons.ui.loadStringRes
import com.vitorpamplona.amethyst.commons.ui.stringRes
import kotlinx.coroutines.launch
@@ -30,6 +30,7 @@ import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.vitorpamplona.amethyst.commons.feeds.FeedContentState
import com.vitorpamplona.amethyst.commons.feeds.FeedState
import com.vitorpamplona.amethyst.commons.ui.components.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.commons.ui.feeds.FeedEmpty
import com.vitorpamplona.amethyst.commons.ui.feeds.FeedError
import com.vitorpamplona.amethyst.commons.ui.feeds.LoadingFeed
@@ -37,7 +38,6 @@ import com.vitorpamplona.amethyst.commons.ui.feeds.RefresheableBox
import com.vitorpamplona.amethyst.commons.ui.feeds.rememberForeverLazyGridState
import com.vitorpamplona.amethyst.commons.ui.feeds.rememberForeverLazyListState
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@Composable
@@ -111,7 +111,6 @@ fun RenderFeedContentState(
CrossfadeIfEnabled(
targetState = feedState,
animationSpec = tween(durationMillis = 100),
accountViewModel = accountViewModel,
) { state ->
when (state) {
is FeedState.Empty -> onEmpty()
@@ -20,57 +20,17 @@
*/
package com.vitorpamplona.amethyst.ui.layouts
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.layout.SubcomposeLayout
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.dp
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.compose.LocalLifecycleOwner
import com.vitorpamplona.amethyst.commons.ui.layouts.DisappearingBarNestedScroll
import com.vitorpamplona.amethyst.commons.ui.layouts.DisappearingBarState
import com.vitorpamplona.amethyst.commons.ui.layouts.LocalDisappearingBarState
import com.vitorpamplona.amethyst.commons.ui.layouts.LocalDisappearingScaffoldPadding
import com.vitorpamplona.amethyst.commons.ui.layouts.rememberDisappearingBarState
import com.vitorpamplona.amethyst.commons.ui.theme.DividerThickness
import com.vitorpamplona.amethyst.ui.components.getActivityWindow
import com.vitorpamplona.amethyst.ui.insets.SafeImeInsets
import com.vitorpamplona.amethyst.ui.insets.rememberSafeImeInsets
import com.vitorpamplona.amethyst.commons.ui.layouts.LocalScreenLayout
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.launch
private enum class DisappearingSlot { Top, Bottom, Content, Fab }
private val FabEdgePadding = 16.dp
import com.vitorpamplona.amethyst.commons.ui.layouts.DisappearingScaffold as SharedDisappearingScaffold
/**
* The app's [SharedDisappearingScaffold]: bars hide on scroll only when the user's immersive
* scrolling setting is on, and never on large screens (rail / permanent drawer), which pin the
* chrome.
*/
@Composable
fun DisappearingScaffold(
isInvertedLayout: Boolean,
@@ -81,293 +41,13 @@ fun DisappearingScaffold(
isActive: () -> Boolean = { true },
allowBarHide: Boolean = true,
mainContent: @Composable (padding: PaddingValues) -> Unit,
) {
val state = rememberDisappearingBarState()
// Large screens (rail / permanent drawer) pin the chrome: bars never slide away on
// scroll, and the immersive status-bar hiding stays off.
val canHideBars = allowBarHide && !LocalScreenLayout.current.isLargeScreen
// Hold the latest values in state so the NSC's captured lambda stays fresh across
// recompositions without rebuilding the NSC itself.
val latestIsActive by rememberUpdatedState(isActive)
val latestAllowBarHide by rememberUpdatedState(canHideBars)
val latestAccountViewModel by rememberUpdatedState(accountViewModel)
val connection =
remember(state, isInvertedLayout) {
DisappearingBarNestedScroll(
state = state,
canScroll = {
latestAllowBarHide &&
latestIsActive() &&
latestAccountViewModel.settings.isImmersiveScrollingActive()
},
reverseLayout = isInvertedLayout,
)
}
// Only wire the lifecycle observer + system-bar control when the scaffold actually moves its bars.
if (canHideBars) {
ResetBarsOnResume(state)
ImmersiveStatusBarEffect(state)
}
// If the bars were scrolled away when hiding got disabled (e.g. the window grew to a
// large tier mid-scroll), nothing above can bring them back — the nested-scroll
// connection and the resume reset are gone. Snap them visible here instead of
// leaving the chrome stranded off-screen.
LaunchedEffect(canHideBars, state) {
if (!canHideBars) state.resetToVisible()
}
// When bars are pinned, skip attaching the nested-scroll connection entirely.
// The outer Surface provides the Material container color + onBackground as
// LocalContentColor, matching M3 Scaffold's behaviour (without it, default text
// color falls back to Color.Black and is invisible on the dark theme).
// Hoisted above the branch: imePaddingSafe() inside both arms would put the call in two
// different composition groups, so toggling canHideBars (a window-size-class change) would
// dispose and rebuild it. Resolved once here, and handed to ScaffoldLayout below so the value
// this pads with is the same object the nav-bar subtraction reads.
val imeInsets = rememberSafeImeInsets()
val baseModifier =
if (canHideBars) {
Modifier.windowInsetsPadding(imeInsets).nestedScroll(connection)
} else {
Modifier.windowInsetsPadding(imeInsets)
}
val rootModifier =
baseModifier
// systemBars (not just statusBars) so a desktop window's caption bar is respected too.
.let { if (topBar == null) it.windowInsetsPadding(WindowInsets.systemBars.only(WindowInsetsSides.Top)) else it }
.let { if (bottomBar == null) it.navigationBarsPadding() else it }
Surface(
modifier = rootModifier,
color = MaterialTheme.colorScheme.background,
contentColor = MaterialTheme.colorScheme.onBackground,
) {
ScaffoldLayout(
state = state,
topBar = topBar,
bottomBar = bottomBar,
floatingButton = floatingButton,
imeInsets = imeInsets,
mainContent = mainContent,
)
}
}
@Composable
private fun ScaffoldLayout(
state: DisappearingBarState,
topBar: (@Composable () -> Unit)?,
bottomBar: (@Composable () -> Unit)?,
floatingButton: (@Composable () -> Unit)?,
imeInsets: SafeImeInsets,
mainContent: @Composable (padding: PaddingValues) -> Unit,
) {
val navBarInsets = WindowInsets.navigationBars
SubcomposeLayout { constraints ->
val layoutWidth = constraints.maxWidth
val layoutHeight = constraints.maxHeight
val looseConstraints = constraints.copy(minWidth = 0, minHeight = 0)
val topPlaceable =
topBar?.let { bar ->
subcompose(DisappearingSlot.Top) {
Column(
modifier =
Modifier.graphicsLayer {
translationY = state.topHeightOffset
},
) {
bar()
HorizontalDivider(thickness = DividerThickness)
}
}.firstOrNull()?.measure(looseConstraints)
}
val topHeight = topPlaceable?.height ?: 0
val bottomPlaceable =
bottomBar?.let { bar ->
subcompose(DisappearingSlot.Bottom) {
Column(
modifier =
Modifier.graphicsLayer {
translationY = -state.bottomHeightOffset
},
) {
bar()
}
}.firstOrNull()?.measure(looseConstraints)
}
// When the bar lambda is provided but its content emits nothing (e.g. AppBottomBar
// hides itself on canPop entries, or while the keyboard is up), reserve the
// system-nav-bar inset so the FAB and content stay clear of the navigation bar instead
// of sliding under it. Subtract the IME inset: the root imePadding has already lifted the
// whole scaffold above the keyboard, and the IME inset spans the nav-bar band, so
// reserving the full nav bar on top of that would double-count and strand a gap above the
// keyboard (the `bottomBar == null` branch gets this for free via navigationBarsPadding,
// which excludes the consumed IME inset). The `bottomBar == null` case is on rootModifier.
val bottomHeight =
(bottomPlaceable?.height ?: 0).let { measured ->
if (bottomBar != null && measured == 0) {
(navBarInsets.getBottom(this) - imeInsets.getBottom(this)).coerceAtLeast(0)
} else {
measured
}
}
// Publish the measured limits so the nested-scroll connection can clamp correctly.
state.topHeightLimit = topHeight.toFloat()
state.bottomHeightLimit = bottomHeight.toFloat()
val contentPadding =
PaddingValues(
top = topHeight.toDp(),
bottom = bottomHeight.toDp(),
)
val contentPlaceable =
subcompose(DisappearingSlot.Content) {
CompositionLocalProvider(
LocalDisappearingScaffoldPadding provides contentPadding,
LocalDisappearingBarState provides state,
) {
mainContent(contentPadding)
}
}.firstOrNull()?.measure(
Constraints.fixed(layoutWidth, layoutHeight),
)
val fabPlaceable =
floatingButton?.let { fab ->
subcompose(DisappearingSlot.Fab) {
FloatingButtonHolder(state = state) { fab() }
}.firstOrNull()?.measure(looseConstraints)
}
val fabEdgePx = FabEdgePadding.roundToPx()
layout(layoutWidth, layoutHeight) {
contentPlaceable?.place(0, 0)
topPlaceable?.place(0, 0)
bottomPlaceable?.place(0, layoutHeight - bottomHeight)
if (fabPlaceable != null) {
val x = layoutWidth - fabPlaceable.width - fabEdgePx
val yBase = layoutHeight - bottomHeight - fabPlaceable.height - fabEdgePx
fabPlaceable.place(x, yBase)
}
}
}
}
/**
* Holds the floating button. Scales / fades it with the bottom-bar collapse fraction and
* rides along with the bar via graphicsLayer, so the layout pass is untouched while
* the bar animates.
*/
@Composable
private fun FloatingButtonHolder(
state: DisappearingBarState,
content: @Composable () -> Unit,
) {
Box(
modifier =
Modifier.graphicsLayer {
val visible = (1f - state.bottomCollapsedFraction).coerceAtLeast(0.001f)
scaleX = visible
scaleY = visible
alpha = visible
translationY = -state.bottomHeightOffset
},
) {
content()
}
}
@Composable
private fun ResetBarsOnResume(state: DisappearingBarState) {
val lifecycleOwner = LocalLifecycleOwner.current
val scope = rememberCoroutineScope()
DisposableEffect(lifecycleOwner, state) {
val observer =
LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME &&
(state.topHeightOffset != 0f || state.bottomHeightOffset != 0f)
) {
scope.launch { state.resetToVisible() }
}
}
lifecycleOwner.lifecycle.addObserver(observer)
onDispose { lifecycleOwner.lifecycle.removeObserver(observer) }
}
}
/** Fraction at which the tracked in-app bar is considered fully settled into the hidden edge. */
private const val STATUS_BAR_HIDE_THRESHOLD = 0.999f
/**
* Fraction the chrome must fall back below before the OS status bar reappears. Lower than
* [STATUS_BAR_HIDE_THRESHOLD] on purpose: this hysteresis gap means the finger that stops a fast
* scroll — which makes a tiny reverse reveal — does not immediately pop the status bar back. The
* user has to deliberately scroll the chrome back by ~30% before the status bar returns.
*/
private const val STATUS_BAR_SHOW_THRESHOLD = 0.7f
/**
* Hides the OS status bar once the tracked in-app bar has settled into the hidden edge, and shows it
* the moment it starts coming back. The OS status bar is binary (cannot slide), so it is driven off
* the collapse fraction with a near-1 threshold, which means it toggles on settle rather than
* mid-drag.
*
* The status bar sits with the top in-app bar, so it tracks the top bar's collapse. The top and
* bottom bars can collapse at different rates (each clamps to its own height), so tracking the top
* fraction keeps the status bar in step with the chrome directly beneath it. On screens with no top
* bar (topHeightLimit == 0) it falls back to the bottom bar, so the status bar still hides in sync
* with the bottom navigation.
*
* Only the top status bar is touched; the bottom OS navigation bar is left alone.
* BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE lets the user swipe to peek the status bar while immersed.
* The status bar is always restored on dispose so leaving an immersive screen can never strand a
* hidden bar.
*/
@Composable
private fun ImmersiveStatusBarEffect(state: DisappearingBarState) {
val view = LocalView.current
if (view.isInEditMode) return
val window = getActivityWindow() ?: return
val controller = remember(window, view) { WindowInsetsControllerCompat(window, view) }
LaunchedEffect(controller, state) {
controller.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
var statusBarHidden = false
snapshotFlow {
if (state.topHeightLimit > 0f) state.topCollapsedFraction else state.bottomCollapsedFraction
}.distinctUntilChanged()
.collect { fraction ->
// Hysteresis: hide once fully settled, but only show again after the chrome has
// been pulled back past STATUS_BAR_SHOW_THRESHOLD. Between the two thresholds the
// status bar keeps its current state, so a stray reverse reveal can't flip it.
val shouldHide =
when {
fraction >= STATUS_BAR_HIDE_THRESHOLD -> true
fraction <= STATUS_BAR_SHOW_THRESHOLD -> false
else -> statusBarHidden
}
if (shouldHide != statusBarHidden) {
statusBarHidden = shouldHide
if (shouldHide) {
controller.hide(WindowInsetsCompat.Type.statusBars())
} else {
controller.show(WindowInsetsCompat.Type.statusBars())
}
}
}
}
DisposableEffect(controller) {
onDispose { controller.show(WindowInsetsCompat.Type.statusBars()) }
}
}
) = SharedDisappearingScaffold(
isInvertedLayout = isInvertedLayout,
topBar = topBar,
bottomBar = bottomBar,
floatingButton = floatingButton,
immersiveScrolling = { accountViewModel.settings.isImmersiveScrollingActive() },
isActive = isActive,
allowBarHide = allowBarHide && !LocalScreenLayout.current.isLargeScreen,
mainContent = mainContent,
)
@@ -29,52 +29,14 @@ import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSiz
import androidx.compose.material3.windowsizeclass.WindowSizeClass
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
/** How the app shell presents its top-level navigation for the current window size. */
enum class NavigationStyle {
/** Compact windows (phones): bottom navigation bar + modal drawer. */
BOTTOM_BAR,
/**
* Every non-Compact window that does not dock — portrait tablets and unfolded foldables at
* any width, plus short landscape windows: a left navigation rail replaces the bottom bar
* and the drawer stays modal behind the rail's avatar button.
*/
NAV_RAIL,
/** Wide, landscape, tall windows (landscape tablets, desktop): the drawer docks permanently on the left. */
PERMANENT_DRAWER,
}
/**
* The shell layout decisions for the current window, published once per window size change
* through [LocalScreenLayout] so every screen, bar and panel agrees on the same tier.
*/
@Immutable
data class ScreenLayoutSpec(
val navigationStyle: NavigationStyle,
val hasRoomForNotificationPanel: Boolean,
) {
/**
* True on the rail and permanent-drawer tiers. Large screens hide the bottom bar and pin
* the top/bottom chrome (no disappearing bars on scroll).
*/
val isLargeScreen: Boolean get() = navigationStyle != NavigationStyle.BOTTOM_BAR
companion object {
val Phone = ScreenLayoutSpec(NavigationStyle.BOTTOM_BAR, hasRoomForNotificationPanel = false)
}
}
val LocalScreenLayout = compositionLocalOf { ScreenLayoutSpec.Phone }
import com.vitorpamplona.amethyst.commons.ui.layouts.NavigationStyle
import com.vitorpamplona.amethyst.commons.ui.layouts.ScreenLayoutSpec
/**
* Minimum window width for the docked notification panel: a leading navigation pane, a
@@ -58,6 +58,7 @@ import com.vitorpamplona.amethyst.commons.relayClient.user.LocalUserFinderAccoun
import com.vitorpamplona.amethyst.commons.resources.Res
import com.vitorpamplona.amethyst.commons.resources.invalid_nip19_uri
import com.vitorpamplona.amethyst.commons.resources.invalid_nip19_uri_description
import com.vitorpamplona.amethyst.commons.ui.layouts.LocalScreenLayout
import com.vitorpamplona.amethyst.service.crashreports.DisplayCrashMessages
import com.vitorpamplona.amethyst.service.relayClient.authCommand.compose.RelayAuthPromptHost
import com.vitorpamplona.amethyst.service.relayClient.notifyCommand.compose.DisplayNotifyMessages
@@ -74,7 +75,6 @@ import com.vitorpamplona.amethyst.ui.broadcast.DisplayBroadcastProgress
import com.vitorpamplona.amethyst.ui.call.CallActivity
import com.vitorpamplona.amethyst.ui.components.getActivity
import com.vitorpamplona.amethyst.ui.components.toasts.DisplayErrorMessages
import com.vitorpamplona.amethyst.ui.layouts.LocalScreenLayout
import com.vitorpamplona.amethyst.ui.layouts.rememberScreenLayoutSpec
import com.vitorpamplona.amethyst.ui.navigation.bottombars.LocalTabReselectCoordinator
import com.vitorpamplona.amethyst.ui.navigation.bottombars.TabReselectCoordinator
@@ -49,6 +49,7 @@ import com.vitorpamplona.amethyst.commons.favorites.FavoriteAppIcon
import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
import com.vitorpamplona.amethyst.commons.model.navigation.BottomBarEntry
import com.vitorpamplona.amethyst.commons.model.navigation.Route
import com.vitorpamplona.amethyst.commons.ui.layouts.LocalScreenLayout
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.commons.ui.stringRes
import com.vitorpamplona.amethyst.commons.ui.theme.DividerThickness
@@ -61,7 +62,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.onSurface65
import com.vitorpamplona.amethyst.favorites.BrowserIconRegistry
import com.vitorpamplona.amethyst.favorites.FavoriteAppsRegistry
import com.vitorpamplona.amethyst.favorites.rememberNappletIconModel
import com.vitorpamplona.amethyst.ui.layouts.LocalScreenLayout
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
/** Content height of the [AppBottomBar] (the 50.dp Column inside [RenderBottomMenu]),
@@ -25,8 +25,7 @@ import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalDensity
import com.vitorpamplona.amethyst.ui.insets.SafeImeInsets
import com.vitorpamplona.amethyst.ui.insets.rememberSafeImeInsets
import com.vitorpamplona.amethyst.commons.ui.insets.rememberSafeImeInsets
enum class KeyboardState {
Opened,
@@ -36,7 +35,7 @@ enum class KeyboardState {
/**
* Whether the soft keyboard is currently on screen, derived from the IME inset.
*
* This intentionally reads the same [SafeImeInsets] that drives `Modifier.imePaddingSafe()`
* This intentionally reads the same [SafeImeInsets][com.vitorpamplona.amethyst.commons.ui.insets.SafeImeInsets] that drives `Modifier.imePaddingSafe()`
* everywhere else in the app, so the two can never disagree about whether the keyboard is up.
*
* The original implementation measured `View.getWindowVisibleDisplayFrame` from a
@@ -48,7 +47,7 @@ enum class KeyboardState {
*
* Reading `WindowInsets.ime` fixed that, but has a latch of its own: Compose stops updating the
* inset entirely once its insets listener is left mid-animation, which pins this back at [Opened]
* in exactly the same way. [SafeImeInsets] is what closes that last hole, which is why this reads
* in exactly the same way. [SafeImeInsets][com.vitorpamplona.amethyst.commons.ui.insets.SafeImeInsets] is what closes that last hole, which is why this reads
* the corrected inset rather than the raw one.
*/
@Composable
@@ -74,12 +74,12 @@ import com.vitorpamplona.amethyst.commons.scheduledposts.ScheduledPostStatus
import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage
import com.vitorpamplona.amethyst.commons.ui.loadPluralStringRes
import com.vitorpamplona.amethyst.commons.ui.pluralStringRes
import com.vitorpamplona.amethyst.commons.ui.richtext.CreateTextWithEmoji
import com.vitorpamplona.amethyst.commons.ui.stringRes
import com.vitorpamplona.amethyst.commons.ui.theme.AccountPictureModifier
import com.vitorpamplona.amethyst.commons.ui.theme.Size10dp
import com.vitorpamplona.amethyst.commons.ui.theme.Size55dp
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserInfo
import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji
import com.vitorpamplona.amethyst.ui.note.toShortDisplay
import com.vitorpamplona.amethyst.ui.screen.AccountSessionManager
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@@ -98,6 +98,7 @@ import com.vitorpamplona.amethyst.commons.model.User
import com.vitorpamplona.amethyst.commons.model.navigation.DrawerSectionId
import com.vitorpamplona.amethyst.commons.model.navigation.NavBarItem
import com.vitorpamplona.amethyst.commons.model.navigation.Route
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
import com.vitorpamplona.amethyst.commons.resources.Res
import com.vitorpamplona.amethyst.commons.resources.bookmarks
import com.vitorpamplona.amethyst.commons.resources.drafts
@@ -120,6 +121,7 @@ import com.vitorpamplona.amethyst.commons.scheduledposts.ScheduledPostStatus
import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.EmptyNav
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.commons.ui.richtext.CreateTextWithEmoji
import com.vitorpamplona.amethyst.commons.ui.stringRes
import com.vitorpamplona.amethyst.commons.ui.theme.DividerThickness
import com.vitorpamplona.amethyst.commons.ui.theme.DoubleHorzSpacer
@@ -147,7 +149,6 @@ import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNo
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserContactCardsFollowerCount
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserInfo
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserStatuses
import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji
import com.vitorpamplona.amethyst.ui.layouts.PermanentDrawerWidth
import com.vitorpamplona.amethyst.ui.navigation.bottombars.NavBarCatalog
import com.vitorpamplona.amethyst.ui.navigation.bottombars.NavBarItemDef
@@ -26,9 +26,8 @@ import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import com.vitorpamplona.amethyst.commons.ui.insets.rememberSafeImeInsets
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.EmptyNav
import com.vitorpamplona.amethyst.ui.insets.SafeImeInsets
import com.vitorpamplona.amethyst.ui.insets.rememberSafeImeInsets
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.withTimeoutOrNull
@@ -44,7 +43,7 @@ const val IME_SETTLE_TIMEOUT_MS = 700L
* [WindowInsetsAnimationCompat][androidx.core.view.WindowInsetsAnimationCompat] is cancelled before
* its terminal (zero) frame reaches Compose. `WindowInsets.ime` is a single app-wide holder, so it
* stays "animating" and every IME padding in the app — not just the screen being left — freezes at
* the keyboard height. [SafeImeInsets] is the backstop for when that freeze turns out to be
* the keyboard height. [SafeImeInsets][com.vitorpamplona.amethyst.commons.ui.insets.SafeImeInsets] is the backstop for when that freeze turns out to be
* permanent; this is the prevention, and it is the cheaper of the two because it keeps the padding
* from ever being wrong.
*
@@ -64,7 +63,7 @@ fun interface ImeSettler {
}
/**
* Reads the same [SafeImeInsets] that drives `Modifier.imePaddingSafe()`, so the settler and the
* Reads the same [SafeImeInsets][com.vitorpamplona.amethyst.commons.ui.insets.SafeImeInsets] that drives `Modifier.imePaddingSafe()`, so the settler and the
* padding can never disagree about whether the keyboard is gone. Reading the raw `WindowInsets.ime`
* here would make every navigation burn the full [IME_SETTLE_TIMEOUT_MS] once the inset is stranded,
* waiting on a value that is never going to move again.
@@ -24,17 +24,16 @@ import com.vitorpamplona.amethyst.commons.model.Note
import com.vitorpamplona.amethyst.commons.model.User
import com.vitorpamplona.amethyst.commons.model.cache.LocalCache
import com.vitorpamplona.amethyst.commons.model.concord.ConcordChannel
import com.vitorpamplona.amethyst.commons.model.emphChat.EphemeralChatChannel
import com.vitorpamplona.amethyst.commons.model.marmotGroups.MarmotGroupChatroom
import com.vitorpamplona.amethyst.commons.model.navigation.Route
import com.vitorpamplona.amethyst.commons.model.navigation.minichatRouteFor
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
import com.vitorpamplona.amethyst.commons.model.nip28PublicChats.PublicChatChannel
import com.vitorpamplona.amethyst.commons.model.nip29RelayGroups.RelayGroupChannel
import com.vitorpamplona.amethyst.commons.model.nip53LiveActivities.LiveActivitiesChannel
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.buzz.notifications.MemberAddedNotificationEvent
import com.vitorpamplona.quartz.experimental.ephemChat.chat.EphemeralChatEvent
import com.vitorpamplona.quartz.experimental.ephemChat.chat.RoomId
import com.vitorpamplona.quartz.experimental.nip82SoftwareApps.application.SoftwareApplicationEvent
import com.vitorpamplona.quartz.experimental.zapPolls.ZapPollEvent
import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent
@@ -79,34 +78,6 @@ import com.vitorpamplona.quartz.nipA4PublicMessages.PublicMessageEvent
import com.vitorpamplona.quartz.nipCCGeocaching.curation.GeocacheCurationListEvent
import com.vitorpamplona.quartz.nipCCGeocaching.listing.GeocacheListingEvent
/**
* A minichat reply — a kind-1111 [CommentEvent] posted into a chat message's thread — should open
* that message's minichat ([Route.ChatMinichat]), not the whole channel it lives in. Regular chat
* messages are kind 9 / 42, so a [CommentEvent] in a *chat context* is always a thread reply. We
* gate on the chat context (the reply is attached to a Concord / relay-group / public-chat gatherer,
* or its root message is) precisely so a generic NIP-22 comment on an article or note keeps its own
* thread route and isn't mistaken for a minichat. Returns null when it isn't a chat-context comment.
*/
fun minichatRouteFor(note: Note): Route? {
val comment = note.event as? CommentEvent ?: return null
val rootId = comment.rootEventIds().firstOrNull() ?: return null
// Prefer the reply's own Concord channel (the reply arrived over that plane, so its gatherer
// always carries the community/channel), else the root note's if it happens to be loaded. Passing
// these lets the minichat screen resolve the plane + relays even when the parent isn't cached.
val concord =
note.inGatherers?.firstNotNullOfOrNull { it as? ConcordChannel }
?: LocalCache.getNoteIfExists(rootId)?.inGatherers?.firstNotNullOfOrNull { it as? ConcordChannel }
if (concord != null) {
return Route.ChatMinichat(rootId, concord.channelId.communityId, concord.channelId.channelId)
}
val inChatContext = note.isInChatGatherer() || LocalCache.getNoteIfExists(rootId)?.isInChatGatherer() == true
return if (inChatContext) Route.ChatMinichat(rootId) else null
}
private fun Note.isInChatGatherer(): Boolean = inGatherers?.any { it is ConcordChannel || it is RelayGroupChannel || it is PublicChatChannel } == true
/**
* Kinds whose destination is `Route.Note(id)` — the generic thread view — no matter what the
* event body turns out to say. Mirrors the `else ->` branch of [routeForInner]: every kind here
@@ -419,26 +390,6 @@ fun routeToMessage(
accountViewModel: AccountViewModel,
): Route = routeToMessage(user.pubkeyHex, draftMessage, replyId, draftId, expiresDays, accountViewModel)
fun routeFor(note: EphemeralChatChannel): Route = Route.EphemeralChat(note.roomId.id, note.roomId.relayUrl.url)
fun routeFor(note: PublicChatChannel): Route = Route.PublicChatChannel(note.idHex)
fun routeFor(note: LiveActivitiesChannel): Route = Route.LiveActivityChannel(note.address.kind, note.address.pubKeyHex, note.address.dTag)
fun routeFor(roomId: RoomId): Route = Route.EphemeralChat(roomId.id, roomId.relayUrl.url)
fun routeFor(note: RelayGroupChannel): Route = Route.RelayGroup(note.groupId.id, note.groupId.relayUrl.url)
fun routeFor(groupId: GroupId): Route = Route.RelayGroup(groupId.id, groupId.relayUrl.url)
fun routeFor(channel: ConcordChannel): Route = Route.Concord(channel.channelId.communityId, channel.channelId.channelId)
fun routeFor(user: User): Route.Profile = Route.Profile(user.pubkeyHex)
fun routeForUser(userHex: HexKey): Route.Profile = Route.Profile(userHex)
fun authorRouteFor(note: Note): Route.Profile? = note.author?.pubkeyHex?.let { Route.Profile(it) }
fun routeReplyTo(
note: Note,
account: Account,
@@ -37,6 +37,7 @@ import com.vitorpamplona.amethyst.commons.resources.Res
import com.vitorpamplona.amethyst.commons.resources.your_profile_image
import com.vitorpamplona.amethyst.commons.search.SearchQuery
import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage
import com.vitorpamplona.amethyst.commons.ui.layouts.LocalScreenLayout
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.ShorterTopAppBar
import com.vitorpamplona.amethyst.commons.ui.note.ArrowBackIcon
@@ -46,7 +47,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.HeaderPictureModifier
import com.vitorpamplona.amethyst.commons.ui.theme.Size22Modifier
import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserPicture
import com.vitorpamplona.amethyst.ui.layouts.LocalScreenLayout
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
/**
@@ -40,6 +40,7 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
import com.vitorpamplona.amethyst.commons.resources.Res
import com.vitorpamplona.amethyst.commons.resources.new_badge_award_notif
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
@@ -36,6 +36,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import com.vitorpamplona.amethyst.commons.model.Bolt12ZapEntry
import com.vitorpamplona.amethyst.commons.model.Note
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.commons.ui.note.ZappedIcon
import com.vitorpamplona.amethyst.commons.ui.theme.Size25dp
@@ -35,9 +35,9 @@ import com.vitorpamplona.amethyst.commons.resources.copy_text_translated
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.util.setText
import com.vitorpamplona.amethyst.commons.ui.stringRes
import com.vitorpamplona.amethyst.ui.components.cachedTranslation
import com.vitorpamplona.amethyst.ui.components.util.setText
import com.vitorpamplona.amethyst.ui.note.types.displayedNoteText
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import kotlinx.coroutines.launch
@@ -24,9 +24,9 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import com.vitorpamplona.amethyst.commons.model.Note
import com.vitorpamplona.amethyst.commons.ui.components.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.commons.ui.theme.SimpleImageBorder
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNote
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.ui.note.elements.BannerImage
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@@ -45,7 +45,7 @@ fun DisplayAuthorBanner(
)
} else {
val authorState by observeNote(baseNote, accountViewModel)
CrossfadeIfEnabled(authorState.note.author, accountViewModel = accountViewModel) { author ->
CrossfadeIfEnabled(authorState.note.author) { author ->
if (author != null) {
BannerImage(
author,
@@ -36,10 +36,11 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.vitorpamplona.amethyst.commons.model.User
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
import com.vitorpamplona.amethyst.commons.model.navigation.routeForUser
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.commons.ui.theme.Size25dp
import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor
import com.vitorpamplona.amethyst.ui.navigation.routes.routeForUser
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import kotlinx.collections.immutable.ImmutableList
@@ -40,6 +40,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.commons.ui.theme.StdStartPadding
import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor
@@ -72,6 +72,8 @@ import com.vitorpamplona.amethyst.commons.model.Note
import com.vitorpamplona.amethyst.commons.model.NoteState
import com.vitorpamplona.amethyst.commons.model.User
import com.vitorpamplona.amethyst.commons.model.navigation.Route
import com.vitorpamplona.amethyst.commons.model.navigation.authorRouteFor
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
import com.vitorpamplona.amethyst.commons.resources.Res
import com.vitorpamplona.amethyst.commons.resources.nutzap
import com.vitorpamplona.amethyst.commons.resources.profile_image
@@ -84,6 +86,7 @@ import com.vitorpamplona.amethyst.commons.ui.note.LikedIcon
import com.vitorpamplona.amethyst.commons.ui.note.RepostedIcon
import com.vitorpamplona.amethyst.commons.ui.note.ScoreTagSmall
import com.vitorpamplona.amethyst.commons.ui.note.ZappedIcon
import com.vitorpamplona.amethyst.commons.ui.richtext.InLineIconRenderer
import com.vitorpamplona.amethyst.commons.ui.stringRes
import com.vitorpamplona.amethyst.commons.ui.theme.BitcoinOrange
import com.vitorpamplona.amethyst.commons.ui.theme.HalfTopPadding
@@ -106,9 +109,7 @@ import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUse
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserPicture
import com.vitorpamplona.amethyst.ui.components.CoreSecretMessage
import com.vitorpamplona.amethyst.ui.components.ExpandableRichTextViewer
import com.vitorpamplona.amethyst.ui.components.InLineIconRenderer
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
import com.vitorpamplona.amethyst.ui.navigation.routes.authorRouteFor
import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor
import com.vitorpamplona.amethyst.ui.note.elements.NoteDropDownMenu
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@@ -49,6 +49,8 @@ import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
import com.vitorpamplona.amethyst.commons.model.AddressableNote
import com.vitorpamplona.amethyst.commons.model.Note
import com.vitorpamplona.amethyst.commons.model.User
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
import com.vitorpamplona.amethyst.commons.model.navigation.routeForUser
import com.vitorpamplona.amethyst.commons.model.nip05DnsIdentifiers.Nip05State
import com.vitorpamplona.amethyst.commons.model.nip05DnsIdentifiers.Nip05VerifState
import com.vitorpamplona.amethyst.commons.resources.Res
@@ -56,7 +58,9 @@ import com.vitorpamplona.amethyst.commons.resources.nip05_checking
import com.vitorpamplona.amethyst.commons.resources.nip05_failed
import com.vitorpamplona.amethyst.commons.resources.nip05_verified
import com.vitorpamplona.amethyst.commons.ui.components.ClickableTextPrimary
import com.vitorpamplona.amethyst.commons.ui.components.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.commons.ui.richtext.CreateTextWithEmoji
import com.vitorpamplona.amethyst.commons.ui.stringRes
import com.vitorpamplona.amethyst.commons.ui.theme.Font14SP
import com.vitorpamplona.amethyst.commons.ui.theme.NIP05IconSize
@@ -69,11 +73,8 @@ import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.EventFinderFilterAssemblerSubscription
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNote
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserStatuses
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji
import com.vitorpamplona.amethyst.ui.components.LoadNote
import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor
import com.vitorpamplona.amethyst.ui.navigation.routes.routeForUser
import com.vitorpamplona.amethyst.ui.painterRes
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.LoadUser
@@ -466,7 +467,7 @@ fun RenderNIP05VerifiedSymbol(
modifier: Modifier,
accountViewModel: AccountViewModel,
) {
CrossfadeIfEnabled(targetState = state, accountViewModel = accountViewModel) {
CrossfadeIfEnabled(targetState = state) {
when (it) {
is Nip05VerifState.Verifying, is Nip05VerifState.NotStarted -> {
// Treat "not started" and "in flight" as a single loading visual so the
@@ -66,6 +66,7 @@ import com.vitorpamplona.amethyst.commons.model.cache.LocalCache
import com.vitorpamplona.amethyst.commons.model.concord.ConcordChannel
import com.vitorpamplona.amethyst.commons.model.isCommunityDefinition
import com.vitorpamplona.amethyst.commons.model.navigation.Route
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
import com.vitorpamplona.amethyst.commons.model.nip28PublicChats.PublicChatChannel
import com.vitorpamplona.amethyst.commons.model.nip29RelayGroups.RelayGroupChannel
import com.vitorpamplona.amethyst.commons.model.replyingDirectlyTo
@@ -100,6 +100,7 @@ import com.vitorpamplona.amethyst.commons.resources.quick_action_unfollow
import com.vitorpamplona.amethyst.commons.resources.quick_action_unmute_thread
import com.vitorpamplona.amethyst.commons.resources.report_dialog_block_hide_user_btn
import com.vitorpamplona.amethyst.commons.resources.report_dialog_blocking_a_user
import com.vitorpamplona.amethyst.commons.ui.components.util.setText
import com.vitorpamplona.amethyst.commons.ui.loadStringRes
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.commons.ui.stringRes
@@ -109,7 +110,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.SmallestBorder
import com.vitorpamplona.amethyst.commons.ui.theme.isLight
import com.vitorpamplona.amethyst.commons.ui.theme.secondaryButtonBackground
import com.vitorpamplona.amethyst.commons.util.njumpLink
import com.vitorpamplona.amethyst.ui.components.util.setText
import com.vitorpamplona.amethyst.ui.navigation.routes.routeEditDraftTo
import com.vitorpamplona.amethyst.ui.painterRes
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@@ -38,6 +38,7 @@ import com.vitorpamplona.amethyst.commons.hashtags.Cashu
import com.vitorpamplona.amethyst.commons.hashtags.CustomHashTagIcons
import com.vitorpamplona.amethyst.commons.model.Note
import com.vitorpamplona.amethyst.commons.model.NutzapEntry
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
import com.vitorpamplona.amethyst.commons.resources.Res
import com.vitorpamplona.amethyst.commons.resources.nutzap
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
@@ -37,6 +37,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.commons.hashtags.Cashu
import com.vitorpamplona.amethyst.commons.hashtags.CustomHashTagIcons
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
import com.vitorpamplona.amethyst.commons.resources.Res
import com.vitorpamplona.amethyst.commons.resources.nutzap
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
@@ -41,6 +41,7 @@ import com.vitorpamplona.amethyst.commons.model.Note
import com.vitorpamplona.amethyst.commons.model.OnchainZapEntry
import com.vitorpamplona.amethyst.commons.model.OnchainZapStatus
import com.vitorpamplona.amethyst.commons.model.cache.LocalCache
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.commons.ui.note.OnchainZappedIcon
import com.vitorpamplona.amethyst.commons.ui.note.PendingClockBadge
@@ -152,7 +152,9 @@ import com.vitorpamplona.amethyst.commons.resources.reload_mint_title
import com.vitorpamplona.amethyst.commons.resources.sats_to_complete
import com.vitorpamplona.amethyst.commons.ui.components.AnimatedBorderTextCornerRadius
import com.vitorpamplona.amethyst.commons.ui.components.ClickableBox
import com.vitorpamplona.amethyst.commons.ui.components.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.commons.ui.components.GenericLoadable
import com.vitorpamplona.amethyst.commons.ui.components.util.setText
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.commons.ui.note.ChangeReactionIcon
import com.vitorpamplona.amethyst.commons.ui.note.CommentIcon
@@ -167,6 +169,7 @@ import com.vitorpamplona.amethyst.commons.ui.note.ShareIcon
import com.vitorpamplona.amethyst.commons.ui.note.VoiceReplyIcon
import com.vitorpamplona.amethyst.commons.ui.note.ZapIcon
import com.vitorpamplona.amethyst.commons.ui.note.ZappedIcon
import com.vitorpamplona.amethyst.commons.ui.richtext.InLineIconRenderer
import com.vitorpamplona.amethyst.commons.ui.stringRes
import com.vitorpamplona.amethyst.commons.ui.theme.BitcoinOrange
import com.vitorpamplona.amethyst.commons.ui.theme.ButtonBorder
@@ -222,13 +225,10 @@ import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNo
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteZaps
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.nwc.NWCFinderFilterAssemblerSubscription
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserInfo
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.ui.actions.uploads.FloatingRecordingIndicator
import com.vitorpamplona.amethyst.ui.actions.uploads.MAX_VOICE_RECORD_SECONDS
import com.vitorpamplona.amethyst.ui.actions.uploads.RecordAudioBox
import com.vitorpamplona.amethyst.ui.components.InLineIconRenderer
import com.vitorpamplona.amethyst.ui.components.toasts.multiline.UserBasedErrorMessage
import com.vitorpamplona.amethyst.ui.components.util.setText
import com.vitorpamplona.amethyst.ui.navigation.routes.routeReplyTo
import com.vitorpamplona.amethyst.ui.note.elements.ShareOptionsBottomSheet
import com.vitorpamplona.amethyst.ui.note.types.EditState
@@ -603,7 +603,6 @@ private fun RenderShowIndividualReactionsButton(
CrossfadeIfEnabled(
targetState = wantsToSeeReactions.value,
label = "RenderShowIndividualReactionsButton",
accountViewModel = accountViewModel,
) {
if (it) {
ExpandLessIcon(modifier = Size22Modifier, Res.string.close_all_reactions_to_this_post)
@@ -1112,7 +1111,7 @@ fun LikeReaction(
onLongClick = { nav.nav(Route.UpdateReactionType) },
) {
ObserveLikeIcon(baseNote, accountViewModel) { reactionType ->
CrossfadeIfEnabled(targetState = reactionType, contentAlignment = Center, label = "LikeIcon", accountViewModel = accountViewModel) {
CrossfadeIfEnabled(targetState = reactionType, contentAlignment = Center, label = "LikeIcon") {
if (reactionType != null) {
RenderReactionType(reactionType, heartSizeModifier, iconFontSize)
} else {
@@ -1447,7 +1446,7 @@ fun ZapReaction(
accountViewModel,
zapStartingTime,
) { zapIconState ->
CrossfadeIfEnabled(targetState = zapIconState, label = "ZapIcon", accountViewModel = accountViewModel) {
CrossfadeIfEnabled(targetState = zapIconState, label = "ZapIcon") {
if (it.wasZappedByLoggedInUser) {
ZappedIcon(iconSizeModifier)
} else {
@@ -1460,7 +1459,7 @@ fun ZapReaction(
baseNote,
accountViewModel,
) { zapIconState ->
CrossfadeIfEnabled(targetState = zapIconState, label = "ZapIcon", accountViewModel = accountViewModel) {
CrossfadeIfEnabled(targetState = zapIconState, label = "ZapIcon") {
if (it.wasZappedByLoggedInUser) {
ZappedIcon(iconSizeModifier)
} else if (it.hasPendingPaymentRequest) {
@@ -50,13 +50,13 @@ import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
import com.vitorpamplona.amethyst.commons.model.Note
import com.vitorpamplona.amethyst.commons.resources.Res
import com.vitorpamplona.amethyst.commons.resources.expand_relay_list
import com.vitorpamplona.amethyst.commons.ui.components.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.commons.ui.stringRes
import com.vitorpamplona.amethyst.commons.ui.theme.ShowMoreRelaysButtonBoxModifer
import com.vitorpamplona.amethyst.commons.ui.theme.Size17Modifier
import com.vitorpamplona.amethyst.commons.ui.theme.StdVertSpacer
import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import kotlinx.coroutines.FlowPreview
@@ -130,7 +130,7 @@ private fun RenderRelaySlot(
accountViewModel: AccountViewModel,
nav: INav,
) {
CrossfadeIfEnabled(targetState = relay, label = "RenderRelay", modifier = Size17Modifier, accountViewModel = accountViewModel) {
CrossfadeIfEnabled(targetState = relay, label = "RenderRelay", modifier = Size17Modifier) {
if (it != null) {
RenderRelay(it, accountViewModel, nav)
}
@@ -56,6 +56,7 @@ import com.vitorpamplona.amethyst.commons.resources.expand_relay_list
import com.vitorpamplona.amethyst.commons.resources.relay_info
import com.vitorpamplona.amethyst.commons.ui.components.ClickableBox
import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage
import com.vitorpamplona.amethyst.commons.ui.components.util.setText
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.commons.ui.stringRes
import com.vitorpamplona.amethyst.commons.ui.theme.LargeRelayIconModifier
@@ -71,7 +72,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.relayIconModifier
import com.vitorpamplona.amethyst.commons.ui.theme.ripple24dp
import com.vitorpamplona.amethyst.commons.ui.theme.warningColorOnSecondSurface
import com.vitorpamplona.amethyst.model.nip11RelayInfo.loadRelayInfo
import com.vitorpamplona.amethyst.ui.components.util.setText
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import kotlinx.coroutines.launch
@@ -35,10 +35,12 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.unit.sp
import com.vitorpamplona.amethyst.commons.model.Note
import com.vitorpamplona.amethyst.commons.model.User
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
import com.vitorpamplona.amethyst.commons.resources.Res
import com.vitorpamplona.amethyst.commons.resources.and
import com.vitorpamplona.amethyst.commons.resources.replying_to
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.commons.ui.richtext.CreateClickableTextWithEmoji
import com.vitorpamplona.amethyst.commons.ui.stringRes
import com.vitorpamplona.amethyst.commons.ui.theme.StdVertSpacer
import com.vitorpamplona.amethyst.commons.ui.theme.lessImportantLink
@@ -72,13 +72,16 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.commons.emojicoder.EmojiCoder
import com.vitorpamplona.amethyst.commons.model.AddressableNote
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
import com.vitorpamplona.amethyst.commons.resources.Res
import com.vitorpamplona.amethyst.commons.resources.add
import com.vitorpamplona.amethyst.commons.resources.new_reaction_symbol
import com.vitorpamplona.amethyst.commons.ui.components.AnimatedBorderTextCornerRadius
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.note.LikedIcon
import com.vitorpamplona.amethyst.commons.ui.richtext.InLineIconRenderer
import com.vitorpamplona.amethyst.commons.ui.stringRes
import com.vitorpamplona.amethyst.commons.ui.theme.ButtonBorder
import com.vitorpamplona.amethyst.commons.ui.theme.StdVertSpacer
@@ -86,8 +89,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText
import com.vitorpamplona.amethyst.commons.util.firstFullChar
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteEventAndMapNotNull
import com.vitorpamplona.amethyst.ui.components.InLineIconRenderer
import com.vitorpamplona.amethyst.ui.insets.imePaddingSafe
import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor
import com.vitorpamplona.amethyst.ui.note.types.RenderEmojiPack
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@@ -110,6 +110,7 @@ import com.vitorpamplona.amethyst.commons.resources.zap_type_public_explainer
import com.vitorpamplona.amethyst.commons.resources.zap_type_section_explainer
import com.vitorpamplona.amethyst.commons.ui.components.TextSpinner
import com.vitorpamplona.amethyst.commons.ui.components.TitleExplainer
import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe
import com.vitorpamplona.amethyst.commons.ui.stringRes
import com.vitorpamplona.amethyst.commons.ui.theme.ButtonBorder
import com.vitorpamplona.amethyst.commons.ui.theme.SettingsCategoryFirstModifier
@@ -117,7 +118,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.SettingsCategorySpacingModifi
import com.vitorpamplona.amethyst.commons.ui.theme.Size20Modifier
import com.vitorpamplona.amethyst.commons.ui.theme.ThemeComparisonRow
import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText
import com.vitorpamplona.amethyst.ui.insets.imePaddingSafe
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.keyBackup.getFragmentActivity
import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel
@@ -34,6 +34,7 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.commons.model.User
import com.vitorpamplona.amethyst.commons.model.navigation.Route
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
import com.vitorpamplona.amethyst.commons.profile.ui.FollowButton
import com.vitorpamplona.amethyst.commons.profile.ui.ListButton
import com.vitorpamplona.amethyst.commons.profile.ui.UnfollowButton
@@ -39,6 +39,7 @@ 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.routeFor
import com.vitorpamplona.amethyst.commons.resources.Res
import com.vitorpamplona.amethyst.commons.resources.profile_image
import com.vitorpamplona.amethyst.commons.resources.profile_image_of_user
@@ -23,30 +23,26 @@ package com.vitorpamplona.amethyst.ui.note
import android.content.Context
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.compose.LocalLifecycleOwner
import com.vitorpamplona.amethyst.commons.model.ImmutableListOfLists
import com.vitorpamplona.amethyst.commons.model.Note
import com.vitorpamplona.amethyst.commons.model.User
import com.vitorpamplona.amethyst.commons.ui.components.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.commons.ui.note.PlayIcon
import com.vitorpamplona.amethyst.commons.ui.note.UserNameText
import com.vitorpamplona.amethyst.commons.ui.theme.StdButtonSizeModifier
import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNote
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserInfo
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserNickname
import com.vitorpamplona.amethyst.service.tts.TextToSpeechHelper
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.utils.Log
@@ -91,7 +87,7 @@ fun WatchAuthorWithBlank(
inner(noteAuthor)
} else {
val authorState by observeNote(baseNote, accountViewModel)
CrossfadeIfEnabled(targetState = authorState.note.author, modifier = modifier, label = "WatchAuthorWithBlank", accountViewModel = accountViewModel) { newAuthor ->
CrossfadeIfEnabled(targetState = authorState.note.author, modifier = modifier, label = "WatchAuthorWithBlank") { newAuthor ->
inner(newAuthor)
}
}
@@ -109,59 +105,22 @@ fun UsernameDisplay(
val userMetadata by observeUserInfo(baseUser, accountViewModel)
val nickname by observeUserNickname(baseUser, accountViewModel)
CrossfadeIfEnabled(targetState = userMetadata, modifier = weight, label = "UsernameDisplay", accountViewModel = accountViewModel) {
CrossfadeIfEnabled(targetState = userMetadata, modifier = weight, label = "UsernameDisplay") {
// the account's own nickname for this user wins over the user's metadata;
// its custom emojis resolve against the contact card's tags, not the profile's
val petName = nickname?.petName
val name = petName ?: it?.info?.bestName()
if (name != null) {
UserDisplay(name, if (petName != null) nickname?.tags else it?.tags, weight, fontWeight, textColor, textAlign)
} else {
NPubDisplay(baseUser, weight, fontWeight, textColor, textAlign)
}
UserNameText(
user = baseUser,
displayName = petName ?: it?.info?.bestName(),
tags = if (petName != null) nickname?.tags else it?.tags,
modifier = weight,
fontWeight = fontWeight,
textColor = textColor,
textAlign = textAlign,
)
}
}
@Composable
private fun NPubDisplay(
user: User,
modifier: Modifier,
fontWeight: FontWeight = FontWeight.Bold,
textColor: Color = Color.Unspecified,
textAlign: TextAlign? = null,
) {
Text(
text = remember { user.pubkeyDisplayHex() },
fontWeight = fontWeight,
modifier = modifier,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
color = textColor,
textAlign = textAlign,
)
}
@Composable
private fun UserDisplay(
bestDisplayName: String,
tags: ImmutableListOfLists<String>?,
modifier: Modifier,
fontWeight: FontWeight = FontWeight.Bold,
textColor: Color = Color.Unspecified,
textAlign: TextAlign? = null,
) {
CreateTextWithEmoji(
text = bestDisplayName,
tags = tags,
fontWeight = fontWeight,
textAlign = textAlign,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = modifier,
color = textColor,
)
}
@Composable
fun DrawPlayName(name: String) {
val context = LocalContext.current
@@ -27,10 +27,10 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import com.vitorpamplona.amethyst.commons.model.Note
import com.vitorpamplona.amethyst.commons.ui.components.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteHasEvent
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.UserFinderByParentFilterAssemblerSubscription
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@OptIn(ExperimentalFoundationApi::class)
@@ -80,7 +80,7 @@ fun WatchNoteEvent(
} else {
// avoid observing costs if already has an event.
val hasEvent by observeNoteHasEvent(baseNote, accountViewModel)
CrossfadeIfEnabled(targetState = hasEvent, accountViewModel = accountViewModel) {
CrossfadeIfEnabled(targetState = hasEvent) {
if (it) {
onNoteEventFound()
} else {
@@ -99,6 +99,7 @@ import com.vitorpamplona.amethyst.commons.resources.zap_type_private
import com.vitorpamplona.amethyst.commons.resources.zap_type_private_explainer
import com.vitorpamplona.amethyst.commons.resources.zap_type_public
import com.vitorpamplona.amethyst.commons.resources.zap_type_public_explainer
import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.TopBarWithBackButton
import com.vitorpamplona.amethyst.commons.ui.note.buttons.CloseButton
@@ -113,7 +114,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.service.ZapPaymentHandler
import com.vitorpamplona.amethyst.ui.components.toasts.multiline.UserBasedErrorMessage
import com.vitorpamplona.amethyst.ui.insets.imePaddingSafe
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.wallet.OnchainZapSendDialog
import com.vitorpamplona.quartz.nip01Core.core.Event
@@ -33,6 +33,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.commons.ui.note.ZappedIcon
import com.vitorpamplona.amethyst.commons.ui.theme.DoubleVertSpacer
@@ -46,6 +46,7 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.commons.hashtags.CustomHashTagIcons
import com.vitorpamplona.amethyst.commons.hashtags.Lightning
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
import com.vitorpamplona.amethyst.commons.resources.Res
import com.vitorpamplona.amethyst.commons.resources.clink_lightning_offer
import com.vitorpamplona.amethyst.commons.resources.clink_offer_amount_range
@@ -47,12 +47,12 @@ import com.vitorpamplona.amethyst.commons.resources.invoice_expired
import com.vitorpamplona.amethyst.commons.resources.lightning_invoice
import com.vitorpamplona.amethyst.commons.resources.pay
import com.vitorpamplona.amethyst.commons.resources.sats
import com.vitorpamplona.amethyst.commons.ui.components.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.commons.ui.stringRes
import com.vitorpamplona.amethyst.commons.ui.theme.ButtonBorder
import com.vitorpamplona.amethyst.commons.ui.theme.Size18Modifier
import com.vitorpamplona.amethyst.service.lnurl.CachedLnInvoiceParser
import com.vitorpamplona.amethyst.service.lnurl.InvoiceAmount
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.ui.components.PaymentCard
import com.vitorpamplona.amethyst.ui.components.PaymentCardAmount
import com.vitorpamplona.amethyst.ui.components.PaymentCardDescription
@@ -85,7 +85,7 @@ fun MayBeInvoicePreview(
accountViewModel: AccountViewModel,
) {
LoadValueFromInvoice(lnbcWord = lnbcWord) { invoiceAmount ->
CrossfadeIfEnabled(targetState = invoiceAmount, label = "MayBeInvoicePreview", accountViewModel = accountViewModel) {
CrossfadeIfEnabled(targetState = invoiceAmount, label = "MayBeInvoicePreview") {
if (it != null) {
InvoicePreview(it.invoice, it.amount, it.description, it.expiresAt, accountViewModel)
} else {
@@ -95,10 +95,10 @@ import com.vitorpamplona.amethyst.commons.resources.location_picker_search_empty
import com.vitorpamplona.amethyst.commons.resources.location_picker_search_hint
import com.vitorpamplona.amethyst.commons.resources.location_picker_title
import com.vitorpamplona.amethyst.commons.resources.location_picker_use_mine
import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe
import com.vitorpamplona.amethyst.commons.ui.stringRes
import com.vitorpamplona.amethyst.service.location.ForwardGeolocation
import com.vitorpamplona.amethyst.service.location.LocationState
import com.vitorpamplona.amethyst.ui.insets.imePaddingSafe
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.geohashChat.label
import com.vitorpamplona.quartz.experimental.bitchat.geohash.GeohashChannelLevel
import com.vitorpamplona.quartz.nip01Core.tags.geohash.GeoHash
@@ -42,12 +42,12 @@ import androidx.compose.ui.text.style.TextOverflow
import coil3.compose.AsyncImage
import com.vitorpamplona.amethyst.commons.model.navigation.Route
import com.vitorpamplona.amethyst.commons.richtext.RichTextParser
import com.vitorpamplona.amethyst.commons.ui.components.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.commons.ui.components.UrlPreviewState
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.commons.ui.state.produceCachedState
import com.vitorpamplona.amethyst.model.UrlCachedPreviewer
import com.vitorpamplona.amethyst.service.playback.composable.VideoView
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.ui.components.ClickableUrl
import com.vitorpamplona.amethyst.ui.components.DisplayUrlWithLoadingSymbol
import com.vitorpamplona.amethyst.ui.components.UrlPreviewCard
@@ -217,7 +217,6 @@ private fun MyLoadUrlPreviewDirect(
CrossfadeIfEnabled(
targetState = urlPreviewState,
label = "UrlPreview",
accountViewModel = accountViewModel,
) { state ->
when (state) {
is UrlPreviewState.Loaded -> {
@@ -286,7 +285,6 @@ private fun MyLoadUrlPreviewDirectFillWidth(
CrossfadeIfEnabled(
targetState = urlPreviewState,
label = "UrlPreview",
accountViewModel = accountViewModel,
) { state ->
when (state) {
is UrlPreviewState.Loaded -> {
@@ -26,6 +26,7 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
import com.vitorpamplona.amethyst.commons.model.Note
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
import com.vitorpamplona.amethyst.commons.resources.Res
import com.vitorpamplona.amethyst.commons.resources.forked_from
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
@@ -70,10 +70,10 @@ import com.vitorpamplona.amethyst.commons.resources.timestamp_it
import com.vitorpamplona.amethyst.commons.resources.timestamp_pending
import com.vitorpamplona.amethyst.commons.resources.unfollow
import com.vitorpamplona.amethyst.commons.resources.unpin_from_profile
import com.vitorpamplona.amethyst.commons.ui.components.util.setText
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.commons.ui.stringRes
import com.vitorpamplona.amethyst.commons.ui.theme.LightRedColor
import com.vitorpamplona.amethyst.ui.components.util.setText
import com.vitorpamplona.amethyst.ui.note.QuickActionAlertDialogOneButton
import com.vitorpamplona.amethyst.ui.note.copyNoteTextAction
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@@ -76,6 +76,7 @@ import com.vitorpamplona.amethyst.commons.resources.lightning_invoice
import com.vitorpamplona.amethyst.commons.resources.post_anonymously
import com.vitorpamplona.amethyst.commons.resources.what_s_on_your_mind
import com.vitorpamplona.amethyst.commons.resources.zapraiser
import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe
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
@@ -103,7 +104,6 @@ import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia
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.insets.imePaddingSafe
import com.vitorpamplona.amethyst.ui.navigation.navs.Nav
import com.vitorpamplona.amethyst.ui.note.BaseUserPicture
import com.vitorpamplona.amethyst.ui.note.NoteCompose
@@ -80,6 +80,7 @@ import coil3.compose.AsyncImage
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.model.EmptyTagList
import com.vitorpamplona.amethyst.commons.model.Note
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
import com.vitorpamplona.amethyst.commons.model.toImmutableListOfLists
import com.vitorpamplona.amethyst.commons.resources.Res
import com.vitorpamplona.amethyst.commons.resources.app_definition_available_on
@@ -101,16 +102,16 @@ import com.vitorpamplona.amethyst.commons.resources.profile_banner
import com.vitorpamplona.amethyst.commons.resources.profile_image
import com.vitorpamplona.amethyst.commons.richtext.RichTextParser
import com.vitorpamplona.amethyst.commons.ui.components.ClickableTextPrimary
import com.vitorpamplona.amethyst.commons.ui.components.util.setText
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.commons.ui.note.LinkIcon
import com.vitorpamplona.amethyst.commons.ui.richtext.CreateTextWithEmoji
import com.vitorpamplona.amethyst.commons.ui.stringRes
import com.vitorpamplona.amethyst.commons.ui.theme.Size16Modifier
import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNote
import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
import com.vitorpamplona.amethyst.ui.components.ZoomableImageDialog
import com.vitorpamplona.amethyst.ui.components.util.setText
import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor
import com.vitorpamplona.amethyst.ui.note.BaseUserPicture
import com.vitorpamplona.amethyst.ui.note.LoadAddressableNote
@@ -43,6 +43,7 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.commons.model.Note
import com.vitorpamplona.amethyst.commons.model.User
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
import com.vitorpamplona.amethyst.commons.model.toImmutableListOfLists
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.service.playback.composable.LoadThumbAndThenVideoView
@@ -63,6 +63,7 @@ import com.vitorpamplona.amethyst.commons.model.Note
import com.vitorpamplona.amethyst.commons.model.User
import com.vitorpamplona.amethyst.commons.model.cache.LocalCache
import com.vitorpamplona.amethyst.commons.model.navigation.Route
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
import com.vitorpamplona.amethyst.commons.resources.Res
import com.vitorpamplona.amethyst.commons.resources.accept_badge
import com.vitorpamplona.amethyst.commons.resources.award_badge
@@ -32,6 +32,7 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.commons.model.Note
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
import com.vitorpamplona.amethyst.commons.ui.components.GenericLoadable
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.commons.ui.theme.StdVertSpacer
@@ -33,6 +33,7 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.commons.model.Note
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
import com.vitorpamplona.amethyst.commons.ui.components.GenericLoadable
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.commons.ui.theme.HalfVertPadding
@@ -46,6 +46,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel
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.model.navigation.routeFor
import com.vitorpamplona.amethyst.commons.nip64Chess.ChessChallenge
import com.vitorpamplona.amethyst.commons.nip64Chess.ui.ChessGameViewer
import com.vitorpamplona.amethyst.commons.resources.Res
@@ -62,6 +62,7 @@ import com.vitorpamplona.amethyst.commons.model.EmptyTagList
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.model.navigation.routeFor
import com.vitorpamplona.amethyst.commons.model.toImmutableListOfLists
import com.vitorpamplona.amethyst.commons.resources.Res
import com.vitorpamplona.amethyst.commons.resources.about_us
@@ -49,11 +49,11 @@ import androidx.compose.ui.unit.dp
import coil3.compose.AsyncImage
import com.vitorpamplona.amethyst.commons.model.Note
import com.vitorpamplona.amethyst.commons.ui.components.AddButton
import com.vitorpamplona.amethyst.commons.ui.components.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.commons.ui.components.RemoveButton
import com.vitorpamplona.amethyst.commons.ui.theme.Size35Modifier
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteAndMap
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteEvent
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.ui.components.ShowMoreButton
import com.vitorpamplona.amethyst.ui.note.LoadAddressableNote
import com.vitorpamplona.amethyst.ui.note.getGradient
@@ -189,7 +189,7 @@ private fun EmojiListOptions(
usersEmojiList.event?.isTaggedAddressableNote(emojiPackNote.idHex)
}
CrossfadeIfEnabled(targetState = hasAddedThis, label = "EmojiListOptions", accountViewModel = accountViewModel) {
CrossfadeIfEnabled(targetState = hasAddedThis, label = "EmojiListOptions") {
if (it != true) {
AddButton(modifier = Modifier.padding(start = 3.dp)) { accountViewModel.addEmojiPack(emojiPackNote) }
} else {
@@ -56,6 +56,7 @@ import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
import com.vitorpamplona.amethyst.commons.model.AddressableNote
import com.vitorpamplona.amethyst.commons.model.Note
import com.vitorpamplona.amethyst.commons.model.navigation.Route
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
import com.vitorpamplona.amethyst.commons.model.toImmutableListOfLists
import com.vitorpamplona.amethyst.commons.resources.Res
import com.vitorpamplona.amethyst.commons.resources.preview_card_image_for
@@ -30,8 +30,8 @@ import com.vitorpamplona.amethyst.commons.model.Note
import com.vitorpamplona.amethyst.commons.richtext.BaseMediaContent
import com.vitorpamplona.amethyst.commons.richtext.MediaLocalImage
import com.vitorpamplona.amethyst.commons.richtext.MediaLocalVideo
import com.vitorpamplona.amethyst.commons.ui.components.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNote
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.ui.components.LoadNote
import com.vitorpamplona.amethyst.ui.components.SensitivityWarning
import com.vitorpamplona.amethyst.ui.components.ZoomableContentView
@@ -110,7 +110,7 @@ private fun ObserverAndRenderNIP95(
mutableStateOf<BaseMediaContent?>(newContent)
}
CrossfadeIfEnabled(targetState = content, accountViewModel = accountViewModel) {
CrossfadeIfEnabled(targetState = content) {
if (it != null) {
SensitivityWarning(note = header, accountViewModel = accountViewModel) {
ZoomableContentView(
@@ -43,6 +43,7 @@ import androidx.compose.ui.unit.sp
import com.vitorpamplona.amethyst.commons.model.EmptyTagList
import com.vitorpamplona.amethyst.commons.model.Note
import com.vitorpamplona.amethyst.commons.model.cache.LocalCache
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
import com.vitorpamplona.amethyst.commons.model.toImmutableListOfLists
import com.vitorpamplona.amethyst.commons.resources.Res
import com.vitorpamplona.amethyst.commons.resources.follow_list_item_label
@@ -48,6 +48,7 @@ import com.vitorpamplona.amethyst.commons.resources.copy_to_clipboard
import com.vitorpamplona.amethyst.commons.resources.fundraiser_ends
import com.vitorpamplona.amethyst.commons.resources.fundraiser_onchain_donation
import com.vitorpamplona.amethyst.commons.resources.preview_card_image_for
import com.vitorpamplona.amethyst.commons.ui.components.util.setText
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.commons.ui.note.CopyIcon
import com.vitorpamplona.amethyst.commons.ui.stringRes
@@ -55,7 +56,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.Size18Modifier
import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText
import com.vitorpamplona.amethyst.commons.ui.theme.replyModifier
import com.vitorpamplona.amethyst.ui.components.MyAsyncImage
import com.vitorpamplona.amethyst.ui.components.util.setText
import com.vitorpamplona.amethyst.ui.note.elements.DefaultImageHeader
import com.vitorpamplona.amethyst.ui.note.elements.DefaultImageHeaderBackground
import com.vitorpamplona.amethyst.ui.note.elements.DisplayUncitedHashtags
@@ -47,6 +47,7 @@ import com.vitorpamplona.amethyst.commons.model.ImmutableListOfLists
import com.vitorpamplona.amethyst.commons.model.Note
import com.vitorpamplona.amethyst.commons.model.User
import com.vitorpamplona.amethyst.commons.model.highlights.HighlightQuote
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
import com.vitorpamplona.amethyst.commons.model.toImmutableListOfLists
import com.vitorpamplona.amethyst.commons.ui.components.ClickableTextPrimary
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.EmptyNav
@@ -54,6 +55,7 @@ import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.commons.ui.note.HighlightQuoteIndent
import com.vitorpamplona.amethyst.commons.ui.note.HighlightQuoteSpacing
import com.vitorpamplona.amethyst.commons.ui.note.HighlightedQuote
import com.vitorpamplona.amethyst.commons.ui.richtext.CreateClickableTextWithEmoji
import com.vitorpamplona.amethyst.commons.ui.theme.ThemeComparisonColumn
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNote
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserInfo
@@ -48,10 +48,12 @@ import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.commons.model.Note
import com.vitorpamplona.amethyst.commons.model.User
import com.vitorpamplona.amethyst.commons.model.cache.LocalCache
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
import com.vitorpamplona.amethyst.commons.resources.Res
import com.vitorpamplona.amethyst.commons.resources.live_stream_has_ended
import com.vitorpamplona.amethyst.commons.resources.live_stream_is_offline
import com.vitorpamplona.amethyst.commons.resources.preview_card_image_for
import com.vitorpamplona.amethyst.commons.ui.components.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.EmptyNav
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.commons.ui.stringRes
@@ -61,7 +63,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.imageModifier
import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText
import com.vitorpamplona.amethyst.service.playback.composable.VideoView
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNote
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.ui.components.MyAsyncImage
import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor
import com.vitorpamplona.amethyst.ui.note.ClickableUserPicture
@@ -164,7 +165,7 @@ fun RenderLiveActivityEventInner(
Spacer(modifier = StdHorzSpacer)
CrossfadeIfEnabled(targetState = status, label = "RenderLiveActivityEventInner", accountViewModel = accountViewModel) {
CrossfadeIfEnabled(targetState = status, label = "RenderLiveActivityEventInner") {
when (it) {
StatusTag.STATUS.LIVE -> {
media?.let {
@@ -48,6 +48,7 @@ import androidx.compose.ui.unit.sp
import androidx.core.net.toUri
import com.vitorpamplona.amethyst.commons.model.Note
import com.vitorpamplona.amethyst.commons.model.User
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
import com.vitorpamplona.amethyst.commons.resources.Res
import com.vitorpamplona.amethyst.commons.resources.meeting_room_in_space
import com.vitorpamplona.amethyst.commons.resources.meeting_room_in_space_unknown
@@ -64,6 +65,7 @@ import com.vitorpamplona.amethyst.commons.resources.nest_presence_listening
import com.vitorpamplona.amethyst.commons.resources.nest_presence_on_stage
import com.vitorpamplona.amethyst.commons.resources.nest_presence_raised_hand
import com.vitorpamplona.amethyst.commons.resources.nest_presence_speaking
import com.vitorpamplona.amethyst.commons.ui.components.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.commons.ui.stringRes
import com.vitorpamplona.amethyst.commons.ui.theme.SmallBorder
@@ -71,7 +73,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.SpacedBy5dp
import com.vitorpamplona.amethyst.commons.ui.theme.StdHorzSpacer
import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNote
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor
import com.vitorpamplona.amethyst.ui.note.ClickableUserPicture
import com.vitorpamplona.amethyst.ui.note.UsernameDisplay
@@ -140,7 +141,7 @@ fun RenderMeetingSpaceEventInner(
Spacer(modifier = StdHorzSpacer)
CrossfadeIfEnabled(targetState = status, label = "MeetingSpaceStatus", accountViewModel = accountViewModel) {
CrossfadeIfEnabled(targetState = status, label = "MeetingSpaceStatus") {
when (it) {
MeetingSpaceStatusTag.STATUS.LIVE -> {
MeetingSpaceOpenFlag()
@@ -295,7 +296,7 @@ fun RenderMeetingRoomEventInner(
Spacer(modifier = StdHorzSpacer)
CrossfadeIfEnabled(targetState = status, label = "MeetingRoomStatus", accountViewModel = accountViewModel) {
CrossfadeIfEnabled(targetState = status, label = "MeetingRoomStatus") {
when (it) {
StatusTag.STATUS.LIVE -> {
media?.let {
@@ -71,11 +71,11 @@ import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
import com.vitorpamplona.amethyst.commons.model.Note
import com.vitorpamplona.amethyst.commons.model.cache.LocalCache
import com.vitorpamplona.amethyst.commons.ui.components.util.setText
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.commons.ui.theme.Size16Modifier
import com.vitorpamplona.amethyst.commons.ui.theme.Size25dp
import com.vitorpamplona.amethyst.commons.ui.theme.bitcoinColor
import com.vitorpamplona.amethyst.ui.components.util.setText
import com.vitorpamplona.amethyst.ui.note.UserPicture
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nipBCOnchainZaps.chain.BitcoinTx
@@ -42,6 +42,7 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.commons.model.User
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
import com.vitorpamplona.amethyst.commons.resources.Res
import com.vitorpamplona.amethyst.commons.resources.podcast_hosts_and_guests
import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage
@@ -74,6 +74,7 @@ import com.vitorpamplona.amethyst.commons.model.Note
import com.vitorpamplona.amethyst.commons.model.User
import com.vitorpamplona.amethyst.commons.model.cache.LocalCache
import com.vitorpamplona.amethyst.commons.model.navigation.Route
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
import com.vitorpamplona.amethyst.commons.model.nip88Polls.PollResponsesCache
import com.vitorpamplona.amethyst.commons.model.nip88Polls.TallyResults
import com.vitorpamplona.amethyst.commons.model.toImmutableListOfLists
@@ -36,6 +36,7 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.commons.model.EmptyTagList
import com.vitorpamplona.amethyst.commons.model.Note
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
import com.vitorpamplona.amethyst.commons.model.toImmutableListOfLists
import com.vitorpamplona.amethyst.commons.resources.Res
import com.vitorpamplona.amethyst.commons.resources.private_conversation_notification
@@ -61,12 +61,14 @@ import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
import com.vitorpamplona.amethyst.commons.model.ImmutableListOfLists
import com.vitorpamplona.amethyst.commons.model.Note
import com.vitorpamplona.amethyst.commons.model.User
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
import com.vitorpamplona.amethyst.commons.resources.Res
import com.vitorpamplona.amethyst.commons.resources.number_followers
import com.vitorpamplona.amethyst.commons.resources.profile_card_bot
import com.vitorpamplona.amethyst.commons.resources.profile_card_follows_you
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.commons.ui.note.ShowUserButton
import com.vitorpamplona.amethyst.commons.ui.richtext.CreateTextWithEmoji
import com.vitorpamplona.amethyst.commons.ui.stringRes
import com.vitorpamplona.amethyst.commons.ui.theme.Size16Modifier
import com.vitorpamplona.amethyst.commons.ui.theme.Size5dp
@@ -77,7 +79,6 @@ import com.vitorpamplona.amethyst.service.relayClient.reqCommand.account.observe
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserContactCardsFollowerCount
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserInfo
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserIsFollowing
import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji
import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor
import com.vitorpamplona.amethyst.ui.note.BaseUserPicture
import com.vitorpamplona.amethyst.ui.note.ObserveDisplayNip05Status
@@ -46,6 +46,7 @@ import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
import com.vitorpamplona.amethyst.commons.model.AddressableNote
import com.vitorpamplona.amethyst.commons.model.Note
import com.vitorpamplona.amethyst.commons.model.navigation.Route
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
import com.vitorpamplona.amethyst.commons.model.toImmutableListOfLists
import com.vitorpamplona.amethyst.commons.resources.Res
import com.vitorpamplona.amethyst.commons.resources.publication_untitled_section
@@ -45,6 +45,7 @@ import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.commons.model.Note
import com.vitorpamplona.amethyst.commons.model.cache.LocalCache
import com.vitorpamplona.amethyst.commons.model.navigation.Route
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
import com.vitorpamplona.amethyst.commons.model.nip29RelayGroups.RelayGroupChannel
import com.vitorpamplona.amethyst.commons.nip29RelayGroups.ui.RelayNameChip
import com.vitorpamplona.amethyst.commons.ui.components.GenericLoadable
@@ -58,11 +58,11 @@ import com.vitorpamplona.amethyst.commons.resources.public_home_section
import com.vitorpamplona.amethyst.commons.resources.public_notif_section
import com.vitorpamplona.amethyst.commons.resources.search_relays_title
import com.vitorpamplona.amethyst.commons.resources.trusted_relays_title
import com.vitorpamplona.amethyst.commons.ui.components.util.setText
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.commons.ui.stringRes
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserRelayIntoList
import com.vitorpamplona.amethyst.ui.components.ShowMoreButton
import com.vitorpamplona.amethyst.ui.components.util.setText
import com.vitorpamplona.amethyst.ui.note.AddRelayButton
import com.vitorpamplona.amethyst.ui.note.RemoveRelayButton
import com.vitorpamplona.amethyst.ui.note.getGradient

Some files were not shown because too many files have changed in this diff Show More