feat: per-entity federation privacy toggles for reviews and watchlist
- add federate_reviews + federate_watchlist to UserSettings (default true) - new UserFederationSettingsQuery port with FederationFlags struct - remove get_user_federate_goals from LocalApContentQuery - gate ReviewLogged, ReviewUpdated, WatchlistEntryAdded, on_poster_synced on flags - goals gating migrated to UserFederationSettingsQuery - ReviewDeleted and WatchlistEntryRemoved ungated (tombstones always fire) - sqlite + postgres migrations and adapter impls - settings API and SPA toggles
This commit is contained in:
@@ -18,11 +18,15 @@ export type UpdateGoalRequest = {
|
||||
|
||||
export const userSettingsDtoSchema = z.object({
|
||||
federate_goals: z.boolean(),
|
||||
federate_reviews: z.boolean(),
|
||||
federate_watchlist: z.boolean(),
|
||||
})
|
||||
export type UserSettingsDto = z.infer<typeof userSettingsDtoSchema>
|
||||
|
||||
export type UpdateUserSettingsRequest = {
|
||||
federate_goals: boolean
|
||||
federate_reviews: boolean
|
||||
federate_watchlist: boolean
|
||||
}
|
||||
|
||||
export function getGoals() {
|
||||
|
||||
@@ -178,6 +178,10 @@
|
||||
"privacy": "Privacy",
|
||||
"federateGoals": "Share goals on Fediverse",
|
||||
"federateGoalsDesc": "Broadcast goal progress to followers",
|
||||
"federateReviews": "Share reviews on Fediverse",
|
||||
"federateReviewsDesc": "Broadcast diary entries to followers",
|
||||
"federateWatchlist": "Share watchlist on Fediverse",
|
||||
"federateWatchlistDesc": "Broadcast watchlist additions to followers",
|
||||
"export": "Export",
|
||||
"exportDesc": "Download your diary",
|
||||
"exportCsv": "CSV",
|
||||
|
||||
@@ -3,9 +3,11 @@ import { useTranslation } from "react-i18next"
|
||||
import { useMutation } from "@tanstack/react-query"
|
||||
import {
|
||||
ArrowLeft,
|
||||
BookOpen,
|
||||
ChevronRight,
|
||||
Download,
|
||||
Key,
|
||||
List,
|
||||
LogOut,
|
||||
RefreshCw,
|
||||
ShieldBan,
|
||||
@@ -19,6 +21,7 @@ import { Switch } from "@/components/ui/switch"
|
||||
import { useAuth, useIsAdmin } from "@/components/auth-provider"
|
||||
import { reindexSearch } from "@/lib/api/users"
|
||||
import { useSettings, useUpdateSettings } from "@/hooks/use-goals"
|
||||
import { UpdateUserSettingsRequest } from "@/lib/api/goals"
|
||||
import { useDocumentTitle } from "@/hooks/use-document-title"
|
||||
|
||||
export const Route = createFileRoute("/_app/settings/")({
|
||||
@@ -128,6 +131,18 @@ function PrivacySection() {
|
||||
const { data: settings } = useSettings()
|
||||
const updateMutation = useUpdateSettings()
|
||||
|
||||
const disabled = updateMutation.isPending
|
||||
|
||||
const toggle = (patch: Partial<UpdateUserSettingsRequest>) => {
|
||||
if (!settings) return
|
||||
updateMutation.mutate({
|
||||
federate_goals: settings.federate_goals,
|
||||
federate_reviews: settings.federate_reviews,
|
||||
federate_watchlist: settings.federate_watchlist,
|
||||
...patch,
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p className="mb-1.5 px-1 text-xs font-medium text-muted-foreground">
|
||||
@@ -145,11 +160,41 @@ function PrivacySection() {
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={settings?.federate_goals ?? false}
|
||||
onCheckedChange={(checked) =>
|
||||
updateMutation.mutate({ federate_goals: checked })
|
||||
}
|
||||
disabled={updateMutation.isPending}
|
||||
checked={settings?.federate_goals ?? true}
|
||||
onCheckedChange={(checked) => toggle({ federate_goals: checked })}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 p-3">
|
||||
<span className="text-muted-foreground">
|
||||
<BookOpen className="size-4" />
|
||||
</span>
|
||||
<div className="flex-1">
|
||||
<p className="text-sm font-medium">{t("settings.federateReviews")}</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t("settings.federateReviewsDesc")}
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={settings?.federate_reviews ?? true}
|
||||
onCheckedChange={(checked) => toggle({ federate_reviews: checked })}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 p-3">
|
||||
<span className="text-muted-foreground">
|
||||
<List className="size-4" />
|
||||
</span>
|
||||
<div className="flex-1">
|
||||
<p className="text-sm font-medium">{t("settings.federateWatchlist")}</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t("settings.federateWatchlistDesc")}
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={settings?.federate_watchlist ?? true}
|
||||
onCheckedChange={(checked) => toggle({ federate_watchlist: checked })}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user