Billing, notifications, and no-FOUT
2026-02-21
Billing & subscriptions
- Shipped the settings → billing tab with full subscription management: plan display + status badges, Stripe Checkout upgrade flow, Stripe Portal access, and a cancel flow with a quick reason survey. commit
- Replaced the old single “upgrade” button with a real plan picker (Free/Basic/Advanced), feature grid, and monthly/annual toggle sourced from
GET /plans. - Show prices and compute the annual discount from
priceMonthly/priceAnnual(backend type update from #344). - Fixed the
204 No Contentcase in the API client so empty bodies (e.g.GET /users/me/subscriptionwithout a sub) don’t blow upPromise.alland trip the settings error state. - Follow-ups/tweaks: tightened the 3-column grid, widened the billing section, shortened labels, and scoped the serif font to article title/body only.
Notifications & integrations
- Added an in-app notification center: bell icon with unread badge, dropdown list, mark-as-read/all, 30s polling, and optimistic updates. commit
- Refactored webhooks to match the integration card → detail page pattern. New
/integrations/webhookpage, token count on the card, and a simpler main integrations screen. - Removed
safe()fallbacks around integrations API calls so real failures surface instead of silently returning empty arrays.
Internationalization & typography
- Swapped static JP fonts for broader Noto Sans/Serif coverage and added a
CjkFontLoaderthat pulls locale-specific CJK fonts (JP/KR/SC/TC) via Google Fonts CSS. Line-height overrides for ja/ko/zh. commit - Locale flow iterations:
- Sync locale from the backend preferences API once authenticated; unauthenticated users fall back to
navigator.languages. - Dropped
localStoragefrom resolution in favor of clean API + navigator behavior.
- Sync locale from the backend preferences API once authenticated; unauthenticated users fall back to
- Killed FOUT/flash-of-wrong-locale by blocking render until auth, locale, and CJK font CSS are ready. Switched
next/fontfromswaptoblockand wired a loading-gate chain: AuthProvider → LocaleProvider → CjkFontLoader → app. commit
Tests
- Added a
getSubscriptionmock to cover the settings page billing scenarios.