Skip to content

Schedule send, contacts CRM, inbox tabs, and reliability fixes#40

Open
clickclacknvrwhack wants to merge 5 commits into
MaxGhenis:mainfrom
clickclacknvrwhack:feat/schedule-contacts-crm-unread-repairs
Open

Schedule send, contacts CRM, inbox tabs, and reliability fixes#40
clickclacknvrwhack wants to merge 5 commits into
MaxGhenis:mainfrom
clickclacknvrwhack:feat/schedule-contacts-crm-unread-repairs

Conversation

@clickclacknvrwhack

Copy link
Copy Markdown
Contributor

A batch of new features and reliability fixes for the inbox, all local-first and test-covered.

✨ Features

  • Schedule send (text + rich media) — compose a message or attach a file, pick Tomorrow 9 AM / 5 PM / custom, and it sends at that time. New scheduled_messages table, a background scheduler (startup catch-up + 20s ticker, atomic exactly-once claim, offline-retry/fail, per-platform routing), /api/schedule + /api/schedule-media endpoints, and a pending strip above the composer with cancel.
  • Contacts CRM — a person-icon view: everyone you've messaged on the left; detail on the right with photo, all contact info, last-contacted, tags, a reach-out tickler/cadence, and an auto-generated relationship summary. Open a person's thread from their card; sort alphabetically (numbers last) or by most-messaged. Plus read-only Google Messages contact sync (no inbox flooding) and add-local-contact.
  • Inbox tabs + reaction reactor names — custom inbox tabs with multi-select move, and reaction tooltips that name who reacted.
  • Architecture code mapdocs/CODEMAP.md.

🔧 Repairs

  • Recurring unread badges — reads no longer get resurrected by Google's stale conversation re-syncs (new last_read_ts watermark; migration auto-protects already-read threads).
  • iMessage tapbacksLoved "…" texts convert into emoji reactions instead of cluttering threads.
  • "Empty message" stubs — empty placeholder rows with wrong dates are detected and removed (live + startup repair).
  • Three white flashes on send/receive — replaced full-thread re-render with keyed reconcile.
  • Images stuck on a loading spinner — media load/error handlers resolve correctly.
  • Group-message creation failure — two-step RCS-group (CREATE_RCS) flow + E.164 phone normalization.
  • Recents ordering — contentless group-reaction stubs no longer float 1:1 threads to the top.

Notes

  • Layered, buildable commits: store → backend → web → docs.
  • Full Go suite green (go test ./internal/... ./cmd/); staticcheck clean on changed packages.
  • Branch is currently a few commits behind upstream main; happy to rebase if preferred.

🤖 Generated with Claude Code

clickclacknvrwhack and others added 5 commits June 12, 2026 19:12
…king

Extends the SQLite store with the data layer for this batch of features:

- scheduled_messages table + queries (scheduled.go): create/list/cancel,
  atomic pending->sending claim for exactly-once delivery, due-message
  lookup, and on-demand media-blob loading. Backs schedule-send.
- last_read_ts read watermark on conversations (conversations.go, db.go):
  MarkConversationRead advances it, and UpsertConversation only honors an
  incoming unread flag when the event carries genuinely newer activity --
  so Google's stale unread re-syncs can't resurrect a cleared badge.
- people.go / contact_meta.go: contacts-CRM aggregation (group 1:1s by
  person, real message counts) plus per-person tags, reach-out cadence,
  and cached relationship summaries.
- tapback.go: parse iMessage tapbacks (Loved "...") into emoji reactions
  on the referenced message, with a startup repair pass.
- stub.go: detect and batch-remove empty placeholder rows that would
  otherwise render as "Empty message" and wrongly surface threads.

Each addition is covered by table-driven tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Backend for the new features plus several reliability fixes:

- scheduler.go: background loop (startup catch-up + 20s ticker) that sends
  due scheduled messages. Atomically claims each (exactly-once), checks the
  route is connected, and routes text/media by platform -- WhatsApp/Signal
  carry captions inline; SMS uploads via libgm and sends any caption as a
  follow-up text. Offline -> retry; >=5 attempts -> mark failed. Test hooks
  (sendText/sendMediaOverride) keep it deterministic in tests.
- contacts.go: read-only Google Messages contact sync (never creates
  conversations, so it can't flood the inbox).
- gm.go / send_group.go: two-step CREATE_RCS group-creation flow and E.164
  phone normalization, fixing "Google Messages didn't return a conversation"
  and missing-"+" group sends.
- events.go / backfill.go: gate incoming messages through ApplyTapback and
  IsEmptyStubMessage; advance recency only on real content.
- app.go: startup repair chain (legacy artifacts -> contentless recency ->
  tapbacks -> empty stubs -> media placeholders).
- story/summary.go: local relationship summaries with system-message
  filtering ("No communication" when only RCS banners exist).
- importer/contacts_macos.go + CLI wiring for `import contacts`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Web layer for the new features plus rendering fixes:

- Schedule-send endpoints: /api/schedule (GET list + POST text),
  /api/schedule-media (POST multipart, persists the blob), and
  /api/schedule/{id} DELETE to cancel. Compose UI gains a clock menu
  (Tomorrow 9 AM / 5 PM / custom), media-aware scheduling, and a pending
  strip above the composer with a paperclip for attachments.
- Contacts CRM endpoints (/api/people, /api/people/{key}/{tags,reach-out,
  summary}, /api/contacts sync + add-local) and a person-icon CRM view:
  list with sort modes, detail with photo, contact info, last-contacted,
  tags, reach-out tickler, summary, and open-conversation.
- Rendering fixes: keyed-reconcile message rendering (no more triple
  white flash on send/receive) and window-scoped media load/error
  handlers (images no longer stick on the loading spinner).
- normalizePhoneNumber (E.164) for new conversations.

Covered by phone_test.go, schedule_media_test.go, and e2e specs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs/CODEMAP.md: a guide to where everything lives -- entry points, the
app coordinator, platform connectors, the SQLite store, importers, the
web/API + UI, MCP tools, analytics/viz, and cross-cutting data flows
(receive, send, new conversation, schedule-send, contacts CRM).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase C orphan-contact discovery is now gated by upstream's
orphanContactDiscoveryEnabled() (OPENMESSAGES_BACKFILL_DISCOVER_ORPHANS),
introduced in MaxGhenis#21, which supersedes the App.EnableContactDiscovery field
this branch had added for the same purpose. Remove the now-dead field and
the redundant test assignments; the tests already opt in via t.Setenv.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@clickclacknvrwhack clickclacknvrwhack force-pushed the feat/schedule-contacts-crm-unread-repairs branch from 41a9a7b to 1f2ebf2 Compare June 13, 2026 02:24
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