mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-04-11 16:07:15 +02:00
* feat(settings): add relay lists management section - Fetch additional relay list kinds (10006, 10007, 10050) on login - Add "Relays" tab to Settings with accordion UI for each relay list kind - Support NIP-65 relay list (kind 10002) with read/write markers - Support blocked relays (10006), search relays (10007), DM relays (10050) - Add/remove relays with URL sanitization and normalization - Explicit save button publishes only modified lists as replaceable events https://claude.ai/code/session_01JHirYU56sKDKYhRx6aCQ54 * docs: add plan for honoring blocked & search relay lists Detailed implementation plan for: - Kind 10006: filter blocked relays from all connection paths - Kind 10007: use search relays for NIP-50 queries https://claude.ai/code/session_01JHirYU56sKDKYhRx6aCQ54 * refactor(settings): extract relay list logic into tested lib, fix UX issues Extract parseRelayEntries, buildRelayListTags, sanitizeRelayInput, and comparison/mode helpers into src/lib/relay-list-utils.ts with 52 tests covering roundtrips, normalization, edge cases, and mode conversions. UX fixes: - Replace RelayLink (navigates away on click) with static RelaySettingsRow - Remove redundant inbox/outbox icons (mode dropdown is sufficient) - Always-visible delete button instead of hover-only opacity - Per-accordion dirty indicator (CircleDot icon) for modified lists - Discard button to reset all changes - Read-only account explanation text - Human-friendly descriptions (no NIP references or kind numbers) - Separator between relay list and add input - Larger relay icons and text for readability https://claude.ai/code/session_01JHirYU56sKDKYhRx6aCQ54 * feat(settings): use KindBadge and NIPBadge in relay list accordions Replace plain text kind names with KindBadge (full variant showing icon, name, and kind number) and add NIPBadge next to each list description. This gives power users the protocol context they expect. Also document KindBadge and NIPBadge as shared components in CLAUDE.md. https://claude.ai/code/session_01JHirYU56sKDKYhRx6aCQ54 * feat(settings): add favorite relays list (kind 10012) to relay settings Add kind 10012 (Favorite Relays / Relay Feeds) to the settings UI and account sync fetching. Uses "relay" tags like other NIP-51 lists. https://claude.ai/code/session_01JHirYU56sKDKYhRx6aCQ54 * fix(kinds): use semantic icons for blocked and search relay lists - Kind 10006 (Blocked Relays): Radio → ShieldBan - Kind 10007 (Search Relays): Radio → Search These icons propagate to KindBadge, settings accordions, and event renderers via getKindInfo(). Generic relay kinds (10002, 30002, etc.) keep the Radio icon. https://claude.ai/code/session_01JHirYU56sKDKYhRx6aCQ54 --------- Co-authored-by: Claude <noreply@anthropic.com>
159 lines
5.7 KiB
TypeScript
159 lines
5.7 KiB
TypeScript
import { Tabs, TabsContent, TabsList, TabsTrigger } from "./ui/tabs";
|
|
import {
|
|
Select,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
} from "./ui/select";
|
|
import { Switch } from "./ui/switch";
|
|
import { useSettings } from "@/hooks/useSettings";
|
|
import { useTheme } from "@/lib/themes";
|
|
import { Palette, FileEdit, Radio } from "lucide-react";
|
|
import { RelayListsSettings } from "./settings/RelayListsSettings";
|
|
|
|
export function SettingsViewer() {
|
|
const { settings, updateSetting } = useSettings();
|
|
const { themeId, setTheme, availableThemes } = useTheme();
|
|
|
|
return (
|
|
<div className="h-full flex flex-col">
|
|
<Tabs defaultValue="appearance" className="flex-1 flex flex-col">
|
|
<div className="border-b px-6 py-3">
|
|
<TabsList>
|
|
<TabsTrigger value="appearance" className="gap-2">
|
|
<Palette className="h-4 w-4" />
|
|
Appearance
|
|
</TabsTrigger>
|
|
<TabsTrigger value="post" className="gap-2">
|
|
<FileEdit className="h-4 w-4" />
|
|
Post
|
|
</TabsTrigger>
|
|
<TabsTrigger value="relays" className="gap-2">
|
|
<Radio className="h-4 w-4" />
|
|
Relays
|
|
</TabsTrigger>
|
|
</TabsList>
|
|
</div>
|
|
|
|
<div className="flex-1 overflow-y-auto">
|
|
<TabsContent value="appearance" className="m-0 p-6 space-y-6">
|
|
<div>
|
|
<h3 className="text-lg font-semibold mb-1">Appearance</h3>
|
|
<p className="text-sm text-muted-foreground">
|
|
Customize display preferences
|
|
</p>
|
|
</div>
|
|
|
|
<div className="space-y-6">
|
|
<div className="flex items-center justify-between gap-4">
|
|
<div className="space-y-0.5">
|
|
<label
|
|
htmlFor="theme"
|
|
className="text-base font-medium cursor-pointer"
|
|
>
|
|
Theme
|
|
</label>
|
|
<p className="text-xs text-muted-foreground">
|
|
Choose your color scheme
|
|
</p>
|
|
</div>
|
|
<Select value={themeId} onValueChange={setTheme}>
|
|
<SelectTrigger id="theme" className="w-32">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
{availableThemes.map((theme) => (
|
|
<SelectItem key={theme.id} value={theme.id}>
|
|
{theme.name}
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
|
|
<div className="flex items-center justify-between gap-4">
|
|
<div className="space-y-0.5">
|
|
<label
|
|
htmlFor="show-client-tags"
|
|
className="text-base font-medium cursor-pointer"
|
|
>
|
|
Show client tags
|
|
</label>
|
|
<p className="text-xs text-muted-foreground">
|
|
Display client identifiers in events
|
|
</p>
|
|
</div>
|
|
<Switch
|
|
id="show-client-tags"
|
|
checked={settings?.appearance?.showClientTags ?? true}
|
|
onCheckedChange={(checked: boolean) =>
|
|
updateSetting("appearance", "showClientTags", checked)
|
|
}
|
|
/>
|
|
</div>
|
|
|
|
<div className="flex items-center justify-between gap-4">
|
|
<div className="space-y-0.5">
|
|
<label
|
|
htmlFor="load-media"
|
|
className="text-base font-medium cursor-pointer"
|
|
>
|
|
Load media
|
|
</label>
|
|
<p className="text-xs text-muted-foreground">
|
|
Render links to media as inline images, videos, and audio
|
|
</p>
|
|
</div>
|
|
<Switch
|
|
id="load-media"
|
|
checked={settings?.appearance?.loadMedia ?? true}
|
|
onCheckedChange={(checked: boolean) =>
|
|
updateSetting("appearance", "loadMedia", checked)
|
|
}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</TabsContent>
|
|
|
|
<TabsContent value="post" className="m-0 p-6 space-y-6">
|
|
<div>
|
|
<h3 className="text-lg font-semibold mb-1">Post Settings</h3>
|
|
<p className="text-sm text-muted-foreground">
|
|
Configure event publishing
|
|
</p>
|
|
</div>
|
|
|
|
<div className="space-y-4">
|
|
<div className="flex items-center justify-between gap-4">
|
|
<div className="space-y-0.5">
|
|
<label
|
|
htmlFor="include-client-tag"
|
|
className="text-base font-medium cursor-pointer"
|
|
>
|
|
Include client tag
|
|
</label>
|
|
<p className="text-xs text-muted-foreground">
|
|
Add Grimoire tag to published events
|
|
</p>
|
|
</div>
|
|
<Switch
|
|
id="include-client-tag"
|
|
checked={settings?.post?.includeClientTag ?? true}
|
|
onCheckedChange={(checked: boolean) =>
|
|
updateSetting("post", "includeClientTag", checked)
|
|
}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</TabsContent>
|
|
|
|
<TabsContent value="relays" className="m-0 p-6 space-y-6">
|
|
<RelayListsSettings />
|
|
</TabsContent>
|
|
</div>
|
|
</Tabs>
|
|
</div>
|
|
);
|
|
}
|