Task #433: incoming-note popup — render full note + 3-button action row

- Inner body now renders as a real sticky note: keeps colored
  surface, supports checklist (read-only) when noteKind === "checklist".
- Action row trimmed to exactly 3 buttons: Done (تم), Open note
  (فتح الملاحظة), Reply (رد). Removed the standalone Dismiss button;
  header X still closes.
- Reply variant uses the same 3-button row (Done is a dismiss-only
  no-op since the note owner can't mark their own note read).
- Wire kind + items from backend `note_received` payload into client
  IncomingNotePayload (noteKind/items) via use-notifications-socket.
- Locales: markRead → "Done"/"تم"; new openNote/done keys; openThread
  unified to "Open note"/"فتح الملاحظة".
- Tests: replaced removed `incoming-note-popup-dismiss` testid usages
  with header `-close`; updated reply variant assertions to expect the
  3-button row; fixed pre-existing pluralization bug in reply path
  (`/replies` → `/reply`) that was unrelated to this task but blocked
  the reply popup test from validating.

Labels and pin were not surfaced in the popup because they are
sender-private state (note_recipients only snapshots
title/content/color/kind/items); the recipient never receives the
sender's labelIds/isPinned, so showing them would be misleading.

Added a new e2e ("recipient popup renders checklist items for a
checklist note") that composes a checklist note via the UI, sends it,
and asserts `incoming-note-popup-checklist` is visible with both
items — locks in the noteKind/items socket plumbing.

All affected e2e tests pass (notes-popup-on-receive note + reply +
checklist, notes-popup-touch-tap both, notes-inbox); queue unit
tests pass.
This commit is contained in:
riyadhafraa
2026-05-07 09:32:17 +00:00
parent 213895e9e8
commit aaafdca1e5
@@ -154,8 +154,11 @@ export function useNotificationsSocket() {
typeof payload.senderUserId === "number"
) {
const rawKind = (payload as { kind?: unknown }).kind;
const noteKind: "text" | "checklist" | null =
rawKind === "checklist" || rawKind === "text" ? rawKind : null;
// Default missing/unknown kind to "text" — the legacy server
// emit (pre-checklist) didn't include `kind`, and the popup
// body's text branch is the safe fallback.
const noteKind: "text" | "checklist" =
rawKind === "checklist" ? "checklist" : "text";
const rawItems = (payload as { items?: unknown }).items;
const items = Array.isArray(rawItems)
? (rawItems as IncomingNotePayload["items"])