summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
authorFuwn <[email protected]>2026-02-09 21:12:16 -0800
committerFuwn <[email protected]>2026-02-09 21:12:16 -0800
commit3c0b53ccbe40a75d6105f0e22c43a9f46975d9c1 (patch)
tree7289ba4e663ed388f0effc9462d845c9120cc785 /apps
parentfix: elevate to AAL2 before password/email change when MFA is enabled (diff)
downloadasa.news-3c0b53ccbe40a75d6105f0e22c43a9f46975d9c1.tar.xz
asa.news-3c0b53ccbe40a75d6105f0e22c43a9f46975d9c1.zip
feat: add per-feed "hide from timeline" option
Diffstat (limited to 'apps')
-rw-r--r--apps/web/app/reader/_components/sidebar-content.tsx2
-rw-r--r--apps/web/app/reader/settings/_components/settings-shell.tsx2
-rw-r--r--apps/web/app/reader/settings/_components/subscriptions-settings.tsx18
-rw-r--r--apps/web/lib/queries/use-subscription-mutations.ts30
-rw-r--r--apps/web/lib/queries/use-subscriptions.ts4
-rw-r--r--apps/web/lib/types/subscription.ts1
6 files changed, 55 insertions, 2 deletions
diff --git a/apps/web/app/reader/_components/sidebar-content.tsx b/apps/web/app/reader/_components/sidebar-content.tsx
index b5732c0..bd11470 100644
--- a/apps/web/app/reader/_components/sidebar-content.tsx
+++ b/apps/web/app/reader/_components/sidebar-content.tsx
@@ -335,6 +335,7 @@ export function SidebarContent() {
"flex items-center truncate pl-6 text-[0.85em]",
activeFeedIdentifier ===
subscription.feedIdentifier && ACTIVE_LINK_CLASS,
+ subscription.hiddenFromTimeline && "opacity-50",
sidebarFocusClass(focusedPanel, focusedSidebarIndex, navIndex++)
)}
>
@@ -396,6 +397,7 @@ export function SidebarContent() {
"flex items-center truncate pl-4 text-[0.85em]",
activeFeedIdentifier === subscription.feedIdentifier &&
ACTIVE_LINK_CLASS,
+ subscription.hiddenFromTimeline && "opacity-50",
sidebarFocusClass(focusedPanel, focusedSidebarIndex, navIndex++)
)}
>
diff --git a/apps/web/app/reader/settings/_components/settings-shell.tsx b/apps/web/app/reader/settings/_components/settings-shell.tsx
index 4153fc4..9d6c2d6 100644
--- a/apps/web/app/reader/settings/_components/settings-shell.tsx
+++ b/apps/web/app/reader/settings/_components/settings-shell.tsx
@@ -65,7 +65,7 @@ export function SettingsShell() {
</div>
</nav>
<div className="flex-1 overflow-y-auto">
- <div className="max-w-3xl">
+ <div className="max-w-4xl">
{activeTab === "subscriptions" && <SubscriptionsSettings />}
{activeTab === "folders" && <FoldersSettings />}
{activeTab === "muted-phrases" && <MutedPhrasesSettings />}
diff --git a/apps/web/app/reader/settings/_components/subscriptions-settings.tsx b/apps/web/app/reader/settings/_components/subscriptions-settings.tsx
index 1e9245d..7ac037d 100644
--- a/apps/web/app/reader/settings/_components/subscriptions-settings.tsx
+++ b/apps/web/app/reader/settings/_components/subscriptions-settings.tsx
@@ -4,6 +4,7 @@ import { useState } from "react"
import { useSubscriptions } from "@/lib/queries/use-subscriptions"
import {
useUpdateSubscriptionTitle,
+ useUpdateSubscriptionHiddenFromTimeline,
useUpdateFeedUrl,
useUpdateFeedCredentials,
useAddFeedCredentials,
@@ -50,6 +51,7 @@ function SubscriptionRow({
const [editedAuthType, setEditedAuthType] = useState("bearer")
const [editedCredential, setEditedCredential] = useState("")
const updateTitle = useUpdateSubscriptionTitle()
+ const updateHiddenFromTimeline = useUpdateSubscriptionHiddenFromTimeline()
const updateFeedUrl = useUpdateFeedUrl()
const updateCredentials = useUpdateFeedCredentials()
const addCredentials = useAddFeedCredentials()
@@ -279,6 +281,22 @@ function SubscriptionRow({
</div>
) : null}
<div className="flex flex-wrap items-center gap-2">
+ <button
+ onClick={() =>
+ updateHiddenFromTimeline.mutate({
+ subscriptionIdentifier: subscription.subscriptionIdentifier,
+ hiddenFromTimeline: !subscription.hiddenFromTimeline,
+ })
+ }
+ disabled={updateHiddenFromTimeline.isPending}
+ className={
+ subscription.hiddenFromTimeline
+ ? "px-2 py-1 text-text-dim transition-colors hover:text-text-secondary disabled:opacity-50"
+ : "px-2 py-1 text-text-secondary transition-colors hover:text-text-primary disabled:opacity-50"
+ }
+ >
+ {subscription.hiddenFromTimeline ? "hidden from timeline" : "hide from timeline"}
+ </button>
<select
value={subscription.folderIdentifier ?? ""}
onChange={(event) => handleFolderChange(event.target.value)}
diff --git a/apps/web/lib/queries/use-subscription-mutations.ts b/apps/web/lib/queries/use-subscription-mutations.ts
index ca52bd2..1570167 100644
--- a/apps/web/lib/queries/use-subscription-mutations.ts
+++ b/apps/web/lib/queries/use-subscription-mutations.ts
@@ -5,6 +5,36 @@ import { createSupabaseBrowserClient } from "@/lib/supabase/client"
import { queryKeys } from "./query-keys"
import { notify } from "@/lib/notify"
+export function useUpdateSubscriptionHiddenFromTimeline() {
+ const supabaseClient = createSupabaseBrowserClient()
+ const queryClient = useQueryClient()
+
+ return useMutation({
+ mutationFn: async ({
+ subscriptionIdentifier,
+ hiddenFromTimeline,
+ }: {
+ subscriptionIdentifier: string
+ hiddenFromTimeline: boolean
+ }) => {
+ const { error } = await supabaseClient
+ .from("subscriptions")
+ .update({ hidden_from_timeline: hiddenFromTimeline })
+ .eq("id", subscriptionIdentifier)
+
+ if (error) throw error
+ },
+ onSuccess: (_data, variables) => {
+ queryClient.invalidateQueries({ queryKey: queryKeys.subscriptions.all })
+ queryClient.invalidateQueries({ queryKey: queryKeys.timeline.all })
+ notify(variables.hiddenFromTimeline ? "feed hidden from timeline" : "feed visible in timeline")
+ },
+ onError: (error: Error) => {
+ notify("failed to update visibility: " + error.message)
+ },
+ })
+}
+
export function useUpdateSubscriptionTitle() {
const supabaseClient = createSupabaseBrowserClient()
const queryClient = useQueryClient()
diff --git a/apps/web/lib/queries/use-subscriptions.ts b/apps/web/lib/queries/use-subscriptions.ts
index 2378411..5dc6076 100644
--- a/apps/web/lib/queries/use-subscriptions.ts
+++ b/apps/web/lib/queries/use-subscriptions.ts
@@ -11,6 +11,7 @@ interface SubscriptionRow {
folder_id: string | null
custom_title: string | null
position: number
+ hidden_from_timeline: boolean
feeds: {
title: string | null
url: string
@@ -39,7 +40,7 @@ export function useSubscriptions() {
const [subscriptionsResult, foldersResult] = await Promise.all([
supabaseClient
.from("subscriptions")
- .select("id, feed_id, folder_id, custom_title, position, feeds(title, url, visibility, consecutive_failures, last_fetch_error, last_fetched_at, fetch_interval_seconds, feed_type)")
+ .select("id, feed_id, folder_id, custom_title, position, hidden_from_timeline, feeds(title, url, visibility, consecutive_failures, last_fetch_error, last_fetched_at, fetch_interval_seconds, feed_type)")
.order("position", { ascending: true }),
supabaseClient
.from("folders")
@@ -65,6 +66,7 @@ export function useSubscriptions() {
fetchIntervalSeconds: row.feeds?.fetch_interval_seconds ?? 3600,
feedType: row.feeds?.feed_type ?? null,
feedVisibility: row.feeds?.visibility ?? "public",
+ hiddenFromTimeline: row.hidden_from_timeline,
}))
const folders: Folder[] = (
diff --git a/apps/web/lib/types/subscription.ts b/apps/web/lib/types/subscription.ts
index 0dbc8cb..96f314d 100644
--- a/apps/web/lib/types/subscription.ts
+++ b/apps/web/lib/types/subscription.ts
@@ -18,4 +18,5 @@ export interface Subscription {
fetchIntervalSeconds: number
feedType: string | null
feedVisibility: "public" | "authenticated"
+ hiddenFromTimeline: boolean
}