Skip to content

Tags: QuackbackIO/quackback

Tags

v0.7.0

Toggle v0.7.0's commit message
v0.7.0 — Widget auth improvements, anonymous merge, UI refinements 

v0.6.9

Toggle v0.6.9's commit message
Unify similar posts design and animations across widget and portal Widget: - Wrap similar ideas in AnimatePresence with opacity+height fade matching the portal's animation (duration: 0.15, ease: easeOut) Portal: - Redesign SimilarPostsCard to match widget aesthetic: status dot above title, clean row layout with vote pill, "Similar ideas" label with outline lightbulb icon - Remove CompactPostCard dependency and match strength labels

v0.6.8

Toggle v0.6.8's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Widget: changelog tab, configurable navigation, and inline email capt… …ure (#103) * Add changelog tab and configurable bottom navigation to widget - Add Feedback and Changelog tabs to widget bottom bar - Create widget-changelog.tsx and widget-changelog-detail.tsx for browsing changelogs in-widget - Add tabs config (feedback/changelog toggles) to WidgetConfig with admin UI - Hide tab bar when only one tab is enabled - Show contextual headings ("Share your ideas" / "What's new") in header row - Update widget preview to reflect tab configuration live - Support SDK open command with { view: 'changelog' } * Add inline email capture and fix widget auth redirect URLs - Add WidgetEmailCapture component for lightweight inline identification - Add identifyWithEmail to WidgetAuthProvider with in-flight deduplication - Extract shared applyIdentifyResult to deduplicate SDK identify and email capture paths - Show inline email form when HMAC is off and anonymous features are disabled - Fall back to portal redirect when HMAC verification is enabled - Fix broken /b/{boardSlug} URLs (route doesn't exist) to use /auth/login - Add hmacRequired to PublicWidgetConfig for widget-side branching - Vote button on post detail scrolls to email capture form when HMAC is off - Guard SDK quackback:open changelog command against disabled tab * Use solid icons and larger text for widget tab bar * Auto-identify widget users from portal session and improve email capture UX Portal session reuse: - If user is logged into the portal, pass their identity (not the token) to the widget via loader data - Widget exchanges identity for a bearer token on mount via the existing identify endpoint — token never appears in serialized HTML - SDK identify() calls override the portal session as before Email capture improvements: - Add compact variant for inline use in post detail (email + Go in one row) - Full variant: "No account needed" subtitle, collapsible "+ Add your name" - Compact variant used in post detail comment section * Fix email capture visibility and tab guard for SDK open command - Show email capture when either voting OR commenting requires auth, not just commenting (fixes missing scroll target when anon commenting is enabled but anon voting is disabled) - Guard quackback:open with view: 'new-post' against disabled feedback tab (prevents bypassing changelog-only mode via SDK)

v0.6.7

Toggle v0.6.7's commit message
Add publishedAt parameter to MCP changelog tools Allow backdating and scheduling changelog entries via the MCP create_changelog and update_changelog tools. The publishedAt field accepts an ISO 8601 datetime string and overrides the publish boolean. Past dates backdate the entry, future dates schedule it.

v0.6.6

Toggle v0.6.6's commit message
Fix remaining broken dynamic imports after settings service refactor 

v0.6.5

