mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-10 16:33:27 +00:00
feat(ui): unify note-header markers into a two-tier pill/quiet-mark system
Tier 1 (HeaderPill): tappable or verifiable metadata now renders as the squared secondary-container chip — location, PoW, OTS (already pills), plus expiration and the bounty reward (bolt icon + amount, tap opens the pledge dialog; icon turns orange once the user has pledged). Tier 2 (QuietMark, new): passive state markers — Boosted, Draft, Edited/ Original, pinned, private rumor — share one spec: 12sp medium gray text with an optional 12dp icon. Draft is no longer a hardcoded English literal (new R.string.draft). Cross-cutting cleanups: - Header rows (NoteCompose First/SecondUserInfoRow, ThreadFeedView) own spacing via Arrangement.spacedBy(5dp) instead of per-marker paddings. - Hashtag/community/fork links use the existing lessImportantLink theme color instead of ad-hoc primary.copy(alpha = 0.52f) (fork was full primary, louder than everything else on the line). - NIP-05 badge drops hardcoded Color.Yellow/Color.Red for theme-aware placeholderText/error. - Note-header timestamps switch to the Short style at 12sp; other TimeAgo callers keep the dotted default. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AgEpNtwnetPhETXQSdq4b5
This commit is contained in:
+2
-2
@@ -472,7 +472,7 @@ fun RenderNIP05VerifiedSymbol(
|
||||
symbol = MaterialSymbols.Downloading,
|
||||
contentDescription = stringRes(id = R.string.nip05_checking),
|
||||
modifier = modifier,
|
||||
tint = Color.Yellow,
|
||||
tint = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -492,7 +492,7 @@ fun RenderNIP05VerifiedSymbol(
|
||||
symbol = MaterialSymbols.Report,
|
||||
contentDescription = stringRes(id = R.string.nip05_failed),
|
||||
modifier = modifier,
|
||||
tint = Color.Red,
|
||||
tint = MaterialTheme.colorScheme.error,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ package com.vitorpamplona.amethyst.ui.note
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
@@ -32,7 +33,6 @@ import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
@@ -53,7 +53,6 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.compositeOver
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.R
|
||||
@@ -85,11 +84,14 @@ import com.vitorpamplona.amethyst.ui.note.elements.DisplayLocation
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.DisplayOts
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.DisplayPoW
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.DisplayReward
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.HeaderPill
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.MoreOptionsButton
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.QuietMark
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.Reward
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.ShowForkInformation
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.StaleRelayHint
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.TimeAgo
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.TimeAgoStyle
|
||||
import com.vitorpamplona.amethyst.ui.note.types.BadgeDisplay
|
||||
import com.vitorpamplona.amethyst.ui.note.types.DisplayBlockedRelayList
|
||||
import com.vitorpamplona.amethyst.ui.note.types.DisplayBroadcastRelayList
|
||||
@@ -207,8 +209,6 @@ import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.Font12SP
|
||||
import com.vitorpamplona.amethyst.ui.theme.HalfDoubleVertSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.HalfPadding
|
||||
import com.vitorpamplona.amethyst.ui.theme.HalfStartPadding
|
||||
import com.vitorpamplona.amethyst.ui.theme.Height4dpModifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size10dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size25dp
|
||||
@@ -216,7 +216,7 @@ import com.vitorpamplona.amethyst.ui.theme.Size30dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size34dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size55Modifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size55dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size5dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.UserNameMaxRowHeight
|
||||
import com.vitorpamplona.amethyst.ui.theme.UserNameRowHeight
|
||||
import com.vitorpamplona.amethyst.ui.theme.channelNotePictureModifier
|
||||
@@ -1743,6 +1743,7 @@ fun SecondUserInfoRow(
|
||||
|
||||
Row(
|
||||
verticalAlignment = CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(Size5dp),
|
||||
modifier = UserNameMaxRowHeight,
|
||||
) {
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
@@ -1755,32 +1756,19 @@ fun SecondUserInfoRow(
|
||||
|
||||
val baseReward = remember(noteEvent) { noteEvent.bountyBaseReward()?.let { Reward(it) } }
|
||||
if (baseReward != null) {
|
||||
Spacer(StdHorzSpacer)
|
||||
DisplayReward(baseReward, note, accountViewModel, nav)
|
||||
DisplayReward(baseReward, note, accountViewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DisplayExpiration(expirationDate: Long) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(
|
||||
symbol = MaterialSymbols.Timer,
|
||||
contentDescription = stringRes(R.string.expiration_date_label),
|
||||
modifier = Modifier.padding(start = 5.dp).size(12.dp),
|
||||
tint = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
val context = LocalContext.current
|
||||
Text(
|
||||
text = timeAheadNoDot(expirationDate, context),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
fontSize = Font12SP,
|
||||
maxLines = 1,
|
||||
modifier = Modifier.padding(start = 3.dp),
|
||||
)
|
||||
}
|
||||
val context = LocalContext.current
|
||||
HeaderPill(
|
||||
symbol = MaterialSymbols.Timer,
|
||||
text = timeAheadNoDot(expirationDate, context),
|
||||
contentDescription = stringRes(R.string.expiration_date_label),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -1800,26 +1788,12 @@ fun DisplayOtsIfInOriginal(
|
||||
|
||||
@Composable
|
||||
fun DisplayDraft() {
|
||||
Text(
|
||||
"Draft",
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = Font12SP,
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
maxLines = 1,
|
||||
modifier = HalfStartPadding,
|
||||
)
|
||||
QuietMark(text = stringRes(R.string.draft))
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DisplayDraftChat() {
|
||||
Text(
|
||||
"Draft",
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
modifier = Modifier,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = Font12SP,
|
||||
maxLines = 1,
|
||||
)
|
||||
QuietMark(text = stringRes(R.string.draft))
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -1832,7 +1806,11 @@ fun FirstUserInfoRow(
|
||||
nav: INav,
|
||||
moreOptions: (@Composable () -> Unit)? = null,
|
||||
) {
|
||||
Row(verticalAlignment = CenterVertically, modifier = UserNameRowHeight) {
|
||||
Row(
|
||||
verticalAlignment = CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(Size5dp),
|
||||
modifier = UserNameRowHeight,
|
||||
) {
|
||||
val isRepost = baseNote.event is RepostEvent || baseNote.event is GenericRepostEvent
|
||||
val isDraft = baseNote.isDraft()
|
||||
val textColor = if (isRepost) MaterialTheme.colorScheme.grayText else Color.Unspecified
|
||||
@@ -1849,12 +1827,10 @@ fun FirstUserInfoRow(
|
||||
if (zapSender != null) {
|
||||
if (showAuthorPicture) {
|
||||
UserPicture(zapSender, Size25dp, accountViewModel = accountViewModel, nav = nav)
|
||||
Spacer(HalfPadding)
|
||||
}
|
||||
UsernameDisplay(zapSender, Modifier.weight(1f), textColor = textColor, accountViewModel = accountViewModel)
|
||||
} else if (showAuthorPicture) {
|
||||
NoteAuthorPicture(baseNote, Size25dp, accountViewModel = accountViewModel, nav = nav)
|
||||
Spacer(HalfPadding)
|
||||
NoteUsernameDisplay(baseNote, Modifier.weight(1f), textColor = textColor, accountViewModel = accountViewModel)
|
||||
} else {
|
||||
NoteUsernameDisplay(baseNote, Modifier.weight(1f), textColor = textColor, accountViewModel = accountViewModel)
|
||||
@@ -1908,13 +1884,11 @@ fun FirstUserInfoRow(
|
||||
|
||||
val geo = remember(noteEvent) { noteEvent?.geoHashOrScope() }
|
||||
if (geo != null) {
|
||||
Spacer(StdHorzSpacer)
|
||||
DisplayLocation(geo, accountViewModel, nav)
|
||||
}
|
||||
|
||||
val pow = remember(noteEvent) { noteEvent?.strongPoWOrNull() }
|
||||
if (pow != null) {
|
||||
Spacer(StdHorzSpacer)
|
||||
DisplayPoW(pow)
|
||||
}
|
||||
|
||||
@@ -1924,7 +1898,7 @@ fun FirstUserInfoRow(
|
||||
|
||||
JumpToParentReplyButton(baseNote, accountViewModel, nav)
|
||||
|
||||
TimeAgo(baseNote)
|
||||
TimeAgo(baseNote, style = TimeAgoStyle.Short, fontSize = Font12SP)
|
||||
|
||||
if (moreOptions == null) {
|
||||
MoreOptionsButton(baseNote, editState, accountViewModel, nav)
|
||||
@@ -1936,21 +1910,17 @@ fun FirstUserInfoRow(
|
||||
|
||||
@Composable
|
||||
fun PinnedMark() {
|
||||
Icon(
|
||||
QuietMark(
|
||||
symbol = MaterialSymbols.PushPin,
|
||||
contentDescription = stringRes(R.string.pinned_notes),
|
||||
modifier = Modifier.padding(start = 5.dp).size(14.dp),
|
||||
tint = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PrivateRumorMark() {
|
||||
Icon(
|
||||
QuietMark(
|
||||
symbol = MaterialSymbols.Lock,
|
||||
contentDescription = stringRes(R.string.private_rumor_mark),
|
||||
modifier = Modifier.padding(start = 5.dp).size(14.dp),
|
||||
tint = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1976,7 +1946,7 @@ fun JumpToParentReplyButton(
|
||||
} ?: return
|
||||
|
||||
ClickableBox(
|
||||
modifier = Modifier.padding(start = 5.dp).size(20.dp),
|
||||
modifier = Modifier.size(20.dp),
|
||||
onClick = {
|
||||
nav.nav { routeFor(parentNote, accountViewModel.account) }
|
||||
},
|
||||
|
||||
@@ -20,24 +20,11 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.note.elements
|
||||
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.Font12SP
|
||||
import com.vitorpamplona.amethyst.ui.theme.HalfStartPadding
|
||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||
|
||||
@Composable
|
||||
fun BoostedMark() {
|
||||
Text(
|
||||
stringRes(id = R.string.boosted),
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = Font12SP,
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
maxLines = 1,
|
||||
modifier = HalfStartPadding,
|
||||
)
|
||||
QuietMark(text = stringRes(id = R.string.boosted))
|
||||
}
|
||||
|
||||
+3
-3
@@ -36,7 +36,7 @@ import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.theme.Font12SP
|
||||
import com.vitorpamplona.amethyst.ui.theme.HalfStartPadding
|
||||
import com.vitorpamplona.amethyst.ui.theme.lessImportantLink
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Address
|
||||
import com.vitorpamplona.quartz.nip72ModCommunities.communityAddress
|
||||
import com.vitorpamplona.quartz.nip72ModCommunities.definition.CommunityDefinitionEvent
|
||||
@@ -47,7 +47,7 @@ fun DisplayFollowingCommunityInPost(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
Column(HalfStartPadding) {
|
||||
Column {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) { DisplayCommunity(baseNote, accountViewModel, nav) }
|
||||
}
|
||||
}
|
||||
@@ -67,7 +67,7 @@ private fun DisplayCommunity(
|
||||
ClickableTextColor(
|
||||
label,
|
||||
style = LocalTextStyle.current.copy(fontSize = Font12SP),
|
||||
linkColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.52f),
|
||||
linkColor = MaterialTheme.colorScheme.lessImportantLink,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
) {
|
||||
|
||||
+12
-28
@@ -20,40 +20,24 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.note.elements
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.note.types.EditState
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.Font12SP
|
||||
import com.vitorpamplona.amethyst.ui.theme.HalfStartPadding
|
||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||
|
||||
@Composable
|
||||
fun DisplayEditStatus(editState: EditState) {
|
||||
Text(
|
||||
text =
|
||||
buildAnnotatedString {
|
||||
if (editState.showingVersion.value == editState.originalVersionId()) {
|
||||
append(stringRes(id = R.string.original))
|
||||
} else if (editState.showingVersion.value == editState.lastVersionId()) {
|
||||
append(stringRes(id = R.string.edited))
|
||||
} else {
|
||||
append(stringRes(id = R.string.edited_number, editState.versionId()))
|
||||
}
|
||||
},
|
||||
style =
|
||||
LocalTextStyle.current.copy(
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = Font12SP,
|
||||
),
|
||||
maxLines = 1,
|
||||
modifier = HalfStartPadding.clickable { editState.nextModification() },
|
||||
val label =
|
||||
if (editState.showingVersion.value == editState.originalVersionId()) {
|
||||
stringRes(id = R.string.original)
|
||||
} else if (editState.showingVersion.value == editState.lastVersionId()) {
|
||||
stringRes(id = R.string.edited)
|
||||
} else {
|
||||
stringRes(id = R.string.edited_number, editState.versionId())
|
||||
}
|
||||
|
||||
QuietMark(
|
||||
text = label,
|
||||
onClick = { editState.nextModification() },
|
||||
)
|
||||
}
|
||||
|
||||
+2
-1
@@ -40,6 +40,7 @@ import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.theme.Font12SP
|
||||
import com.vitorpamplona.amethyst.ui.theme.lessImportantLink
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.firstIsTaggedHashes
|
||||
import com.vitorpamplona.quartz.nip22Comments.CommentEvent
|
||||
import com.vitorpamplona.quartz.nip73ExternalIds.topics.HashtagId
|
||||
@@ -85,7 +86,7 @@ private fun DisplayTagList(
|
||||
ClickableTextColor(
|
||||
"#$firstTag",
|
||||
style = LocalTextStyle.current.copy(fontSize = Font12SP),
|
||||
linkColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.52f),
|
||||
linkColor = MaterialTheme.colorScheme.lessImportantLink,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
) {
|
||||
|
||||
@@ -32,7 +32,6 @@ import com.vitorpamplona.amethyst.ui.note.LoadOts
|
||||
import com.vitorpamplona.amethyst.ui.note.timeAgoNoDot
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.HalfStartPadding
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
|
||||
@@ -63,7 +62,6 @@ fun DisplayOts(
|
||||
HeaderPill(
|
||||
symbol = MaterialSymbols.CheckCircle,
|
||||
text = stringRes(R.string.existed_since, timeStr),
|
||||
modifier = HalfStartPadding,
|
||||
contentDescription = stringRes(R.string.ots_info_title),
|
||||
onClick = {
|
||||
accountViewModel.toastManager.toast(
|
||||
@@ -78,7 +76,6 @@ fun DisplayOts(
|
||||
HeaderPill(
|
||||
symbol = MaterialSymbols.Schedule,
|
||||
text = stringRes(id = R.string.timestamp_pending_short),
|
||||
modifier = HalfStartPadding,
|
||||
contentDescription = stringRes(R.string.ots_info_title),
|
||||
)
|
||||
},
|
||||
|
||||
+11
-34
@@ -20,7 +20,6 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.note.elements
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.IntrinsicSize
|
||||
@@ -55,20 +54,16 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.ClickableTextColor
|
||||
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteReplies
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
||||
import com.vitorpamplona.amethyst.ui.note.ZapIcon
|
||||
import com.vitorpamplona.amethyst.ui.note.ZappedIcon
|
||||
import com.vitorpamplona.amethyst.ui.note.buttons.CloseButton
|
||||
import com.vitorpamplona.amethyst.ui.note.buttons.PostButton
|
||||
import com.vitorpamplona.amethyst.ui.note.showAmount
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size20Modifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange
|
||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -83,28 +78,13 @@ fun DisplayReward(
|
||||
baseReward: Reward,
|
||||
baseNote: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
var popupExpanded by remember { mutableStateOf(false) }
|
||||
|
||||
Column {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.clickable { popupExpanded = true },
|
||||
) {
|
||||
ClickableTextColor(
|
||||
"#bounty",
|
||||
linkColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.52f),
|
||||
) {
|
||||
nav.nav(Route.Hashtag("bounty"))
|
||||
}
|
||||
RenderPledgeAmount(baseNote, baseReward, accountViewModel) { popupExpanded = true }
|
||||
|
||||
RenderPledgeAmount(baseNote, baseReward, accountViewModel)
|
||||
}
|
||||
|
||||
if (popupExpanded) {
|
||||
AddBountyAmountDialog(baseNote, accountViewModel) { popupExpanded = false }
|
||||
}
|
||||
if (popupExpanded) {
|
||||
AddBountyAmountDialog(baseNote, accountViewModel) { popupExpanded = false }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,6 +93,7 @@ private fun RenderPledgeAmount(
|
||||
baseNote: Note,
|
||||
baseReward: Reward,
|
||||
accountViewModel: AccountViewModel,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
val repliesState by observeNoteReplies(baseNote, accountViewModel)
|
||||
var reward by remember {
|
||||
@@ -142,16 +123,12 @@ private fun RenderPledgeAmount(
|
||||
}
|
||||
}
|
||||
|
||||
if (hasPledge) {
|
||||
ZappedIcon(modifier = Size20Modifier)
|
||||
} else {
|
||||
ZapIcon(modifier = Size20Modifier, MaterialTheme.colorScheme.placeholderText)
|
||||
}
|
||||
|
||||
Text(
|
||||
HeaderPill(
|
||||
symbol = MaterialSymbols.Bolt,
|
||||
text = reward,
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
maxLines = 1,
|
||||
contentDescription = stringRes(R.string.bounty_label),
|
||||
iconTint = if (hasPledge) BitcoinOrange else null,
|
||||
onClick = onClick,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ import com.vitorpamplona.amethyst.ui.note.LoadAddressableNote
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.Font14SP
|
||||
import com.vitorpamplona.amethyst.ui.theme.lessImportantLink
|
||||
import com.vitorpamplona.quartz.experimental.forks.IForkableEvent
|
||||
|
||||
@Composable
|
||||
@@ -85,7 +86,7 @@ fun ForkInformationRowLightColor(
|
||||
clickablePart = stringRes(id = R.string.forked_from) + " " + (userState?.info?.bestName() ?: author.pubkeyDisplayHex()),
|
||||
maxLines = 1,
|
||||
route = route,
|
||||
overrideColor = MaterialTheme.colorScheme.primary,
|
||||
overrideColor = MaterialTheme.colorScheme.lessImportantLink,
|
||||
fontSize = Font14SP,
|
||||
nav = nav,
|
||||
tags = userState?.tags,
|
||||
|
||||
@@ -32,6 +32,7 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
|
||||
@@ -49,6 +50,7 @@ fun HeaderPill(
|
||||
text: String,
|
||||
modifier: Modifier = Modifier,
|
||||
contentDescription: String? = null,
|
||||
iconTint: Color? = null,
|
||||
onClick: (() -> Unit)? = null,
|
||||
) {
|
||||
Surface(
|
||||
@@ -64,7 +66,7 @@ fun HeaderPill(
|
||||
Icon(
|
||||
symbol = symbol,
|
||||
contentDescription = contentDescription,
|
||||
tint = MaterialTheme.colorScheme.onSecondaryContainer,
|
||||
tint = iconTint ?: MaterialTheme.colorScheme.onSecondaryContainer,
|
||||
modifier = Modifier.size(11.dp),
|
||||
)
|
||||
Text(
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.note.elements
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
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.MaterialSymbol
|
||||
import com.vitorpamplona.amethyst.ui.theme.Font12SP
|
||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||
|
||||
/**
|
||||
* Quiet passive-state marker for note headers: Boosted, Draft, Edited, pinned,
|
||||
* private rumor, ... One spec for all of them so the header reads as a single
|
||||
* system: 12sp medium gray text with an optional 12dp icon. Contrast with
|
||||
* [HeaderPill], which is the loud tier for tappable/verifiable metadata.
|
||||
*
|
||||
* Spacing between markers is owned by the parent row (`Arrangement.spacedBy`),
|
||||
* not baked in here.
|
||||
*/
|
||||
@Composable
|
||||
fun QuietMark(
|
||||
text: String? = null,
|
||||
symbol: MaterialSymbol? = null,
|
||||
contentDescription: String? = null,
|
||||
modifier: Modifier = Modifier,
|
||||
onClick: (() -> Unit)? = null,
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(3.dp),
|
||||
modifier = if (onClick != null) modifier.clickable(onClick = onClick) else modifier,
|
||||
) {
|
||||
if (symbol != null) {
|
||||
Icon(
|
||||
symbol = symbol,
|
||||
contentDescription = contentDescription,
|
||||
tint = MaterialTheme.colorScheme.placeholderText,
|
||||
modifier = Modifier.size(12.dp),
|
||||
)
|
||||
}
|
||||
if (text != null) {
|
||||
Text(
|
||||
text = text,
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
fontSize = Font12SP,
|
||||
lineHeight = Font12SP,
|
||||
fontWeight = FontWeight.Medium,
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -125,16 +125,25 @@ fun ToggleableTimeAgoText(
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TimeAgo(note: Note) {
|
||||
fun TimeAgo(
|
||||
note: Note,
|
||||
style: TimeAgoStyle = TimeAgoStyle.Dotted,
|
||||
fontSize: TextUnit = TextUnit.Unspecified,
|
||||
) {
|
||||
val time = note.createdAt() ?: return
|
||||
TimeAgo(time)
|
||||
TimeAgo(time, style, fontSize)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TimeAgo(time: Long) {
|
||||
fun TimeAgo(
|
||||
time: Long,
|
||||
style: TimeAgoStyle = TimeAgoStyle.Dotted,
|
||||
fontSize: TextUnit = TextUnit.Unspecified,
|
||||
) {
|
||||
ToggleableTimeAgoText(
|
||||
timestamp = time,
|
||||
style = TimeAgoStyle.Dotted,
|
||||
style = style,
|
||||
fontSize = fontSize,
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
}
|
||||
|
||||
+12
-4
@@ -127,6 +127,7 @@ import com.vitorpamplona.amethyst.ui.note.elements.MoreOptionsButton
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.Reward
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.ShowForkInformation
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.TimeAgo
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.TimeAgoStyle
|
||||
import com.vitorpamplona.amethyst.ui.note.observeEdits
|
||||
import com.vitorpamplona.amethyst.ui.note.showAmount
|
||||
import com.vitorpamplona.amethyst.ui.note.types.AudioHeader
|
||||
@@ -231,6 +232,7 @@ import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.EditFieldBorder
|
||||
import com.vitorpamplona.amethyst.ui.theme.EditFieldTrailingIconModifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.FeedPadding
|
||||
import com.vitorpamplona.amethyst.ui.theme.Font12SP
|
||||
import com.vitorpamplona.amethyst.ui.theme.PaddingHorizontal12Modifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size20dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size40dp
|
||||
@@ -703,7 +705,10 @@ private fun FullBleedNoteCompose(
|
||||
}
|
||||
|
||||
Column(modifier = Modifier.padding(start = 10.dp)) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(Size5dp),
|
||||
) {
|
||||
if (zapSender != null) {
|
||||
UsernameDisplay(zapSender, Modifier.weight(1f), accountViewModel = accountViewModel)
|
||||
} else {
|
||||
@@ -738,12 +743,15 @@ private fun FullBleedNoteCompose(
|
||||
|
||||
Expiration(baseNote)
|
||||
|
||||
TimeAgo(note = baseNote)
|
||||
TimeAgo(note = baseNote, style = TimeAgoStyle.Short, fontSize = Font12SP)
|
||||
|
||||
MoreOptionsButton(baseNote, editState, accountViewModel, nav)
|
||||
}
|
||||
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(Size5dp),
|
||||
) {
|
||||
Column(
|
||||
Modifier.weight(1f),
|
||||
) {
|
||||
@@ -765,7 +773,7 @@ private fun FullBleedNoteCompose(
|
||||
|
||||
val baseReward = remember { noteEvent.bountyBaseReward()?.let { Reward(it) } }
|
||||
if (baseReward != null) {
|
||||
DisplayReward(baseReward, baseNote, accountViewModel, nav)
|
||||
DisplayReward(baseReward, baseNote, accountViewModel)
|
||||
}
|
||||
|
||||
val pow = remember(noteEvent) { noteEvent.strongPoWOrNull() }
|
||||
|
||||
@@ -3041,6 +3041,8 @@
|
||||
<string name="it_s_not_possible_to_react_to_a_draft_note">It\'s not possible to react a draft note</string>
|
||||
<string name="it_s_not_possible_to_zap_to_a_draft_note">It\'s not possible to zap a draft note</string>
|
||||
<string name="draft_note">Draft Note</string>
|
||||
<string name="draft">Draft</string>
|
||||
<string name="bounty_label">Bounty</string>
|
||||
|
||||
<string name="load_from_text">From Msg</string>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user