summaryrefslogtreecommitdiff
path: root/apps/web/app/reader/_components
diff options
context:
space:
mode:
authorFuwn <[email protected]>2026-02-10 01:32:16 -0800
committerFuwn <[email protected]>2026-02-10 01:32:16 -0800
commit293f9ccc308b69f507e752af01a49652faee330f (patch)
treeb699e52309cfab03d539a048de34a98158d6168b /apps/web/app/reader/_components
parentfeat: add automatic timeline refresh with scroll position preservation (diff)
downloadasa.news-293f9ccc308b69f507e752af01a49652faee330f.tar.xz
asa.news-293f9ccc308b69f507e752af01a49652faee330f.zip
feat: gate offline reading to pro and developer plans
Service worker now only caches Supabase REST responses when the user's tier allows offline reading. Client syncs tier status to SW via postMessage after profile loads. Free users see a descriptive offline banner instead of stale cached data.
Diffstat (limited to 'apps/web/app/reader/_components')
-rw-r--r--apps/web/app/reader/_components/offline-banner.tsx10
-rw-r--r--apps/web/app/reader/_components/reader-layout-shell.tsx2
2 files changed, 11 insertions, 1 deletions
diff --git a/apps/web/app/reader/_components/offline-banner.tsx b/apps/web/app/reader/_components/offline-banner.tsx
index 81ebf8f..b6089de 100644
--- a/apps/web/app/reader/_components/offline-banner.tsx
+++ b/apps/web/app/reader/_components/offline-banner.tsx
@@ -1,6 +1,8 @@
"use client"
import { useSyncExternalStore } from "react"
+import { TIER_LIMITS, type SubscriptionTier } from "@asa-news/shared"
+import { useUserProfile } from "@/lib/queries/use-user-profile"
function subscribe(callback: () => void) {
window.addEventListener("online", callback)
@@ -21,12 +23,18 @@ function getServerSnapshot() {
export function OfflineBanner() {
const isOnline = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot)
+ const { data: userProfile } = useUserProfile()
if (isOnline) return null
+ const tier = (userProfile?.tier ?? "free") as SubscriptionTier
+ const allowsOffline = TIER_LIMITS[tier]?.allowsOfflineReading ?? false
+
return (
<div className="border-b border-border bg-background-tertiary px-3 py-1.5 text-center text-text-dim">
- you are offline — showing cached content
+ {allowsOffline
+ ? "you are offline \u2014 showing cached content"
+ : "you are offline \u2014 offline reading is available on pro and developer plans"}
</div>
)
}
diff --git a/apps/web/app/reader/_components/reader-layout-shell.tsx b/apps/web/app/reader/_components/reader-layout-shell.tsx
index 7ec1002..17ae0b5 100644
--- a/apps/web/app/reader/_components/reader-layout-shell.tsx
+++ b/apps/web/app/reader/_components/reader-layout-shell.tsx
@@ -14,6 +14,7 @@ import { KeyboardShortcutsDialog } from "./keyboard-shortcuts-dialog"
import { MfaChallenge } from "./mfa-challenge"
import { OfflineBanner } from "./offline-banner"
import { useKeyboardNavigation } from "@/lib/hooks/use-keyboard-navigation"
+import { useOfflineAccessSync } from "@/lib/hooks/use-offline-access-sync"
import { createSupabaseBrowserClient } from "@/lib/supabase/client"
import { useSubscriptions } from "@/lib/queries/use-subscriptions"
import { useCustomFeeds } from "@/lib/queries/use-custom-feeds"
@@ -58,6 +59,7 @@ export function ReaderLayoutShell({
const { data: subscriptionsData } = useSubscriptions()
const { data: customFeedsData } = useCustomFeeds()
const { data: userProfile } = useUserProfile()
+ useOfflineAccessSync(userProfile?.tier as "free" | "pro" | "developer" | undefined)
const sidebarMaxWidth = useMemo(() => {
if (typeof window === "undefined") return "35%"