Toggle v0.6.5's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Cascade archive/close linked issues on post delete (#98) * feat: cascade archive/close linked issues on post delete Add the ability to archive or close linked external issues when a post is soft-deleted. The delete confirmation dialog now shows checkboxes for each linked integration, letting users choose which issues to clean up. Cascade failures are warnings, never blockers. - Add `status` column to `post_external_links` (active/closed/archived/error) - Add `post.deleted` event type with Slack/Discord/Teams/Zapier notifications - Add archive/close functions for all 11 issue tracker integrations - Add cascade delete service with parallel execution via Promise.allSettled - Update deletePostFn to accept cascade choices and dispatch post.deleted event - Add external links query hook and update DeletePostDialog with checkboxes - Add OnDeleteConfig toggle to all 11 integration settings pages * Fix cascade delete security, correctness, and reliability issues - Validate cascade targets server-side: only accept linkId + shouldArchive from client, load all link metadata from DB scoped to the post - Remove duplicate post.deleted event dispatch (softDeletePost already dispatches it) - Block delete confirmation while external links are still loading - Add OAuth token refresh before cascade archive API calls (Linear, Jira, Asana, Teams) - Fix GraphQL injection in Linear/Monday archive functions (use variables) - Extract duplicated integration display helpers to shared module - Fix Promise.allSettled fallback to preserve link metadata on rejection - Add unit tests for archive, cascade-delete, and integration helpers * Fix non-OK response handling in Linear/Monday and block delete on link fetch error - Check response.ok before parsing GraphQL body in Linear and Monday archive functions to catch 403/429/5xx errors - Block delete confirmation when external links query fails, showing an error message instead of silently skipping cascade operations * Improve delete dialog UX and add external_display_id for friendly identifiers - Remove static vote count from all integration message builders (never updated after sync) - Redesign cascade delete dialog to match mockup: integration icons, descriptive labels, code badge for issue ID, contextual subtitle - Add external_display_id column to post_external_links for human-friendly identifiers separate from the opaque API ID used for lookups/archive - Linear hook now stores UUID as external_id and identifier (e.g. QUA-24) as external_display_id - Fix missing Drizzle migration snapshot for post_external_links status column - Add getIntegrationItemNoun() helper for platform-specific item names * Add tests for Linear integration and shared helpers - Linear hook: externalId/externalDisplayId split, GraphQL mutation, error handling - Linear message builder: description formatting, no vote count, author fallbacks - Linear inbound: HMAC signature verification, status change parsing - Shared helpers: getIntegrationItemNoun coverage for all platform types * Add message builder tests for all integration platforms Covers vote count removal, content formatting, author fallbacks, non-post.created fallback, plus platform-specific behaviors: Jira ADF structure, Asana/Azure HTML escaping, Shortcut title truncation, Zapier structured payload. * Remove Claude Code review workflow * Fix typecheck errors in integration tests * Simplify archive and cascade delete code - Extract handleErrorStatus helper in archive.ts to deduplicate 401/404/error handling across 11 platform functions - Add AbortSignal.timeout(10s) to all external API calls to prevent hung requests from blocking post deletion - Cancel unconsumed response bodies on early-return paths for connection pool reuse - Combine two sequential DB queries in executeCascadeDelete into a single JOIN query - Deduplicate token refresh per integration ID to prevent race conditions when multiple links share the same integration - Move link status updates to batch after Promise.allSettled - Remove unnecessary lowercase + re-capitalize of action verb in delete dialog

v0.6.4

Toggle v0.6.4's commit message
Fix portal post edit view to match normal read-only view Collapse empty paragraphs in TipTap editor via CSS to match static prose rendering (ProseMirror adds <br> to empty paragraphs, preventing margin collapsing). Also fix edit title input to match h1 sizing (sm:text-2xl, mb-4).

v0.6.3

Toggle v0.6.3's commit message
Add Dragonfly caching for hot paths and improve logging Cache tenant settings, integration event mappings, and active webhooks in Dragonfly with 5-minute TTL and write-through invalidation. This eliminates redundant DB queries on every page load and event dispatch. - Add shared cache helpers (cacheGet/cacheSet/cacheDel) to redis.ts - Cache getTenantSettings() with invalidation on all 16 write functions - Cache integration mappings and webhook targets in event dispatch - Invalidate on platform credential, notification channel, and webhook CRUD - Migrate Slack channel cache to shared helpers - Log cache failures as warnings instead of silently swallowing - Remove ~25 noisy read-path console.log entries across domain services - Add warn-level logging for S3 deletion failures (previously silent) - Improve embedding error context with pipeline step and post ID - Add 47 tests across 6 new test files for cache behavior

v0.6.2

