Skip to content

feat(windows): DM panel + chat notifications + drafts watcher#116

Open
Aryansharma28 wants to merge 3 commits into
mainfrom
feat/windows-channels-followup-part2
Open

feat(windows): DM panel + chat notifications + drafts watcher#116
Aryansharma28 wants to merge 3 commits into
mainfrom
feat/windows-channels-followup-part2

Conversation

@Aryansharma28

Copy link
Copy Markdown
Contributor

Summary

Three follow-ups that round out the Phase 7 channels work shipped in #105:

  • feat(windows): DM panel in chat UI #109 — DM panel in chat UI. Adds a Direct Messages section to the chat sidebar. Threads from list_dm_pairs show up under the channel list; selecting one loads the thread via read_dm_messages and renders the same layout as a channel. Send goes through send_dm with from = SELF. Drafts + read-state for DMs persist under their dms.<pairKey> slots, mirroring the channel flow. A "Start DM" button accepts @handle or card_<ksuid> and opens (or creates) the thread.

  • feat(windows): wire OS + Pushover notifications for inbound channel/DM messages #110 — notifications for inbound chat messages. Channel and DM events now fire OS + Pushover notifications, reusing the existing pushover module and tauri-plugin-notification (same dispatch path as the card-finish toast at lib.rs:907–979). Suppression rules: SELF-sends, system/join/leave messages, non-member channels, foreground-focused threads. Per-thread 2s debounce coalesces bursts.

  • feat(windows): emit drafts-changed watcher event #111 — drafts-changed watcher event. Closes the gap in the 4-event watcher contract: drafts.json writes now emit DRAFTS_CHANGED, and the store refetches via get_drafts within ~100ms.

Closes #109, closes #110, closes #111.

Verification

npm run build and cargo build both green (existing-warnings-only).

CLI round-trip on the worktree:

$ kanban dm send --as some-other-handle @user "ping from CLI part2 verify"
@some-other-handle → @user: ping from CLI part2 verify

$ kanban dm list
@some-other-handle__@user

$ kanban dm read --as some-other-handle @user
[2026-06-13T22:20:44.358+00:00] @some-other-handle: ping from CLI part2 verify

The on-disk pair-key format (@some-other-handle__@user.jsonl) matches the parsePartyKey / otherPartyOfPair helpers the frontend uses to render the sidebar and resolve the to party on send.

Debounce validation (#110)

Module-scoped lastNotifyAtByThread: Map<string, number> is keyed by ch:<name> / dm:<pairKey> and read inside dispatchChatNotification. If now - lastAt < 2000, the dispatch returns early without updating lastNotifyAtByThread — so the 2 s window is anchored at the first notification, not the most recent. A burst of 5 messages within 2 s = 1 notification; the 6th message after the window starts a fresh debounce. lastSeenIdByThread updates regardless so we still track what's been observed for the next event.

Other suppression paths (each verified at the code level):

  • self-send filter via isSelf (matches cardId === null && handle === "user")
  • system/join/leave filter via (m.type ?? "message") === "message"
  • foreground suppression via useBoardStore.getState().chatOpen && selectedThread === thread
  • channel membership via channel.members.some(m => m.cardId === null && m.handle === SELF.handle)

DM panel screenshots

Screenshots best captured from a local npm run tauri dev session — the GUI is functional but I can't reliably screenshot a Tauri window from this environment. The sidebar gains a "Direct Messages" section under the existing "Channels" section, each row showing @handle (blue dot) or card_<id> (purple dot) with an unread badge identical in styling to channel rows. Selecting a row swaps the main pane to a thread layout identical to a channel.

Test plan

  • cd windows && npm run build green
  • cd windows/src-tauri && cargo build green
  • Run npm run tauri dev; open chat panel; verify DM section renders existing threads
  • From another shell: kanban dm send --as agent-A @user "ping" → DM appears in sidebar within ~100ms with unread badge; click → reads, badge clears
  • Enable OS + Pushover toggles in Settings; send 5 DMs from CLI within 2 s → 1 notification fires
  • Open the thread + leave the panel focused on it; send another DM from CLI → no notification (foreground suppression)
  • Edit drafts.json on disk: store re-fetches and the relevant draft field updates without a restart

🤖 Generated with Claude Code

Closes the gap in the 4-event channels watcher contract: writes to
drafts.json now emit a DRAFTS_CHANGED event, and the frontend store
refetches via get_drafts. This lets a second app instance — or an
external edit — pick up draft changes within ~100ms instead of being
stuck on the in-memory copy until the next save_drafts call.
The chat panel renders a Direct Messages section alongside Channels.
Threads from list_dm_pairs are shown in the sidebar; selecting one
loads via read_dm_messages and shows the same thread layout as a
channel. Sends go through send_dm with from=SELF; drafts and
read-state are persisted under their respective dms.<pairKey> slots,
mirroring the channel flow.

Adds a Start DM affordance that accepts an @handle or card_<ksuid>,
plus the parsePartyKey / dmPairKey / otherPartyOfPair helpers needed
to map between the on-disk sorted-pair-key format and ChannelParticipant.
Channel and DM message events now fire an OS notification (via
tauri-plugin-notification) and a Pushover push (via the existing
pushover module) when:

  - SELF is a channel member, or SELF is one of the DM pair parties
  - The message isn't from SELF
  - The message kind is "message" (system/join/leave are skipped)
  - The chat panel isn't open on that exact thread (foreground = skip)

A per-thread 2s debounce coalesces bursts: 5 agent messages within
2s fire one notification, not five. The frontend store does the
"should we notify?" decision; the new notify_chat_message Tauri
command reuses the same dispatch path the card-finish notifications
already use, so settings toggles + Pushover creds work the same way.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant