github usertour/usertour v0.8.7

3 hours ago

This release brings announcements back as a first-class content type: compose product news in the builder, target and schedule it, and it lands in the Resource Center as a dated feed — with unread badges on the launcher and, for the news that deserves attention, a one-time popup shown as a centered modal or a speech bubble. Alongside it, a round of content-editor autosave hardening so concurrent edits, republishes, and page exits no longer lose work.

What's Changed

📣 Announcements in the Resource Center

The new Announcement content type publishes into its own Resource Center tab as a feed: newest first, grouped under date separators, each entry showing its intro content. An optional Read more opens the full article on a detail page inside the panel, and navigating back returns to the feed exactly where you left it. Announcement content supports the full block editor, user-attribute interpolation, and buttons limited to the actions that make sense inside a feed.

🔔 Notification levels: silent, badge, popup

Each announcement chooses how loudly it announces itself:

  • Silent — it simply appears in the feed.
  • Badge — an unread count appears on the launcher, the announcement tab, and the Resource Center home row; opening the feed marks everything seen and clears the badges immediately.
  • Popup — the newest unseen announcement additionally presents itself once, either as a centered modal or as a speech bubble anchored to the launcher, following all four launcher placements.

Popup sizing lives in a dedicated Announcement section of the theme editor, so popups follow your theme like every other surface.

🎯 Targeting, scheduling, and analytics

Every announcement carries its own audience rules, evaluated server-side so the feed, the badge count, and the popup always agree on who sees what. Publishing stamps the announcement time; set a future time instead and the announcement stays hidden everywhere until that moment, then slots into the feed under the right date. Seen state is tracked per user and flows into analytics — announcement events show up in the user's activity feed, and the new default events are backfilled into existing projects on upgrade so no analytics are lost.

🐛 Fixes

  • The content editor no longer loses edits under concurrent saves. Editing a published version's content and settings at the same time could fork two drafts and drop one side's changes, republishing could ship a stale config, and targeting edits could silently revert to the pre-edit rules after republish. Forks are now serialized and each save path applies its own changes.
  • The save indicator tells the truth — it stays on while a debounced save is still pending and survives edits made mid-write, so leaving the page no longer discards work that looked saved.
  • Previews no longer crash on empty versions — the launcher list and content-detail previews guard against versions that have no data yet.
  • Resource Center home header background defaults to none, so themes without an explicit header color render as designed.
  • SDK: a failed Resource Center data load is no longer mistaken for an empty one — failures are now retried, and widget socket round-trips are bounded by a timeout so a dropped acknowledgment can't hang the panel.

🛠️ Under the hood

The announcement pipeline is built around a single service so its security-sensitive decisions can't drift:

  • AnnouncementService owns one visibility gate — published, not deleted, announcement time reached, targeting passed — shared by the feed scan and the direct by-id fetch, so a deep link can never expose what the feed would hide. Websocket payloads for the announcement APIs are validated at the boundary.
  • Read state lives in a dedicated BizAnnouncementSeen table; marking is batched, and the insert-returning first-seen path emits each analytics event exactly once.
  • Default events/attributes backfill for existing projects moved from app boot into prisma/seed, running idempotently on every deploy; the seed now connects over DATABASE_DIRECT_URL so a saturated connection pooler can't silently skip it.
  • @usertour/types now holds only types and enums — all instantiated constants moved to @usertour/constants.

Full Changelog: v0.8.6...v0.8.7

Don't miss a new usertour release

NewReleases is sending notifications on new releases.