Skip to content

feat(materialize): the Create-from preview / edit / confirm window (#15) - #114

Merged
Adron merged 1 commit into
parity/queuefrom
issue/15-materialize-preview-window
Sep 16, 2026
Merged

Adron merged 1 commit into
parity/queuefrom
issue/15-materialize-preview-window

Conversation

@Adron

@Adron Adron commented Sep 16, 2026

Copy link
Copy Markdown
Member

Closes #15. Part of epic #10. All five entry points (#12, #13, #14, #16) open this one window.

Built in :core:materialize on top of #11's domain. Nothing outside core/materialize/ was touched.

Design

  • One editing state, four destinations. Switching destination changes only target; nothing is
    thrown away. MaterializeWindowUiState.toRequest() is the single place deciding which edits a
    destination can carry — so a title survives List → Doc (it still applies), while column edits do
    not reach a doc request (a document has no columns) yet are still there on the way back.
  • Entry-point agnostic input: MaterializeLaunch(source, initialTarget, preview).
    MaterializePreview is neutral display material — suggested title/description/file name,
    MaterializeColumns, preview rows keyed by propertyKey, totalRowCount, defaultIsPublic. The
    window never reaches into a feature module, and only column schema ever leaves the device,
    consistent with Create from…: POST /api/materialize plumbing + domain model #11's id-only source rule.
  • Title and visibility are shared rather than duplicated per half — the API reference's both
    example gives list and document the same title, and it makes a destination switch preserve them
    for free.
  • The document preview renders from the source columns, not the edited list columns.
    docConfig carries no field list, so showing renamed columns would promise something the request
    does not actually ask for.
  • Message → message is hidden for a Messages source (help centre: use Quote or Push). The
    message destination creates nothing and hands back the server-built draft — that is Create from…: "To Message" prefills the composer instead of creating #16's hook.
  • The subscriber gate is consumed, not re-implemented: Create from…: POST /api/materialize plumbing + domain model #11's repository refuses a free account
    before the request goes out, surfacing as subscriptionRequired.

Verification

./gradlew :app:assembleDebug testDebugUnitTest → BUILD SUCCESSFUL. :core:materialize 65 tests,
0 failures
(21 new), covering all four required cases:

  • column edits reaching the create request — names, types, removals, and an added column with a null
    sourceKey and a derived propertyKey;
  • a destination switch preserving what applies and dropping what cannot (List→Both, List→Doc,
    Doc→List, →Message);
  • both navigation targets present on success;
  • a blank list title refused with zero requests;
  • a free-account confirm over a real socket that never sees a byte.

7 Compose UI tests compile, not executed (no emulator).

core/materialize/build.gradle.kts gains the Compose plugin and deps — as #11 anticipated, so the
window lives in one place instead of five.

Note for #12#14

The window can only preview what the entry point seeds, so each entry point must supply correct
sourceKeys. For a messages source the documented defaults are Content / Author / Posted / Links /
Tags, but the wire spellings are not published, so they were not guessed here. An entry point
that seeds no columns simply sends no fields and lets the server derive them — a safe default.

Builds the finalization window that opens once a destination is chosen and
shows exactly what will be created, before anything is saved. It lives in
`:core:materialize` on top of #11's domain so the five entry points share one
window instead of re-implementing the preview and the confirm discipline; the
Compose plugin is enabled on the module for it.

- The destination is switchable from inside the window (List / Doc / Both /
  Message). Nothing is discarded on a switch: the edits stay in state and
  `toRequest()` is the single place that decides which of them a destination
  can carry, so a title survives List → Doc while columns, which a document
  has no use for, simply do not reach the request — and are still there on the
  way back.
- For a list: title, optional description, column rename / retype / remove /
  Add column over the twelve-value `ListColumnType`, public-private, and a live
  table of the rows and columns. A blank title is refused locally because the
  server answers `A list title is required`.
- For a document: title, file name, public/private, numbered or bulleted list
  style, inline or sub-item row data, with a rendered markdown preview built
  from the *source* columns — `docConfig` carries no field list, so previewing
  renamed list columns would promise something the request does not ask for.
- On success both navigation targets are exposed independently, so a `both`
  conversion offers a link into the new list and into the new document.
- The subscriber gate is consumed, not re-implemented: a free account's confirm
  is refused by the repository before a byte leaves the device and surfaces as
  `subscriptionRequired` for the host's existing upsell handling.

Message → message stays hidden, as the help centre documents (Quote or Push
covers it), and the message destination creates nothing: it hands the composer
the draft the server built.

Tests: 21 new unit tests — column edits reaching the create request, the
destination switch preserving what still applies and dropping what cannot,
both navigation targets on success, a blank title refused with no request, a
free account confirming with a real socket that never sees a byte, and the
document renderer's four styles. Plus a Compose UI test for the window.
`./gradlew :app:assembleDebug testDebugUnitTest` is green (65 tests in
`:core:materialize`, 0 failures).

Closes #15
@Adron
Adron merged commit 9dfeed0 into parity/queue Sep 16, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant