Matthew Meszaros
|
638c40c629
|
feat: make make sandbox a one-command demo stack and drop the stale kafka/localstack/worker-premium targets
|
2026-07-20 11:12:37 +02:00 |
|
Matthew Meszaros
|
bbc1377a80
|
fix: point sandbox mailboxes at the worker that actually runs, seed that worker row, and wire in the analytics dataset
|
2026-07-20 11:12:37 +02:00 |
|
Matthew Meszaros
|
332851a0d3
|
feat: seed deliverability, contact activity, reply intents, suppression, mailbox errors, an audit trail, a teammate, an api key, and completed/paused campaigns for the sandbox
|
2026-07-20 11:12:37 +02:00 |
|
Matthew Meszaros
|
af048b9d75
|
feat: restore the dovecot imap host behind a sandbox profile so seeded mailboxes really sync
|
2026-07-20 11:12:36 +02:00 |
|
Matthew Meszaros
|
58d7baf79d
|
docs: cover the no-cloud setup and the PUBLIC_HOST knob
|
2026-07-20 09:56:47 +02:00 |
|
Matthew Meszaros
|
49ffaedc9f
|
feat: run make dev on nats and make make up the one-command self-host
|
2026-07-20 09:56:47 +02:00 |
|
Matthew Meszaros
|
9590bd63fb
|
feat: add a no-cloud docker compose stack
|
2026-07-20 09:56:47 +02:00 |
|
Matthew Meszaros
|
a419bde824
|
build: make the docker images cgo-free and cache-mounted so builds are fast
|
2026-07-20 09:56:47 +02:00 |
|
Matthew Meszaros
|
3922333930
|
feat: run the tracking service on nats by default, kafka behind a cargo feature
|
2026-07-20 09:56:29 +02:00 |
|
Matthew Meszaros
|
b1070ce097
|
feat: rip the cloud-provisioning and billing pages out of the admin ui
|
2026-07-20 09:56:29 +02:00 |
|
Matthew Meszaros
|
897da46e6c
|
feat: drop the cloud-provisioning and billing admin routes
|
2026-07-20 09:56:29 +02:00 |
|
Matthew Meszaros
|
543d97f6f3
|
feat: wire the no-cloud providers into the consumer and worker
|
2026-07-20 09:56:29 +02:00 |
|
Matthew Meszaros
|
ed51b081e6
|
feat: wire the no-cloud providers and billing-off into the backend
|
2026-07-20 09:56:29 +02:00 |
|
Matthew Meszaros
|
866e096a2b
|
feat: add a billing-off mode that unlocks every feature
|
2026-07-20 09:56:17 +02:00 |
|
Matthew Meszaros
|
fc91d71492
|
feat: make google, apple and turnstile optional at boot
|
2026-07-20 09:56:17 +02:00 |
|
Matthew Meszaros
|
f94215d27a
|
feat: move kafka behind a build tag so the default build is cgo-free
|
2026-07-20 09:56:16 +02:00 |
|
Matthew Meszaros
|
fb853ea23d
|
refactor: publish mailbox validation over the bus and drop the raw kafka producer
|
2026-07-20 09:56:16 +02:00 |
|
Matthew Meszaros
|
19f753e163
|
refactor: route worker events and tracking through the event bus
|
2026-07-20 09:56:16 +02:00 |
|
Matthew Meszaros
|
e8de52ce30
|
refactor: put the task client behind a scheduler interface
|
2026-07-20 09:56:16 +02:00 |
|
Matthew Meszaros
|
e38de91fec
|
feat: add an in-process postgres task scheduler so delayed sends need no cloud tasks
|
2026-07-20 09:56:02 +02:00 |
|
Matthew Meszaros
|
8de314ae8b
|
feat: serve public blobs from the backend and fix avatar uploads on filesystem
|
2026-07-20 09:56:02 +02:00 |
|
Matthew Meszaros
|
0f1886b9dd
|
feat: add a filesystem blob storage backend
|
2026-07-20 09:56:02 +02:00 |
|
Matthew Meszaros
|
7e84da7f2a
|
feat: add config helpers for the provider switches
|
2026-07-20 09:56:01 +02:00 |
|
Matthew Meszaros
|
d90e07659e
|
chore: remove the dead astra/cassandra code and deps
|
2026-07-20 09:56:01 +02:00 |
|
Matthew Meszaros
|
59d5cc4ca2
|
Merge pull request #76 from warmbly/feature/notifications
Digest-first notifications: fewer emails, targeted audiences, one shared email per team event
|
2026-07-20 08:23:45 +02:00 |
|
Matthew Meszaros
|
07c6ae0aa7
|
feat: kill the settings autosave PUT storm: useAutosave becomes single-flight (the change effect re-arms every render because save is an inline closure, and with the baseline only moving after the response every render during an in-flight save fired another request; concurrent callers now share one in-flight promise and a trailing loop picks up edits made while saving), the notifications settings page hydrates its draft from the server exactly once so refetches never stomp or re-trigger mid-save, and the preferences PUT writes its echoed envelope into the react-query cache instead of invalidating (no more refetch per save)
|
2026-07-20 08:17:50 +02:00 |
|
Matthew Meszaros
|
85ab4ad7b5
|
feat: replace the notification email cadence enum with a configurable bundling window and drop the instant mode entirely: email_digest_minutes (30 minute floor, 30 default, 1440 max, constants in config) replaces email_digest with no per-event option or NOTIFICATION_EMAIL_ALLOW_INSTANT escape hatch, the handler validates the range and email_delivery now returns min/max minutes so clients render the bounds, the web settings page offers window presets (30m/1h/3h/daily) plus a Custom minutes NumberInput and iOS swaps the cadence menu for window presets including the server value when it matches none, the repo clamps stored values on read, and the guide plus deployment guide describe the window and keep only the NOTIFICATION_EMAIL_DAILY_CAP env
|
2026-07-20 08:01:22 +02:00 |
|
Matthew Meszaros
|
682ef3ac24
|
feat: cost guards on the notification email channel: the per-event instant cadence becomes self-host opt-in (NOTIFICATION_EMAIL_ALLOW_INSTANT, default off - PUT rejects it, stored values read back as smart, holds degrade to smart) with the capability exposed as email_delivery on the preferences GET so web and iOS hide the option on hosted deploys, each user gets a rolling 24h budget of non-security notification emails (NOTIFICATION_EMAIL_DAILY_CAP, default 25, 0 unlimited) counted off sent rows with over-budget alerts skipped to the in-app feed while security sign-ins always send, and coalesced group emails re-verify org membership at flush time so a member removed during the hold is dropped from To; deployment guide documents the three envs and the guide notes both limits
|
2026-07-20 07:44:43 +02:00 |
|
Matthew Meszaros
|
5d930057d9
|
feat: harden the notifications settings page against version skew: normalizeNotificationPreferences mirrors the backend defaults merge on the client (missing billing_alert/team_activity/email_digest filled with defaults, invalid cadence coerced to smart) and the page normalizes the fetched preferences before drafting, so an older backend or cached response can no longer crash the category rows
|
2026-07-20 07:36:17 +02:00 |
|
Matthew Meszaros
|
0186ed5c78
|
feat: web notification center + digest settings: the bell dropdown becomes a real notification center with per-category icon chips and tones, Today/Yesterday/Earlier group headers, compact relative timestamps, unread accent styling, All/Unread filter pills, first-load skeleton, empty state and a settings footer link; the settings page gains Billing and Team category sections and an Email delivery section using the shared OptionSelect radio idiom (Instant/Smart with Recommended badge/Hourly/Daily) riding the same autosave PUT, with the read-in-app/security-immediate/shared-email note; the preferences model adds billing_alert, team_activity and the email_digest union, and OptionSelect labels widen to ReactNode for the badge
|
2026-07-20 07:21:20 +02:00 |
|
Matthew Meszaros
|
33b779a406
|
feat: iOS notification preferences catch up with the digest-first email channel: NotificationPreferences decodes via a dynamic keyed container so the new email_digest scalar no longer breaks the flat category-map decode (older servers get billing_alert/team_activity seeded with backend defaults), the settings screen gains an Account group with Billing and Team category rows plus an Email delivery section whose Menu picker sets the instant/smart/hourly/daily cadence through the same PUT as the toggles with revert-on-failure, and the feed inherits icons for both new categories via the shared meta mapping
|
2026-07-20 07:20:33 +02:00 |
|
Matthew Meszaros
|
74c32bfec6
|
feat: document the digest-first notification email channel in the notifications guide: the three delivery rules (read in-app means no email, unread bundles into one email, shared events send one email with every recipient in To), the four cadences with smart as default, the always-immediate security sign-in exception, the new billing and team categories with their defaults, and the permission-targeted routing callout
|
2026-07-20 07:18:19 +02:00 |
|
Matthew Meszaros
|
a534ef1908
|
feat: notification email channel becomes digest-first: email-channel notifications queue as pending rows (migration 000076 adds group_key/email_state/email_due_at/email_attempts) with a due time from a new per-user email_digest cadence (instant/smart 15m/hourly/daily, security sign-ins always immediate), a 30s flush loop with SKIP LOCKED claims bundles a user's pending rows into one digest email and coalesces org-shared group_key events into a single email with every recipient in To, reading a notification in-app cancels its pending email, and NotifyOrg targets only members holding a permission (Slack fires once per group) with new producers: dead-worker downtime to manage_emails members (SetNX-deduped per incident), trial expiry to manage_billing members via new billing_alert category (replacing the direct owner email), and invitation accepts to manage_team members via new team_activity category
|
2026-07-20 07:16:25 +02:00 |
|
Matthew Meszaros
|
a813096b8b
|
Merge pull request #75 from warmbly/feature/compose-email
Compose experience, AI everywhere, undo send, and the iOS ports
|
2026-07-20 06:53:13 +02:00 |
|
Matthew Meszaros
|
0ecaf008f3
|
feat: iOS From picker parity + tag-scoped Auto: the tag chip row becomes a native Menu filter offering every user tag (not just in-use ones) with color dots and checkmarks, filtering turns the Auto row into Auto in <tag> previewing the best in-tag candidate and picking it carries autoTagID into the compose send as from_tag_id (cleared by explicit picks), the From row reads Auto in <tag> with the best member email via account tags lifted into ComposeStore, and ContactPickerSheet swaps its category chips for the same compact menu idiom
|
2026-07-20 05:33:06 +02:00 |
|
Matthew Meszaros
|
847f3ba472
|
feat: document the From menu in the unibox guide: Auto scoring order, tag filtering, tag-scoped Auto picking within a mailbox group, and the from_tag_id API field
|
2026-07-20 05:28:05 +02:00 |
|
Matthew Meszaros
|
296e781ce5
|
feat: scoped Auto in the dashboard From menu: filtering by a mailbox tag turns the Auto row into Auto in <tag> previewing the best-scored member, the pick carries the tag through onChange into ComposeWindow state and the send request's from_tag_id, and the collapsed From row chip reads auto with the tag title while the resolved-signature preview resolves to the best mailbox within the tag
|
2026-07-20 05:28:05 +02:00 |
|
Matthew Meszaros
|
f20300a280
|
feat: tag-scoped auto mailbox pick on compose: POST /unibox/compose accepts from_tag_id which restricts the automatic sender choice to active mailboxes carrying that tag (best-with-budget-first within the group, 400 when the tag has no active members, ignored with an explicit account; GetByTags is user-scoped so foreign tag ids resolve to no members)
|
2026-07-20 05:28:05 +02:00 |
|
Matthew Meszaros
|
91c12e91be
|
feat: document undo send: the unibox guide gains an Undo send section (default 30s, 5-120s under Settings Profile, header countdown with cancel, scheduled and smart sends unaffected) and the unibox API reference notes instant sends hold for the undo window with scheduled_at reflecting it and DELETE /unibox/scheduled/:task_id cancelling inside it
|
2026-07-19 18:57:32 +02:00 |
|
Matthew Meszaros
|
c53225e6f3
|
feat: undo send in the dashboard: instant compose and reply sends feed a new outbox store and an amber header pill counting down to the server scheduled_at with inline Cancel plus a portaled dropdown listing every pending send with per-row cancel, cancelling a compose reopens the docked composer from a full draft seed and cancelling a reply navigates to the thread and reopens the reply composer prefilled via a restore handoff, 404 collapses to Already sent, and the profile settings page gains an autosaved Undo window NumberInput (5-120s) wired to PUT /auth/me/send-preferences patching the cached /auth/me user
|
2026-07-19 18:56:55 +02:00 |
|
Matthew Meszaros
|
10217039f0
|
feat: undo send in the iOS app: instant compose and reply sends swap the sent toast for a dark UndoSendBanner capsule counting down to the server's scheduled_at with an Undo button that cancels via DELETE unibox/scheduled/:task_id, compose undo reopens the sheet through the draft-resume path with the full payload and reply undo re-presents UniboxComposer prefilled from a UniboxReplySnapshot, 404 collapses to an Already sent toast, expiry flips the capsule to Sent briefly, and the User model gains undo_send_seconds (default 30 when absent)
|
2026-07-19 18:53:21 +02:00 |
|
Matthew Meszaros
|
db400ac086
|
feat: undo send window on the backend: migration 000075 adds users.undo_send_seconds (default 30, CHECK 5-120), emailsend queues instant sends (compose, reply, agent-draft approvals) that many seconds into the future so the existing DELETE /unibox/scheduled/:task_id cancel path can still stop them, the value rides /auth/me and a new JWT-only PUT /me/send-preferences updates it with bounds validation and user-cache invalidation
|
2026-07-19 18:45:50 +02:00 |
|
Matthew Meszaros
|
e46fc1cd3e
|
feat: make the composer picker search feel alive: contact searches debounce 300ms and keep previous results on screen while the next query loads (new useDebouncedValue hook + keepPrevious option on useSearchContacts), the browse panel and From menu ease their height with a ResizeObserver-driven AnimatedHeight instead of snapping, in-flight searches show a spinner in the search box plus shimmer skeleton rows on first load, FilterMenu closes itself on any outside click or Escape (its trigger lives inside a data-floating panel the shared hook ignores, so opening the sibling filter now closes the other), and the browse panel widens to 400px
|
2026-07-19 18:36:31 +02:00 |
|
Matthew Meszaros
|
038668ff51
|
feat: clean up the composer picker chrome: a new compact FilterMenu (quiet bordered trigger showing the active choice, portaled dot-row dropdown) replaces the chip rows — the From menu's tag filter becomes one small All-tags dropdown beside the search box, and the browse-contacts panel collapses its three header rows (search, category chips, sort row) into a single row with search plus category and sort dropdowns, hugs its content height instead of a fixed 380px, shows one category badge per row, and only reveals the Add footer once something is picked
|
2026-07-19 18:15:56 +02:00 |
|
Matthew Meszaros
|
7eb8510a89
|
feat: fix the composer's From tag chips and browse-contacts panel: the From menu now offers every defined tag (not just ones already on a mailbox, so a tag created in the bulk bar is immediately selectable), and the browse panel portals to the body with a viewport-anchored fixed position and flip (the compose window clips overflow, so the 380px panel was being cut off inside the To row and reading as a cramped overflowing box), matching the MailboxPicker portal pattern
|
2026-07-19 17:06:00 +02:00 |
|
Matthew Meszaros
|
2aeaa577c8
|
feat: iOS mailbox tag management: a reusable tag picker sheet (search, colored-dot rows, multi-select, inline Create-tag that POSTs /tags cycling the palette and refreshes the session), a Tags section on the mailbox detail Setup tab editing via the optimistic PATCH /emails/:id path, and bulk Add/Remove tags in the list's selection capsule driving the new PATCH /emails/tags endpoint with reload, select-mode exit, and a light haptic on apply
|
2026-07-19 16:14:17 +02:00 |
|
Matthew Meszaros
|
07315cb118
|
feat: iOS compose polish from simulator feedback: the AI menu now targets highlighted text (shared selection quick actions plus a Custom edit through the prompt bar's editing-selection mode, stale-range guarded), the From picker gains tag filter chips built from the account directory and the member's tag definitions (only tags actually in use are offered), and every recipient field gets a plus button opening a browse-contacts sheet with search, category chips, multi-select checkmarks, and already-added rows disabled
|
2026-07-19 16:14:17 +02:00 |
|
Matthew Meszaros
|
02a524616e
|
feat: composer recipient upgrades: a user-plus button on the shared recipient field opens a browse-contacts panel (search, category filter chips, Recent/Name/Email sort, checkbox rows with category badges, already-added rows disabled, Add-N appends with case-insensitive dedupe) available in both compose and reply, and the docked compose window now auto-minimizes to the corner bar when you navigate to another page so the draft follows you without covering the destination
|
2026-07-19 16:13:58 +02:00 |
|
Matthew Meszaros
|
5fae462bb2
|
feat: bulk tag mailboxes from the list's selection bar: a Tags popover with an Add/Remove segmented toggle, tag search, checkbox rows with colored dots, an inline Create-tag row (POSTs /tags cycling the editor's palette and updates the profile cache), and an apply button that hits the new PATCH /emails/tags bulk endpoint, invalidates the emails queries, and keeps the row selection for chaining
|
2026-07-19 16:13:58 +02:00 |
|
Matthew Meszaros
|
e1750d8988
|
feat: add PATCH /emails/tags for bulk mailbox tagging: add_tags/remove_tags set semantics across up to 1000 mailboxes in one transaction (ownership of both mailboxes and tags enforced in SQL so stale ids are skipped, composite-PK insert makes re-adds no-ops), naturally idempotent so no Idempotency-Key needed, one audit entry with account/added/removed counts riding the email_account spine, and the endpoint registered in the docs scope map under WRITE_EMAILS
|
2026-07-19 16:13:43 +02:00 |
|