Every screen of the Communication module, its intent, route, composition, states,
permissions, platform behavior and events. Authoritative components in 00-shared/03 ;
this file enumerates which ones each screen uses with module specifics.
Format mirrors design-docs/auth/05_Screen_Inventory.md.
States = idle / loading / success / empty / error(offline, network) / disabled / permission.
Analytics events follow {module}.{screen}.{action} (proposed; SDK open — 00-shared/10 §8 ).
Permissions: controller is guarded by JwtAuthGuard only
(announcements.controller.ts:11,18) — no communication.* RBAC perms exist yet
(absent from permissions.constants.ts); role gates below are design intent (planned).
Field Detail
Purpose Consume school announcements; primary recipient surface
Entry dashboard Communication hub, app open, push tap (forward-looking)
Exit detail sheet; compose (authors); /announcements/mine
Source GET /announcements (+ optional ?audience=) → sort: { createdAt: -1 } (announcement.service.ts:56-60)
Tabs "For me" (client-filtered: targetUserIds contains me, or audience.type === 'all') / "All" — client-side only (forward-looking), backend does not filter by user (announcement.service.ts:56-60)
Row widgets AppCard announcement tile: title, body excerpt, audience chip, status badge (draft/published), unread dot, date
Pull-to-refresh RefreshIndicator re-fetches list
Empty "No announcements" + CTA "Create one" (authors)
States loading skeleton; error offline banner + retry; permission (401 → login)
Analytics comm.announcements.{open,refresh,tab_switch} (proposed)
a11y unread dot exposed via semantics label "Unread"; chips readable by TalkBack
Motion list fade m-base; new item slide-in m-base (motion tokens 00-shared/08 )
Field Detail
Purpose Full body, audience line, attachments, mark-as-read action
Entry feed row tap, search result, push deep link (forward-looking)
Exit back; attachments via external browser
Source Gap: no GET /announcements/:id route (announcements.controller.ts has only create/list/publish/read/reads). Use the list payload + receipt action (forward-looking)
Actions POST /announcements/:id/read — idempotent $addToSet (announcement.repository.ts:20-33); auto-fire on open for recipients (design choice, (proposed))
Audience line "To: Grade 10 · 120 recipients" from audience.type/value + targetUserIds.length
Status badge draft / published + publishedAt local-formatted
Empty not applicable (not-found → snackbar + pop)
a11y body as single readable block; read-state announced via live region
Analytics comm.announcement.detail.{open,mark_read,attachment_open} (proposed)
Field Detail
Purpose Create draft; audience targeting; publish in one flow
Entry feed FAB, /announcements/mine FAB, empty-state CTA
Exit save draft → success → feed/mine; publish → success → feed
Source POST /announcements (draft, published: false, createdBy from context — announcement.service.ts:31-37) then optional POST /announcements/:id/publish
Form title, body, audience picker (type + value), attachments (planned: upload), priority/expiry (planned — no fields in schema) — see 08_Form_Specifications.md
Audience warning "0 recipients will receive this" when resolution yields [] (role/grade/section typos) (planned) — backend resolves silently (announcement.service.ts:109-148)
States validation inline; draft saved snackbar; publish spinner
Analytics comm.announcement.compose.{save_draft,publish,audience_change} (proposed)
Keyboard title .text, body multiline; toolbar above keyboard
a11y labels linked; audience picker as radio group
Field Detail
Purpose Author's own drafts + published list
Source same GET /announcements; client filters createdBy == me — backend has no "mine" query (forward-looking)
Row announcement tile + status badge (draft / published) + reads count
Empty "No announcements yet" + FAB compose
Analytics comm.announcements.mine.{open,status_filter} (proposed)
Field Detail
Purpose Who read / who hasn't
Source GET /announcements/:id/reads → readBy[] (announcement.service.ts:104-107); read count + targetUserIds.length from list payload
Rows AppListTile: name (user lookup), readAt; segmented "Read / Unread" (unread = targetUserIds − readBy)
States loading; empty (nobody read yet)
Analytics comm.announcement.reads.{open,segmented_switch} (proposed)
Note export / laggard reminders (planned)
Field Detail
Source GET /threads (threads.controller.ts)
Row AppListTile conversation: partner avatar, last message, unread badge
Empty "No conversations" + FAB new thread
Analytics comm.threads.{open,search} (proposed)
Field Detail
Source GET /threads/:id, send POST /messages, mark read PATCH /threads/:id/read (IMPLEMENTATION_PLAN.md:107-112)
Realtime WebSocket via existing WsModule (planned) — IMPLEMENTATION_PLAN.md:119
States offline → outbox queue (forward-looking)
Analytics comm.thread.{open,send,read} (proposed)
Recipient picker (user search), body field; creates via POST /threads + POST /messages.
Full spec deferred — threads are secondary in this package.
AppCard, AppListTile, AppButton, AppTextField, AppSnackbar, AppChips,
AppBadge, AppBottomSheet, AppDialog, AppMenu, AppSkeleton, AppEmptyState,
AppOfflineBanner, AppFAB, AppAvatar, AppSegmentedControl. Module-specific:
AnnouncementCard, AudienceChip, AnnouncementStatusBadge, ReadReceiptRow,
AudiencePicker — defined in 07_Component_Library.md .
comm.announcements.*, comm.announcement.{detail,compose,reads}.*,
comm.threads.*, comm.thread.* — all proposed (SDK open, 00-shared/10 ).
Feed/detail: portrait-first; tablet → two-column list/detail; desktop hover highlights.
Composer: full keyboard-avoidance + resizeToAvoidBottomInset; tablet centers card
≤ 640 dp.
Receipts: tablet shows segmented read/unread side-by-side.