Files
ChatApp/docs/superpowers/plans/2026-05-17-phase7-composer-redesign.md

139 lines
6.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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).