mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-11 16:57:39 +00:00
nests room: stable reactions overlay + repeatable kind-7 + speaker controls
Reaction-side rework after extended testing on 2026-05-13:
* Promoted users weren't seeing speaker controls in their own
Amethyst — `StageControlsBar` gates on `isOnStage && ui.onStageNow`
and `ui.onStageNow` was stuck at false (never reset back to true
after some earlier path flipped it). Added a LaunchedEffect in
NestActivityContent that mirrors `isOnStageMe → ui.onStageNow`
whenever it becomes true, symmetric to the auto-stop effect.
* Reactions overlay sat inside AvatarAndBadges' wrap-content Box,
so adding/removing the chip shifted the inner Box's centre and
the role badges drifted. Lifted the overlay out to be a sibling
of AvatarAndBadges inside the outer fixed-size Box; badges now
stay anchored regardless of chip presence or animation state.
* Reaction grouping was by `targetPubkey` (NIP-25 p-tag) — emojis
showed on the speaker being reacted to, not the reactor. Switched
RoomReactionsAggregator to group by `sourcePubkey`. AudienceGrid
threads `reactionsByPubkey` so an audience reactor sees their own
emoji float from their audience-tab avatar too.
* Reaction chip animation: progress was an `Animatable.value` that
Compose wasn't refreshing in the layout-consuming layer (visible
bug: chip "blinked" with no movement). Replaced with a manual
`withFrameNanos` loop writing to a `MutableFloatState` read inside
a `graphicsLayer { … }` lambda — frame-clock animation that works.
Each kind-7 is its own chip keyed by event-id (no more
groupBy-content collapsing same-emoji bursts into one shared chip
that restarts on every arrival). Chips stack at a fixed-size 30 dp
Box with the emoji centred, so the X position is invariant under
glyph width. Multiple concurrent chips overlap at the right-bottom
corner in a `Box(BottomEnd)` (newest on top) rather than sliding
leftward in a `Row`.
* Bottom-drawer `RoomReactionPickerSheet` (hard-coded 6 emojis, no
NIP-30) replaced by a forked `RoomReactionPopup` that reuses
`ReactionChoicePopupContent` (same NIP-30 custom-emoji support,
same user-configured reaction set) but with two semantic deviations
for live audio rooms: empty `toRemove` so all buttons stay
"fresh", and the click handler signs+broadcasts a fresh kind-7
template directly instead of going through `Account.reactTo` —
which delegates to `ReactionAction.reactTo(note, …)` and
short-circuits on `note.hasReacted(by, reaction)`. The bypass
lets the user fire the same emoji repeatedly during a moment.
* Chip rendering matches NoteCompose: `RenderReactionContent`
handles `:shortcode:url` via `InLineIconRenderer` + `CustomEmoji`,
`"+"`→❤️, `"-"`→👎, anything else as Text.
Tests updated for sender-grouped aggregator semantics.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
d21eb0e226
commit
78be534df5
+12
-4
@@ -78,9 +78,6 @@ class RoomReactionsAggregator {
|
||||
// event id so the same kind-7 from two relays only counts once.
|
||||
private val byEventId = LinkedHashMap<String, RoomReaction>()
|
||||
|
||||
/** Stable key for room-wide reactions in the returned map. */
|
||||
private val roomWideKey = ""
|
||||
|
||||
/** Apply one reaction and return the post-evict snapshot. */
|
||||
fun apply(
|
||||
event: ReactionEvent,
|
||||
@@ -102,13 +99,24 @@ class RoomReactionsAggregator {
|
||||
* cadence (typically every second so the floating-up animation
|
||||
* frame rate is set by the eviction tick rather than by a
|
||||
* per-Composable timer).
|
||||
*
|
||||
* Reactions are grouped by [RoomReaction.sourcePubkey] — the
|
||||
* user who SENT the reaction. The chip floats up from the
|
||||
* reactor's own avatar (matching nostrnests' UX) rather than
|
||||
* from the target speaker's avatar (which would surface the
|
||||
* NIP-25 `p`-tag's "originalAuthor" semantics — useful for
|
||||
* comment threads but confusing in a live audio room, where the
|
||||
* audience expects to see who's reacting, not who's being
|
||||
* reacted to). Room-wide reactions (no `p`-tag at all) still
|
||||
* land under their sender's key, so they show up on the
|
||||
* reactor's avatar just like targeted ones.
|
||||
*/
|
||||
fun evictAndSnapshot(olderThanSec: Long): Map<String, List<RoomReaction>> {
|
||||
val it = byEventId.entries.iterator()
|
||||
while (it.hasNext()) {
|
||||
if (it.next().value.createdAtSec < olderThanSec) it.remove()
|
||||
}
|
||||
return byEventId.values.groupBy { it.targetPubkey ?: roomWideKey }
|
||||
return byEventId.values.groupBy { it.sourcePubkey }
|
||||
}
|
||||
|
||||
/** Whether the aggregator currently holds any unevicted reactions. */
|
||||
|
||||
Reference in New Issue
Block a user