Composer refinement + unification: center modal, NLDD alignment, rename card-less to new, unify with reply composer #195

Closed
opened 2026-07-26 07:03:48 +00:00 by robbertbos · 1 comment
Owner

Context

PR #192 (#192) landed the
new-message composer (compose a message to a channel or person, independent of a
saved card). This issue tracks the refinement + unification round that came out
of the review and local testing. To be done as one coherent effort (in #192 or a
dedicated refactor PR).

Scope

  1. Center the modal like the card modal. ComposeOverlay uses a native
    <dialog> that renders top-aligned; CardDetailsModal is a centered custom
    overlay (.overlay{position:fixed;inset:0;flex-center} + .modal). Mirror
    that pattern (optionally a shared Modal.vue), with useFocusTrap.
  2. NLDD alignment, less custom CSS. Server select -> nldd-dropdown; the
    "Aan" field -> nldd-text-field/nldd-search-field (fixes the search-icon
    overlapping the placeholder); buttons -> nldd-button; labels ->
    nldd-form-field. Only the combobox popup stays custom (NLDD has no combobox).
  3. Restore the formatting toolbar. The compose editor currently reuses only
    the bare MarkdownEditor; reuse the full reply editor stack
    (FormatToolbar/LinkBar/EmojiPicker).
  4. Rename + dedup (uniformity over standalone functions). Rename
    card-less/cardless to new(-message) across frontend AND backend
    (CardlessMessageCreate/CardlessScheduledCreate/create_scheduled_cardless
    /useCreateCardlessScheduled/the cardless prop/isCardless), and make the
    reply path explicitly "reply". Single MAX_FILE_REFS (backend, in every
    Field(max_length=...)) + MAX_ATTACHMENTS (frontend) constant - the cap is
    currently hardcoded in ~8 places. Decision: not a build-time Vite var (it does
    not reach the backend) nor /api/config (dev-only); two named constants that
    reference each other, or a small prod /api/app-config if a true single source
    is wanted. Extract a shared AttachmentList component (currently a 2nd copy,
    missing image thumbnails). Add a useMessageComposer core unifying
    ReplyComposer + ComposeOverlay (content + files + validation +
    send/schedule/undo, strategy injected). This touches the merged, production
    reply path -> highest risk; do carefully and run the backend gates.
  5. Dev Mattermost mock (commit with tests).
    backend/waggle/integrations/mattermost_dev.py + a dev_synthetic_auth gate
    in deps.get_mm_client_for_card make the composer testable locally without a
    real Mattermost (fake channels/people/sends). It is currently uncommitted
    (used only for the local preview); commit it WITH tests so the 100% backend
    coverage gate stays green. Optionally also gate
    scheduled_runner._build_mm_client_for_row for scheduled sends in dev.

Smaller separable follow-ups

  • A "?" shortcut-help table.
  • Editing a scheduled new-message row in place (the row carries no account_id
    yet -> consider adding it to ScheduledPostRead).
  • Playwright e2e for the composer.

Decisions carried in

  • Undo = pre-send delay toast (lib/delayedSend), not a post-send delete.
  • Uniformity over standalone functions.
