Skip to content

[LLV] Kanban demo server sync#641

Draft
mat-hek wants to merge 8 commits into
mf/llv-mountfrom
mf/llv-kanban-comp
Draft

[LLV] Kanban demo server sync#641
mat-hek wants to merge 8 commits into
mf/llv-mountfrom
mf/llv-kanban-comp

Conversation

@mat-hek

@mat-hek mat-hek commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

No description provided.

@mat-hek mat-hek changed the base branch from main to mf/llv-kanban June 19, 2026 10:10
@mat-hek mat-hek force-pushed the mf/llv-kanban-comp branch 6 times, most recently from 98aa5f3 to 7e155be Compare June 23, 2026 10:16
Base automatically changed from mf/llv-kanban to main June 23, 2026 10:29
@mat-hek mat-hek force-pushed the mf/llv-kanban-comp branch 16 times, most recently from 6e30ef6 to 525d632 Compare June 26, 2026 12:44
@mat-hek mat-hek changed the base branch from main to mf/llv-mount June 26, 2026 12:48
@mat-hek mat-hek force-pushed the mf/llv-kanban-comp branch 5 times, most recently from aa83a2e to 412674b Compare June 30, 2026 12:55
@mat-hek mat-hek force-pushed the mf/llv-kanban-comp branch 3 times, most recently from 5cb8665 to 2d3d842 Compare June 30, 2026 14:36
@mat-hek mat-hek force-pushed the mf/llv-kanban-comp branch from 2d3d842 to b06173a Compare June 30, 2026 14:39
Comment on lines +87 to +96
@doc false
# Top-level assign keys cross the JSON boundary as strings; convert them back to
# atoms so they read like Phoenix assigns (`@items`). Nested values are left as
# is — deeply atomizing arbitrary maps would be unsafe.
def __normalize_assigns__(assigns) do
Map.new(assigns, fn
{key, value} when is_atom(key) -> {key, value}
{key, value} when is_binary(key) -> {String.to_atom(key), value}
end)
end

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using JSON is quite inconvenient for client-server communication when both sides are in Elixir, and results in code like this. I think we should switch to erlang term format

mat-hek added 8 commits June 30, 2026 16:59
Add a `@server` phx-target sentinel and a programmatic `push_server_event/3`
so a popconent can deliver an event to the host (server) LiveView that mounted
it, over the regular Phoenix websocket.

The JS overrides the fake view's `withinTargets` to recognize the `@server`
sentinel and resolve the host `[data-phx-session]` root fresh on each event
(a reconnect can swap it out), dispatching via `liveSocket.owner`. A
`window.__llvPushServer` hook backs the Elixir `push_server_event/3`.
Add a `@default` sentinel (dispatch locally, as if untargeted) and a
`LocalLiveView.targets/1` helper that joins several targets into one
`phx-target`, so a single interaction can be delivered to the popconent, the
host LiveView, and/or ordinary selectors/cids at once:

    phx-target={targets([@default, @server])}

Tokens are joined with the ASCII Unit Separator (never present in a selector or
cid); the JS `withinTargets` override splits on it and dispatches each leg.
Adopt the new popconent in the example: convert the standalone
`Local.KanbanLive` into a `Local.Kanban` popconent mounted by a server
`BoardLive`, route "/" to it, and drop the default Phoenix page controller.
Rewrite the `Local.Kanban` popconent into an optimistic, collaborative client
built on the new server-event APIs: edits apply locally and are pushed to the
host via `push_server_event/3`, while removes ride `targets([@default, @server])`
so the same DOM event runs both in the browser and on the server.

Tasks carry client-generated fractional-index ranks (`Local.Rank`) with the
task id baked in, so positions are globally unique and sort deterministically.
The `OrderedMap` is gone — columns and tasks are plain maps sorted by position
on render.
Back the demo with the database and make the server the source of truth.

  * Schemas (Board/Column/Task) + migration, and a `Boards` context with the
    edit operations (add/move/remove) that persist what the client generates.
  * A board index LiveView plus a host `BoardLive` that applies each edit and
    broadcasts `:board_changed` over PubSub, so every viewer re-reads and
    reconciles (failures re-push the authoritative board to roll back).
  * Seeds with fixed, counter-derived ids (stable across re-runs); inserts use
    `on_conflict: :nothing` so re-seeding is idempotent.
ExUnit coverage for the `Boards` context and host `BoardLive`, plus a Playwright
suite driving the WASM popconent end to end (board lifecycle, columns, tasks,
drag & drop, two-client realtime, optimistic rollback). An `e2e_test.exs` boots
the endpoint and runs the Playwright suite; the workspace/lockfile gain the
`test/playwright` package.
GitHub Actions workflow that sets up Elixir + pnpm, installs Playwright
browsers, and runs the local-lv-kanban ExUnit and e2e suites.
@mat-hek mat-hek force-pushed the mf/llv-kanban-comp branch from b06173a to 2a704af Compare June 30, 2026 15:00
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