Toggle v0.6.2's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Fix widget vote/reaction highlights after SSO identify (#97) * Fix widget vote/reaction highlights not showing after SSO identify Three bugs prevented vote and reaction highlights from appearing in the widget after the host app calls Quackback.identify(): 1. Race condition: The votedPosts query fired before identify completed, got an empty result (no auth headers), and cached it for 5 minutes. Fix: add sessionVersion to the query key so it refetches after identify, and gate the query with enabled: hasToken. 2. Post detail fetched without auth: The widget's post detail query used portalDetailQueries.postDetail which doesn't pass Bearer headers. The server couldn't resolve principalId, so hasReacted was always false. Fix: use a widget-specific query that injects getWidgetAuthHeaders(). 3. Session-aware cache keys: All widget queries now include sessionVersion (from WidgetAuthProvider) in their keys, ensuring they automatically refetch with correct auth after any identify call. * Fix widget vote/reaction highlights after SSO identify Three bugs prevented vote and reaction highlights from appearing in the widget after the host app calls Quackback.identify(): 1. Race condition: The votedPosts query fired before identify completed, got an empty result (no auth headers), and cached it for 5 minutes. Fix: gate query with enabled: hasToken and include sessionVersion in the key so it refetches after identify. 2. Post detail fetched without auth: The widget used portalDetailQueries.postDetail which doesn't pass Bearer headers, so hasReacted was always false. Fix: widget-specific query that injects getWidgetAuthHeaders(). 3. No SSR vote highlights: Widget loader didn't pass userId to portalData query, so votedPostIds was always empty during SSR. Fix: pass session.user.id and seed the widget votedPosts cache. Added sessionVersion counter to WidgetAuthProvider that increments on every storeToken() call. Widget queries include it in their keys to automatically refetch with correct auth after any identify call. * Eliminate delay in widget vote highlights after identify The votedPosts query required a second round-trip after identify to fetch which posts the user had voted on. This caused a visible flash where vote buttons appeared un-highlighted before the data arrived. Fix: return votedPostIds directly in the /api/widget/identify response (fetched in parallel with the session lookup, zero extra latency). The auth provider seeds the widget votedPosts cache immediately from the response, so vote buttons highlight instantly after identify. Also moves sessionVersion from useWidgetAuth import (which violated the lib/ cannot import components/ lint rule) to a prop on useWidgetVote, passed from WidgetVoteButton. * Fix Codex review issues: stale cache on logout, mutation race condition P1: Bump sessionVersion when clearing identity (identify(null)) so widget queries move to a new cache key and don't show the previous user's stale vote/reaction data. P2: Use a ref for sessionVersion in mutation callbacks so optimistic updates and success handlers always write to the current cache key, even if ensureSession() bumped the version between render and mutation. * Simplify: reuse existing helpers, extract query key factory, fix review issues - identify.ts: Reuse getAllUserVotedPostIds() instead of duplicating the Drizzle query. Extract findOrCreateSession() helper to clean up the Promise.all. - use-widget-vote.ts: Extract widgetQueryKeys factory and INITIAL_SESSION_VERSION constant. All widget query keys now go through the factory — no more stringly-typed inline keys. - widget-post-detail.tsx: Use widgetQueryKeys.postDetail for query key and invalidation instead of inline string arrays. - widget/index.tsx: Use INITIAL_SESSION_VERSION constant instead of hardcoded 0. Always seed cache (even when empty) for consistency. * Eagerly load widget iframe on identify, remove noopener from powered-by links Iframe is now created immediately when the host app calls identify(), rather than waiting for the first open(). The iframe loads, hydrates, and completes the identify round-trip in the background - so when the user clicks the trigger button, vote highlights are already resolved. The panel starts with display:none to avoid intercepting pointer events while loading in the background. Also removed rel="noopener" from the "Powered by Quackback" links in the widget shell and feedback sidebar to preserve SEO link value. * Scroll widget post detail to top when data loads The scroll-to-top effect only ran on postId change, but the ScrollArea isn't mounted during the loading state (early return). Adding post to the dependency array ensures it also fires when data arrives and the ScrollArea renders. * Fix scroll-to-top firing on every background refetch The effect depended on [postId, post] — since post is an object from useQuery, every background refetch (every 30s) returned a new reference and reset the scroll, interrupting users reading comments. Now uses a ref to track the last scrolled postId. Scrolls exactly once per navigation: runs on every render (no dep array) but no-ops if already scrolled for this postId, handling the case where the ScrollArea isn't mounted during loading.

v0.6.1

Toggle v0.6.1's commit message
Fix OAuth token exchange 500 error for MCP clients The token endpoint handler consumed the request body via request.text() to check for the `resource` parameter, but only reconstructed the Request when `resource` was missing. When MCP clients (e.g. Claude Code) sent `resource` in the token exchange, the body was consumed but never rebuilt, causing Better Auth to receive an empty body and return 500. Always reconstruct the request after reading the body. Also adds consent page redirect URL fallback and DB migration for new OAuth columns (require_pkce, auth_time).