## Context PR #192 (https://code.overheid.nl/robbertbos/waggle/pulls/192) landed the new-message composer (compose a message to a channel or person, independent of a saved card). This issue tracks the refinement + unification round that came out of the review and local testing. To be done as one coherent effort (in #192 or a dedicated refactor PR). ## Scope 1. **Center the modal** like the card modal. `ComposeOverlay` uses a native `<dialog>` that renders top-aligned; `CardDetailsModal` is a centered custom overlay (`.overlay{position:fixed;inset:0;flex-center}` + `.modal`). Mirror that pattern (optionally a shared `Modal.vue`), with `useFocusTrap`. 2. **NLDD alignment, less custom CSS.** Server select -> `nldd-dropdown`; the "Aan" field -> `nldd-text-field`/`nldd-search-field` (fixes the search-icon overlapping the placeholder); buttons -> `nldd-button`; labels -> `nldd-form-field`. Only the combobox popup stays custom (NLDD has no combobox). 3. **Restore the formatting toolbar.** The compose editor currently reuses only the bare `MarkdownEditor`; reuse the full reply editor stack (`FormatToolbar`/`LinkBar`/`EmojiPicker`). 4. **Rename + dedup (uniformity over standalone functions).** Rename `card-less`/`cardless` to `new`(-message) across frontend AND backend (`CardlessMessageCreate`/`CardlessScheduledCreate`/`create_scheduled_cardless` /`useCreateCardlessScheduled`/the `cardless` prop/`isCardless`), and make the reply path explicitly "reply". Single `MAX_FILE_REFS` (backend, in every `Field(max_length=...)`) + `MAX_ATTACHMENTS` (frontend) constant - the cap is currently hardcoded in ~8 places. Decision: not a build-time Vite var (it does not reach the backend) nor `/api/config` (dev-only); two named constants that reference each other, or a small prod `/api/app-config` if a true single source is wanted. Extract a shared `AttachmentList` component (currently a 2nd copy, missing image thumbnails). Add a `useMessageComposer` core unifying `ReplyComposer` + `ComposeOverlay` (content + files + validation + send/schedule/undo, strategy injected). This touches the merged, production reply path -> highest risk; do carefully and run the backend gates. 5. **Dev Mattermost mock (commit with tests).** `backend/waggle/integrations/mattermost_dev.py` + a `dev_synthetic_auth` gate in `deps.get_mm_client_for_card` make the composer testable locally without a real Mattermost (fake channels/people/sends). It is currently uncommitted (used only for the local preview); commit it WITH tests so the 100% backend coverage gate stays green. Optionally also gate `scheduled_runner._build_mm_client_for_row` for scheduled sends in dev. ## Smaller separable follow-ups - A "?" shortcut-help table. - Editing a scheduled new-message row in place (the row carries no `account_id` yet -> consider adding it to `ScheduledPostRead`). - Playwright e2e for the composer. ## Decisions carried in - Undo = pre-send delay toast (`lib/delayedSend`), not a post-send delete. - Uniformity over standalone functions.
Author
Owner

Done: the full scope landed on main via #192.

  1. Shared components/Modal.vue (focus-trap + scroll-lock + Escape), used by ComposeOverlay and the other modals.
  2. nldd-dropdown / nldd-form-field in the overlay; TargetPicker runs on nldd-combo-box + nldd-menu + nldd-icon-button.
  3. Shared MessageEditor.vue (editor + toolbar + linkbar + emoji + mentions), used by ComposeOverlay and ReplyComposer alike.
  4. No occurrences of "cardless" left in backend/waggle or frontend/src; MAX_FILE_REFS = 10 in config.py is the single source (imported by schemas/outgoing_post.py); AttachmentChipList.vue + attachmentLimits.ts; composables/useMessageComposer.ts.
  5. integrations/mattermost_dev.py with tests/unit/test_mattermost_dev.py.

Of the separable follow-ups, the ? shortcut table (ShortcutHelp.vue) and account_id on ScheduledPostRead are done too. Still open: a Playwright e2e for the composer - unit coverage exists (ComposeOverlay/TargetPicker/MessageEditor) and composer-link-formatting/mention-autocomplete touch the editor, but no spec walks target -> send/schedule end to end. Not tracked anywhere else after this close.

Done: the full scope landed on main via #192. 1. Shared `components/Modal.vue` (focus-trap + scroll-lock + Escape), used by ComposeOverlay and the other modals. 2. `nldd-dropdown` / `nldd-form-field` in the overlay; TargetPicker runs on `nldd-combo-box` + `nldd-menu` + `nldd-icon-button`. 3. Shared `MessageEditor.vue` (editor + toolbar + linkbar + emoji + mentions), used by ComposeOverlay and ReplyComposer alike. 4. No occurrences of "cardless" left in backend/waggle or frontend/src; `MAX_FILE_REFS = 10` in `config.py` is the single source (imported by `schemas/outgoing_post.py`); `AttachmentChipList.vue` + `attachmentLimits.ts`; `composables/useMessageComposer.ts`. 5. `integrations/mattermost_dev.py` with `tests/unit/test_mattermost_dev.py`. Of the separable follow-ups, the `?` shortcut table (`ShortcutHelp.vue`) and `account_id` on `ScheduledPostRead` are done too. Still open: a Playwright e2e for the composer - unit coverage exists (ComposeOverlay/TargetPicker/MessageEditor) and `composer-link-formatting`/`mention-autocomplete` touch the editor, but no spec walks target -> send/schedule end to end. Not tracked anywhere else after this close.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
robbertbos/waggle#195
No description provided.