docs(P7): Phase 7 composer redesign plan (Hybrid: + menu + per-attachment view-once)
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
# Phase 7 — Composer Redesign (Hybrid)
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development. Steps use checkbox (`- [ ]`) syntax.
|
||||
|
||||
**Goal:** Reduce composer toolbar from 9 cluttered icons to 5 hierarchically-organized buttons. Move "creative activities" (Whiteboard, Watch-Together, Mini-Games) into a `+` popover. Move View-Once from global composer toggle to per-attachment flag in the upload preview.
|
||||
|
||||
**Rollback anchor:** tag `pre-phase7-composer` (set in T1).
|
||||
|
||||
---
|
||||
|
||||
## Task 1: Rollback anchor
|
||||
|
||||
- [ ] Run:
|
||||
```bash
|
||||
cd "D:\Programmieren\ChatApp-Electron\chat-app"
|
||||
git tag -a pre-phase7-composer -m "Rollback anchor before Phase 7 composer redesign"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 2: `<ComposerActionsMenu>` popover component
|
||||
|
||||
**Files:** Create `apps/desktop/src/components/ComposerActionsMenu.tsx`
|
||||
|
||||
**Shape:**
|
||||
|
||||
```tsx
|
||||
interface Props {
|
||||
anchorRef: React.RefObject<HTMLButtonElement | null>;
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
onAttachFile: () => void;
|
||||
onCreatePoll: () => void;
|
||||
onCreateWhiteboard: () => void;
|
||||
onStartWatchTogether: () => void;
|
||||
onStartGame: () => void;
|
||||
canStartGame?: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
Layout (floating panel anchored above `anchorRef`):
|
||||
|
||||
```
|
||||
┌─────────────────────────────┐
|
||||
│ 📎 Bild / Datei │
|
||||
│ 📊 Umfrage │
|
||||
├─────────────────────────────┤
|
||||
│ AKTIVITÄTEN │
|
||||
│ ✏ Whiteboard │
|
||||
│ 📺 Watch Together │
|
||||
│ 🎮 Spiel starten │
|
||||
└─────────────────────────────┘
|
||||
```
|
||||
|
||||
- Use existing icons from `apps/desktop/src/components/icons.tsx` (grep for `PaperclipIcon`/`PlusIcon`, `PollIcon`, `MonitorShareIcon`, `PlayBoxIcon`, `GameIcon`).
|
||||
- Click outside or `Esc` → `onClose`.
|
||||
- Disabled items: `opacity-50 cursor-not-allowed` + `title` hint (e.g. "Spiele nur in 1:1-Chats").
|
||||
- Each row ≥ 44px tall, `role="menu"`/`role="menuitem"`, arrow-up/down keyboard nav.
|
||||
|
||||
---
|
||||
|
||||
## Task 3: Refactor ConversationPage composer
|
||||
|
||||
**Files:** Modify `apps/desktop/src/pages/ConversationPage.tsx`
|
||||
|
||||
**Target layout:**
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────────────┐
|
||||
│ [+] [😊] [GIF] [🎤] Nachricht schreiben… [→] │
|
||||
└────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
Changes:
|
||||
1. **Remove** inline buttons for: file-attach, poll, whiteboard, watch-together, game-picker.
|
||||
2. **Add** a `+` button at position 1 with a `useRef` anchor.
|
||||
3. **State:** `const [menuOpen, setMenuOpen] = useState(false);` + render `<ComposerActionsMenu>` with the existing handlers wired (`handleCreateWhiteboard`, `handleStartWatchTogether`, `handleStartGame`, `() => setPollDialogOpen(true)`, `() => fileInputRef.current?.click()`).
|
||||
4. **Remove** the standalone View-Once toggle button (moves to T4 per-attachment).
|
||||
5. **Keep inline:** Emoji picker, GIF picker, voice mic, send arrow.
|
||||
6. **Auto-close menu** after any item action.
|
||||
7. Pass `canStartGame={conversation?.members?.length === 2}` so the dropdown reflects the DM-only constraint.
|
||||
|
||||
---
|
||||
|
||||
## Task 4: View-Once per-attachment in `AttachmentPreview`
|
||||
|
||||
**Files:**
|
||||
- Modify `apps/desktop/src/pages/ConversationPage.tsx` (`AttachmentPreview` component + the `attachments[]` state shape).
|
||||
- Modify `apps/desktop/src/hooks/useConversationMessages.ts` (`send()` signature + per-attachment handling).
|
||||
- Possibly extend the per-attachment encrypt/upload helper if it still treats `viewOnce` as a per-message flag.
|
||||
|
||||
**Behavior:**
|
||||
|
||||
Add a third hover-button on each image preview next to `✏` and `✕`: a `👁` icon that toggles `viewOnce` per attachment.
|
||||
- Active: icon switches (e.g. crossed-eye) + small `1×` badge in lower-right corner of the thumb.
|
||||
- Image-only (`file.type.startsWith('image/')`). Hidden on non-image previews.
|
||||
|
||||
**State refactor:**
|
||||
|
||||
Change `attachments: File[]` → `attachments: Array<{ file: File; viewOnce: boolean }>`. Every consumer site updated:
|
||||
- `setAttachments((prev) => [...prev, ...newOnes.map((f) => ({ file: f, viewOnce: false }))])`
|
||||
- `attachments.map((a, idx) => <AttachmentPreview file={a.file} ... onToggleViewOnce={() => setAttachments(prev => prev.map((x, i) => i === idx ? { ...x, viewOnce: !x.viewOnce } : x))} />)`
|
||||
- `setAttachments((prev) => prev.filter((_, i) => i !== idx))` — unchanged shape
|
||||
|
||||
**Send path:**
|
||||
|
||||
The `send()` currently accepts a `viewOnce` option that applies globally. Refactor so the per-attachment flag flows through:
|
||||
- Either change `send(payload, attachments, replyTo, { viewOnce })` → `send(payload, attachmentsWithFlags, replyTo)` where each entry carries its own `viewOnce`
|
||||
- OR pass a parallel `viewOnceFlags: boolean[]` array aligned with attachments
|
||||
|
||||
The encrypt/upload helper already supports per-attachment `view_once` (P2.T14 column `message_attachments.view_once`). The renderer just needs to pass the right flag per row.
|
||||
|
||||
**Grep first** to find the existing wiring: `Grep -rn "view_once\|viewOnce" apps/desktop/src/ packages/shared/src/chat/` — adapt to what's actually there.
|
||||
|
||||
---
|
||||
|
||||
## Task 5: Cleanup + Final gate
|
||||
|
||||
- [ ] `pnpm --filter @chat-app/desktop typecheck` — green
|
||||
- [ ] `pnpm --filter @chat-app/shared test -- --run` — green (71 tests)
|
||||
- [ ] `pnpm --filter @chat-app/desktop test -- --run` — green
|
||||
- [ ] `git status` — clean
|
||||
- [ ] Tag `phase7-done`
|
||||
- [ ] Report smoke-test points:
|
||||
1. Composer shows 5 inline buttons (was 9)
|
||||
2. Click `+` → popover opens; click anywhere outside or `Esc` closes it
|
||||
3. Attach image → preview shows ✏/👁/✕ on hover
|
||||
4. Toggle 👁 on attachment-1 only → recipient sees attachment-1 as view-once, attachment-2 normally
|
||||
5. Everything else unchanged (emoji, GIF, voice, send, edit-message, etc.)
|
||||
|
||||
---
|
||||
|
||||
## Non-goals
|
||||
|
||||
- No emoji-as-icon (uses existing SVG icons).
|
||||
- No slash-commands (deferred to potential Phase 7B).
|
||||
- No reordering of inline buttons beyond the spec.
|
||||
- No per-attachment poll-attach (polls remain message-level).
|
||||
Reference in New Issue
Block a user