e71a2b26ef
The earlier integration audit identified the gaps; this is the
how-to-build-them. Split across four files plus an index so each
review / commit stays small:
- 2026-04-26-tier-plans-index.md — top-level pointer + sequence
dependencies + what's deliberately out of scope.
- 2026-04-26-tier1-coding-plan.md — listener counter, presence
aggregation, augmented kind-10312 tags (publishing/onstage),
live chat (kind 1311), reactions (kind 7 / 9735), edit + close
+ scheduled rooms, role parsing + promote/demote, hand-raise
queue, kick (kind 4312). Concrete file-level wiring for each
step + suggested commit order (six independent PRs).
- 2026-04-26-tier2-coding-plan.md — participant grid,
per-avatar context menu (follow / mute / zap / promote / kick),
zap entry points (room + speaker), share-via-naddr.
- 2026-04-26-tier3-coding-plan.md — room theming PARSER ONLY
(graceful fallback for themed rooms; full theming behind a
later phase), background-audio + wake-lock audit checklist.
- 2026-04-26-tier4-coding-plan.md — moq-auth token re-mint on
long sessions and moq-lite Connection.Reload-equivalent
reconnect with backoff. Step 1 is subsumed by Step 2 once the
reconnect path is in.
No code changes — pure docs. Each plan names exact file paths,
new types, reused helpers, strings, tests, and call-out risks so
an implementer (or follow-up agent) can pick up Step N without
re-deriving the surrounding context.
4.7 KiB
4.7 KiB
Tier 3 — coding plan (room theming, background-audio audit)
Tier 3 of 2026-04-26-nostrnests-integration-audit.md. These are
larger or lower-priority items; do them after Tier 1 and Tier 2 ship.
Step 1 — Room theming PARSER ONLY (#14, partial)
The minimum-viable slice. Goal: don't render themed rooms badly. We parse the theme tags and either honour them with Compose color overrides or ignore them with a graceful fallback (current visual).
Skip the kind 36767 / 16767 Ditto-theme refs and the per-user profile theme — those are full features deferable to a later phase.
Quartz
quartz/.../nip53LiveActivities/meetingSpaces/tags/ColorTag.kt(NEW) — parser for["c", hex, "background"|"text"|"primary"].assemble(hex, target)builder.quartz/.../meetingSpaces/tags/FontTag.kt(NEW) — parser for["f", family, optionalUrl]. Builder.quartz/.../meetingSpaces/tags/BackgroundTag.kt(NEW) — parser for["bg", "url <u>", "mode tile|cover"]. Builder.quartz/.../meetingSpaces/MeetingSpaceEvent.kt— accessors:colors(): List<ColorTag>,font(): FontTag?,background(): BackgroundTag?.quartz/.../meetingSpaces/TagArrayBuilderExt.kt—colors,font,backgroundDSL functions.
Commons / Amethyst
commons/.../viewmodels/AudioRoomViewModel.kt— exposeroomTheme: RoomTheme?derived from the event.commons/.../viewmodels/RoomTheme.kt(NEW):data class RoomTheme( val background: Color?, val text: Color?, val primary: Color?, val backgroundImageUrl: String?, val backgroundMode: BgMode = BgMode.COVER, ) enum class BgMode { TILE, COVER }amethyst/.../audiorooms/room/AudioRoomThemedScope.kt(NEW) — Composable wrapper that takes aRoomTheme?and overrides Material3colorSchemefor its content. If null → no override. WrapAudioRoomFullScreenbody in this.- Background image: render via Coil
AsyncImagebehind everything, honouringbackgroundMode(cover viaContentScale.Crop, tile via customModifier.repeatedBackground).
Skip / defer
- Font loading (
["f", family, url]) — would need aFontFamilyloader. Not user-blocking for parity. - Kind 36767 (Ditto themes) and kind 16767 (per-user profile themes). Mark as out-of-scope for now.
Strings
None — pure visual.
Tests
quartz/.../ColorTagTest.kt— round-trip + invalid hex / target.commons/.../AudioRoomViewModelTest.kt—roomThemepopulates from a fakeMeetingSpaceEventwithc/bgtags.
Risk
- Some rooms use unusual hex shapes (
#abc, named colors). Keep the parser strict (^#?[0-9a-fA-F]{6}$) and fall back on parse failure rather than crashing.
Step 2 — Background audio + wake-lock audit (#15)
Audit checklist
For each item, look in the named file and either confirm the behaviour or open a follow-up bullet:
PARTIAL_WAKE_LOCKduring a broadcast. File:amethyst/src/main/java/com/vitorpamplona/amethyst/service/audiorooms/AudioRoomForegroundService.kt. Check: isPowerManager.newWakeLock(PARTIAL_WAKE_LOCK, "...")acquired inpromoteToMicrophone(...)and released instop(...)? If not, add it.- Foreground service type
microphonefor Android 14+. Same file. Confirm the manifest declaresandroid:foregroundServiceType="microphone"and the service call usesServiceCompat.startForeground(..., FOREGROUND_SERVICE_TYPE_MICROPHONE). - Listener-only foreground type when not broadcasting.
AudioRoomForegroundService.startListening(context)should useFOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK(NOT microphone — Play Store rejects the elevated permission for listen-only users). - Audio focus. AudioTrack alone doesn't request focus; verify
AudioManager.requestAudioFocus(AudioFocusRequest.Builder(...))is called inAudioTrackPlayer.start()and abandoned instop(). If not, add it (so a phone call ducks the room). - PIP keep-alive. Confirm
AudioRoomActivitysurvives the transition to PIP without dropping the foreground service — should already be the case (commit history shows this was audited previously).
Output
A short audit notes file under nestsClient/plans/ if any item
turns up missing — otherwise mark this step done.
Suggested commit order
- theme parser (Quartz: tags + accessors + tests) — small, safe
- theme renderer (Compose: themed scope + background image)
- background-audio audit notes (and any fixes that surface)
Out of scope for Tier 3
- Kind 36767 (Ditto theme reference) and kind 16767 (per-user profile theme) — these are full sub-features behind the basic theming. Defer to a separate phase if needed.
- Per-user font loading — defer.