mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-11 16:57:39 +00:00
fix(chat): timestamp tap opens delivery (not format toggle); relays for Concord
The chat timestamp tap toggled relative/absolute time instead of opening the relay dialog, because ToggleableTimeAgoText has its own inner click. Give it a `toggleable` flag and turn it off for the chat time, so the enclosing tap target opens the delivery dialog; the absolute time now shows as a header inside that dialog so nothing is lost. Also: - Encrypted-channel messages (Concord/Armada) decrypt locally with no per-relay attribution, so the dialog showed "no relay information". Fall back to the channel's own relays as "where this message lives". - Move the "Message Delivery" sheet tile out of the always-visible row into the Show More section — it's a secondary action. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0129yvP2hmVeDFfuKKy94tqX
This commit is contained in:
@@ -89,6 +89,9 @@ fun ToggleableTimeAgoText(
|
||||
fontSize: TextUnit = TextUnit.Unspecified,
|
||||
maxLines: Int = 1,
|
||||
overflow: TextOverflow = TextOverflow.Clip,
|
||||
// When false, the text is not itself clickable, so an enclosing tap target (e.g. the
|
||||
// chat timestamp opening the relay/delivery dialog) receives the tap instead.
|
||||
toggleable: Boolean = true,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val nowState = LocalNowSeconds.current
|
||||
@@ -125,10 +128,14 @@ fun ToggleableTimeAgoText(
|
||||
maxLines = maxLines,
|
||||
overflow = overflow,
|
||||
modifier =
|
||||
modifier.clickable(
|
||||
interactionSource = interactionSource,
|
||||
indication = null,
|
||||
) { showAbsolute = !showAbsolute },
|
||||
if (toggleable) {
|
||||
modifier.clickable(
|
||||
interactionSource = interactionSource,
|
||||
indication = null,
|
||||
) { showAbsolute = !showAbsolute }
|
||||
} else {
|
||||
modifier
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+25
@@ -41,12 +41,14 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
|
||||
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbol
|
||||
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
|
||||
import com.vitorpamplona.amethyst.commons.model.Channel
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.service.relayClient.chatDelivery.ChatDelivery
|
||||
import com.vitorpamplona.amethyst.service.relayClient.chatDelivery.RecipientDelivery
|
||||
@@ -54,6 +56,7 @@ import com.vitorpamplona.amethyst.ui.components.ClickableBox
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.note.UserPicture
|
||||
import com.vitorpamplona.amethyst.ui.note.UsernameDisplay
|
||||
import com.vitorpamplona.amethyst.ui.note.timeAbsolute
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.LoadUser
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
@@ -175,6 +178,23 @@ private fun ChatDeliveryDetailDialog(
|
||||
modifier = Modifier.verticalScroll(rememberScrollState()),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
// Absolute timestamp header (the chat time no longer toggles to absolute
|
||||
// on tap — it opens this dialog instead).
|
||||
val context = LocalContext.current
|
||||
Text(
|
||||
text = timeAbsolute(baseNote.createdAt(), context, prefix = "").trim(),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
fontSize = Font12SP,
|
||||
)
|
||||
|
||||
// Encrypted-channel messages (Concord/Armada) are decrypted locally with no
|
||||
// per-relay attribution, so seen-on is empty — fall back to the channel's own
|
||||
// relays as "where this message lives".
|
||||
val channelRelays =
|
||||
remember(baseNote) {
|
||||
baseNote.inGatherers?.firstNotNullOfOrNull { (it as? Channel)?.relays()?.takeIf { r -> r.isNotEmpty() } } ?: emptySet()
|
||||
}
|
||||
|
||||
val recipients = delivery?.recipients
|
||||
when {
|
||||
!recipients.isNullOrEmpty() ->
|
||||
@@ -196,6 +216,11 @@ private fun ChatDeliveryDetailDialog(
|
||||
RelayDeliveryRow(relay = relay, accepted = true)
|
||||
}
|
||||
|
||||
channelRelays.isNotEmpty() ->
|
||||
channelRelays.sortedBy { it.url }.forEach { relay ->
|
||||
RelayDeliveryRow(relay = relay, accepted = true)
|
||||
}
|
||||
|
||||
else ->
|
||||
// Nothing to list: our own just-sent message no relay has acknowledged
|
||||
// yet (pending), or a received/old message with no recorded relays.
|
||||
|
||||
+13
-8
@@ -261,7 +261,7 @@ fun ChatMessageActionSheet(
|
||||
}
|
||||
|
||||
// Stage one: the primary chat action (reply / edit draft) is always shown.
|
||||
ChatOnlyRow(note, state, onWantsToReply, onWantsToEditDraft, { showDeliveryDialog = true }, onDismiss)
|
||||
ChatOnlyRow(note, state, onWantsToReply, onWantsToEditDraft, onDismiss)
|
||||
|
||||
val handlers =
|
||||
NoteActionHandlers(
|
||||
@@ -312,6 +312,17 @@ fun ChatMessageActionSheet(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Chat-specific: which relays carry this message (and, for our own,
|
||||
// their acceptance). A secondary action, so it lives under Show More.
|
||||
if (!note.isDraft()) {
|
||||
SectionDivider()
|
||||
TileRow {
|
||||
ActionTile(MaterialSymbols.DoneAll, stringRes(R.string.chat_delivery_details_title)) {
|
||||
showDeliveryDialog = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -357,7 +368,7 @@ private fun MoreActionsToggle(
|
||||
|
||||
// ---------- Action tile sections ----------
|
||||
|
||||
/** Chat-specific tiles (reply, message delivery, edit draft) that have no 3-dot menu equivalent. */
|
||||
/** Chat-specific tiles (reply, edit draft) that have no 3-dot menu equivalent. */
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
private fun ChatOnlyRow(
|
||||
@@ -365,7 +376,6 @@ private fun ChatOnlyRow(
|
||||
state: DropDownParams,
|
||||
onWantsToReply: (Note) -> Unit,
|
||||
onWantsToEditDraft: (Note) -> Unit,
|
||||
onShowDelivery: () -> Unit,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
if (note.isDraft() && !state.isLoggedUser) return
|
||||
@@ -384,11 +394,6 @@ private fun ChatOnlyRow(
|
||||
onWantsToReply(note)
|
||||
onDismiss()
|
||||
}
|
||||
// Which relays carry this message (and, for our own, their acceptance) — the
|
||||
// reliable way to reach the relay list now that the detail row is gone.
|
||||
ActionTile(MaterialSymbols.DoneAll, stringRes(R.string.chat_delivery_details_title)) {
|
||||
onShowDelivery()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
@@ -59,6 +59,10 @@ fun ChatTimeAgo(baseNote: Note) {
|
||||
style = TimeAgoStyle.Short,
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
fontSize = Font12SP,
|
||||
// The chat time is wrapped in a tap target that opens the relay/delivery dialog,
|
||||
// so it must not steal the tap to toggle relative/absolute. The absolute time is
|
||||
// shown in that dialog instead.
|
||||
toggleable = false,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user