Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d235a470c0 | |||
| 8b7a7af323 |
Binary file not shown.
|
Before Width: | Height: | Size: 22 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 14 KiB |
@@ -155,6 +155,7 @@ function NavLinkButton({ item }: { item: NavItem }) {
|
||||
|
||||
function MobileFooterLinks({ onClose }: { onClose: () => void }) {
|
||||
const items = [
|
||||
{ label: 'Settings', to: '/settings' },
|
||||
{ label: 'Privacy', to: '/privacy' },
|
||||
{ label: 'Safety', to: '/safety' },
|
||||
{ label: 'Changelog', to: '/changelog' },
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import { useSeoMeta } from '@unhead/react';
|
||||
import { ChevronDown, ChevronUp, Settings2 } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import { PageHeader } from '@/components/PageHeader';
|
||||
import { IntroImage } from '@/components/IntroImage';
|
||||
import { AdvancedSettings } from '@/components/AdvancedSettings';
|
||||
import { WalletSettings } from '@/components/WalletSettings';
|
||||
import { useCurrentUser } from '@/hooks/useCurrentUser';
|
||||
import { useAppContext } from '@/hooks/useAppContext';
|
||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { ChevronDown, ChevronUp } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
|
||||
export function AdvancedSettingsPage() {
|
||||
const { user } = useCurrentUser();
|
||||
@@ -27,27 +26,21 @@ export function AdvancedSettingsPage() {
|
||||
backTo="/settings"
|
||||
alwaysShowBack
|
||||
titleContent={
|
||||
<div className="flex-1 min-w-0">
|
||||
<h1 className="text-xl font-bold">Advanced</h1>
|
||||
<p className="text-sm text-muted-foreground mt-0.5">
|
||||
Wallet connections, system configuration, and other advanced options for power users.
|
||||
</p>
|
||||
<div className="flex items-center gap-3 flex-1 min-w-0">
|
||||
<div className="flex items-center justify-center size-11 rounded-xl shrink-0 bg-muted">
|
||||
<Settings2 className="size-5 text-muted-foreground" />
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<h1 className="text-xl font-bold">Advanced</h1>
|
||||
<p className="text-sm text-muted-foreground mt-0.5 leading-snug">
|
||||
Wallet, system, and power user settings.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
|
||||
<div className="p-4">
|
||||
{/* Intro */}
|
||||
<div className="flex items-center gap-4 px-3 pt-2 pb-4">
|
||||
<IntroImage src="/advanced-intro.png" />
|
||||
<div className="min-w-0">
|
||||
<h2 className="text-sm font-semibold">Power User Settings</h2>
|
||||
<p className="text-xs text-muted-foreground mt-1 leading-relaxed">
|
||||
Wallet connections, system configuration, and other advanced options.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Wallet collapsible — only when logged in */}
|
||||
{user && (
|
||||
<Collapsible open={walletOpen} onOpenChange={setWalletOpen}>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useSeoMeta } from '@unhead/react';
|
||||
import { Monitor, Moon, Sun } from 'lucide-react';
|
||||
import { Monitor, Moon, Palette, Sun } from 'lucide-react';
|
||||
import { PageHeader } from '@/components/PageHeader';
|
||||
import { IntroImage } from '@/components/IntroImage';
|
||||
import { useAppContext } from '@/hooks/useAppContext';
|
||||
import { useTheme } from '@/hooks/useTheme';
|
||||
import { cn } from '@/lib/utils';
|
||||
@@ -53,27 +52,21 @@ export function AppearanceSettingsPage() {
|
||||
backTo="/settings"
|
||||
alwaysShowBack
|
||||
titleContent={
|
||||
<div className="flex-1 min-w-0">
|
||||
<h1 className="text-xl font-bold">Appearance</h1>
|
||||
<p className="text-sm text-muted-foreground mt-0.5">
|
||||
Choose how the app looks.
|
||||
</p>
|
||||
<div className="flex items-center gap-3 flex-1 min-w-0">
|
||||
<div className="flex items-center justify-center size-11 rounded-xl shrink-0 bg-violet-500/10">
|
||||
<Palette className="size-5 text-violet-500" />
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<h1 className="text-xl font-bold">Appearance</h1>
|
||||
<p className="text-sm text-muted-foreground mt-0.5 leading-snug">
|
||||
Light, dark, or system theme.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
|
||||
<div className="p-4">
|
||||
{/* Intro */}
|
||||
<div className="flex items-center gap-4 px-3 pt-2 pb-6">
|
||||
<IntroImage src="/theme-intro.png" />
|
||||
<div className="min-w-0">
|
||||
<h2 className="text-sm font-semibold">Color Mode</h2>
|
||||
<p className="text-xs text-muted-foreground mt-1 leading-relaxed">
|
||||
Pick your preferred color mode. System will automatically match your device's light or dark setting.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Theme options */}
|
||||
<div className="space-y-2">
|
||||
{themeOptions.map((option) => (
|
||||
|
||||
+11
-18
@@ -1,10 +1,9 @@
|
||||
import { useState } from 'react';
|
||||
import { useSeoMeta } from '@unhead/react';
|
||||
import { RotateCcw } from 'lucide-react';
|
||||
import { Filter, RotateCcw } from 'lucide-react';
|
||||
import { MuteSettingsInternals, SensitiveContentSection } from '@/components/ContentSettings';
|
||||
import { MuteListRecoveryDialog } from '@/components/MuteListRecoveryDialog';
|
||||
import { PageHeader } from '@/components/PageHeader';
|
||||
import { IntroImage } from '@/components/IntroImage';
|
||||
import { HelpTip } from '@/components/HelpTip';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { useAppContext } from '@/hooks/useAppContext';
|
||||
@@ -27,26 +26,20 @@ export function ContentPage() {
|
||||
backTo="/settings"
|
||||
alwaysShowBack
|
||||
titleContent={
|
||||
<div className="flex-1 min-w-0">
|
||||
<h1 className="text-xl font-bold">Content</h1>
|
||||
<p className="text-sm text-muted-foreground mt-0.5">
|
||||
Control what you see. Mute users, hashtags, or words, and choose how content warnings are handled. Mutes are encrypted and private.
|
||||
</p>
|
||||
<div className="flex items-center gap-3 flex-1 min-w-0">
|
||||
<div className="flex items-center justify-center size-11 rounded-xl shrink-0 bg-orange-500/10">
|
||||
<Filter className="size-5 text-orange-500" />
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<h1 className="text-xl font-bold">Content</h1>
|
||||
<p className="text-sm text-muted-foreground mt-0.5 leading-snug">
|
||||
Muted users, hashtags, and content warnings.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
|
||||
{/* Lead image — Muted Content */}
|
||||
<div className="flex items-center gap-4 px-7 py-5">
|
||||
<IntroImage src="/mute-intro.png" size="w-28" />
|
||||
<div className="min-w-0">
|
||||
<h2 className="text-base font-semibold">Content Control</h2>
|
||||
<p className="text-xs text-muted-foreground mt-1 leading-relaxed">
|
||||
Hide posts from specific users, hashtags, words, or entire threads. All mutes are encrypted and private.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-4 space-y-0">
|
||||
|
||||
{/* Muted Content Section */}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useSeoMeta } from '@unhead/react';
|
||||
import { LayoutList } from 'lucide-react';
|
||||
import { ContentSettings } from '@/components/ContentSettings';
|
||||
import { PageHeader } from '@/components/PageHeader';
|
||||
import { HelpTip } from '@/components/HelpTip';
|
||||
@@ -19,11 +20,16 @@ export function ContentSettingsPage() {
|
||||
backTo="/settings"
|
||||
alwaysShowBack
|
||||
titleContent={
|
||||
<div className="flex-1 min-w-0">
|
||||
<h1 className="text-xl font-bold flex items-center gap-1.5">Home Feed <HelpTip faqId="fyp" /></h1>
|
||||
<p className="text-sm text-muted-foreground mt-0.5">
|
||||
Nostr supports many content types beyond text posts. Customize which appear in your home feed.
|
||||
</p>
|
||||
<div className="flex items-center gap-3 flex-1 min-w-0">
|
||||
<div className="flex items-center justify-center size-11 rounded-xl shrink-0 bg-sky-500/10">
|
||||
<LayoutList className="size-5 text-sky-500" />
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<h1 className="text-xl font-bold flex items-center gap-1.5">Home Feed <HelpTip faqId="fyp" /></h1>
|
||||
<p className="text-sm text-muted-foreground mt-0.5 leading-snug">
|
||||
Choose which content types appear in your feed.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useSeoMeta } from '@unhead/react';
|
||||
import { Sparkles } from 'lucide-react';
|
||||
import { PageHeader } from '@/components/PageHeader';
|
||||
import { IntroImage } from '@/components/IntroImage';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { useAppContext } from '@/hooks/useAppContext';
|
||||
@@ -24,34 +24,21 @@ export function MagicSettingsPage() {
|
||||
backTo="/settings"
|
||||
alwaysShowBack
|
||||
titleContent={
|
||||
<div className="flex-1 min-w-0">
|
||||
<h1 className="text-xl font-bold">Magic</h1>
|
||||
<p className="text-sm text-muted-foreground mt-0.5">
|
||||
Harness the mystical energies of your device. Imbue your cursor with elemental fire.
|
||||
</p>
|
||||
<div className="flex items-center gap-3 flex-1 min-w-0">
|
||||
<div className="flex items-center justify-center size-11 rounded-xl shrink-0 bg-primary/10">
|
||||
<Sparkles className="size-5 text-primary" />
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<h1 className="text-xl font-bold">Magic</h1>
|
||||
<p className="text-sm text-muted-foreground mt-0.5 leading-snug">
|
||||
Enchanted cursor effects.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
|
||||
<div className="p-4">
|
||||
{/* Intro */}
|
||||
<div className="flex items-center gap-4 px-3 pt-2 pb-6">
|
||||
<IntroImage src="/magic-intro.png" />
|
||||
<div className="min-w-0">
|
||||
<h2 className="text-sm font-semibold">Arcane Configuration</h2>
|
||||
<p className="text-xs text-muted-foreground mt-1 leading-relaxed">
|
||||
Harness the mystical energies of your device. Imbue your cursor with elemental fire and make every interaction feel enchanted.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Ornament */}
|
||||
<div className="flex items-center gap-3 px-2 pb-5">
|
||||
<div className="h-px flex-1 bg-gradient-to-r from-transparent via-primary/40 to-primary/60" />
|
||||
<span className="text-primary/50 text-xs tracking-[0.3em] select-none">✦</span>
|
||||
<div className="h-px flex-1 bg-gradient-to-l from-transparent via-primary/40 to-primary/60" />
|
||||
</div>
|
||||
|
||||
{/* Magic Mouse toggle */}
|
||||
<div
|
||||
className="flex items-start gap-4 rounded-xl px-4 py-4 transition-colors hover:bg-muted/40"
|
||||
@@ -73,12 +60,6 @@ export function MagicSettingsPage() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Bottom ornament */}
|
||||
<div className="flex items-center gap-3 px-2 pt-6 pb-4">
|
||||
<div className="h-px flex-1 bg-gradient-to-r from-transparent via-primary/20 to-primary/30" />
|
||||
<span className="text-primary/30 text-[10px] tracking-[0.4em] select-none">◆</span>
|
||||
<div className="h-px flex-1 bg-gradient-to-l from-transparent via-primary/20 to-primary/30" />
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useSeoMeta } from '@unhead/react';
|
||||
import { Radio } from 'lucide-react';
|
||||
import { Navigate } from 'react-router-dom';
|
||||
import { PageHeader } from '@/components/PageHeader';
|
||||
import { RelayListManager } from '@/components/RelayListManager';
|
||||
import { BlossomSettings } from '@/components/BlossomSettings';
|
||||
import { IntroImage } from '@/components/IntroImage';
|
||||
import { HelpTip } from '@/components/HelpTip';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { useCurrentUser } from '@/hooks/useCurrentUser';
|
||||
@@ -30,26 +30,21 @@ export function NetworkSettingsPage() {
|
||||
backTo="/settings"
|
||||
alwaysShowBack
|
||||
titleContent={
|
||||
<div className="flex-1 min-w-0">
|
||||
<h1 className="text-xl font-bold flex items-center gap-1.5">Network <HelpTip faqId="what-is-nostr" /></h1>
|
||||
<p className="text-sm text-muted-foreground mt-0.5">
|
||||
Relays are servers that store and distribute content across the Nostr network. Blossom servers handle file uploads.
|
||||
</p>
|
||||
<div className="flex items-center gap-3 flex-1 min-w-0">
|
||||
<div className="flex items-center justify-center size-11 rounded-xl shrink-0 bg-emerald-500/10">
|
||||
<Radio className="size-5 text-emerald-500" />
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<h1 className="text-xl font-bold flex items-center gap-1.5">Network <HelpTip faqId="what-is-nostr" /></h1>
|
||||
<p className="text-sm text-muted-foreground mt-0.5 leading-snug">
|
||||
Relays and file upload servers.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
|
||||
<div className="p-4">
|
||||
{/* Intro */}
|
||||
<div className="flex items-center gap-4 px-3 pt-2 pb-4">
|
||||
<IntroImage src="/relay-intro.png" />
|
||||
<div className="min-w-0">
|
||||
<h2 className="text-sm font-semibold">Network Connections</h2>
|
||||
<p className="text-xs text-muted-foreground mt-1 leading-relaxed">
|
||||
Manage your relay connections. Relays are servers that store and distribute Nostr events across the network.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Relays */}
|
||||
<div>
|
||||
|
||||
@@ -272,11 +272,16 @@ export function NotificationSettings() {
|
||||
backTo="/settings"
|
||||
alwaysShowBack
|
||||
titleContent={
|
||||
<div className="flex-1 min-w-0">
|
||||
<h1 className="text-xl font-bold">Notifications</h1>
|
||||
<p className="text-sm text-muted-foreground mt-0.5">
|
||||
Customize which notifications you receive.
|
||||
</p>
|
||||
<div className="flex items-center gap-3 flex-1 min-w-0">
|
||||
<div className="flex items-center justify-center size-11 rounded-xl shrink-0 bg-yellow-500/10">
|
||||
<Bell className="size-5 text-yellow-500" />
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<h1 className="text-xl font-bold">Notifications</h1>
|
||||
<p className="text-sm text-muted-foreground mt-0.5 leading-snug">
|
||||
Choose when and how Agora alerts you.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useSeoMeta } from '@unhead/react';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import {
|
||||
Loader2, Plus, Trash2, ChevronDown,
|
||||
Wallet, Upload, Music, ImageIcon, Film, Mail, Link2, Pencil, Eye, EyeOff, Copy, Check, Download, KeyRound, AlertTriangle, CloudSun,
|
||||
Wallet, Upload, Music, ImageIcon, Film, Mail, Link2, Pencil, Eye, EyeOff, Copy, Check, Download, KeyRound, AlertTriangle, CloudSun, User,
|
||||
} from 'lucide-react';
|
||||
import { nip19 } from 'nostr-tools';
|
||||
import { useNostrLogin } from '@nostrify/react/login';
|
||||
@@ -20,7 +20,6 @@ import { useQueryClient } from '@tanstack/react-query';
|
||||
import { ProfileCard } from '@/components/ProfileCard';
|
||||
import { ProfileRightSidebar } from '@/components/ProfileRightSidebar';
|
||||
import { PageHeader } from '@/components/PageHeader';
|
||||
import { IntroImage } from '@/components/IntroImage';
|
||||
import { HelpTip } from '@/components/HelpTip';
|
||||
import { ImageCropDialog } from '@/components/ImageCropDialog';
|
||||
import { SortableList, SortableItem } from '@/components/SortableList';
|
||||
@@ -686,9 +685,14 @@ export function ProfileSettings() {
|
||||
backTo="/settings"
|
||||
alwaysShowBack
|
||||
titleContent={
|
||||
<div className="flex-1 min-w-0">
|
||||
<h1 className="text-xl font-bold leading-tight">Profile</h1>
|
||||
<p className="text-sm text-muted-foreground">Your Nostr identity is portable — it goes wherever you go.</p>
|
||||
<div className="flex items-center gap-3 flex-1 min-w-0">
|
||||
<div className="flex items-center justify-center size-11 rounded-xl shrink-0 bg-primary/10">
|
||||
<User className="size-5 text-primary" />
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<h1 className="text-xl font-bold leading-tight">Profile</h1>
|
||||
<p className="text-sm text-muted-foreground mt-0.5 leading-snug">Edit your display name, bio, and avatar.</p>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
@@ -700,17 +704,6 @@ export function ProfileSettings() {
|
||||
<Form {...form}>
|
||||
<form id="profile-settings-form" onSubmit={form.handleSubmit(onSubmit)} className="max-w-xl mx-auto px-4 pb-10 space-y-6">
|
||||
|
||||
{/* Intro */}
|
||||
<div className="flex items-center gap-4 px-3 pt-2 pb-2">
|
||||
<IntroImage src="/profile-intro.png" />
|
||||
<div className="min-w-0">
|
||||
<h2 className="text-sm font-semibold">Your Identity</h2>
|
||||
<p className="text-xs text-muted-foreground mt-1 leading-relaxed">
|
||||
Tap any field on the card to edit. Click your avatar or banner to upload and crop a new image.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Interactive profile card */}
|
||||
<ProfileCard
|
||||
pubkey={user.pubkey}
|
||||
|
||||
+58
-30
@@ -1,6 +1,18 @@
|
||||
import { useSeoMeta } from '@unhead/react';
|
||||
import { lazy, Suspense, useState, useEffect, useRef } from 'react';
|
||||
import { ChevronRight, Settings } from 'lucide-react';
|
||||
import { lazy, Suspense, useState, useEffect, useRef, type ComponentType } from 'react';
|
||||
import {
|
||||
Bell,
|
||||
ChevronRight,
|
||||
Crown,
|
||||
Filter,
|
||||
Palette,
|
||||
Radio,
|
||||
Settings,
|
||||
Settings2,
|
||||
Sparkles,
|
||||
User,
|
||||
LayoutList,
|
||||
} from 'lucide-react';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import { PageHeader } from '@/components/PageHeader';
|
||||
import { useCurrentUser } from '@/hooks/useCurrentUser';
|
||||
@@ -9,6 +21,7 @@ import { IntroImage } from '@/components/IntroImage';
|
||||
import { useLayoutOptions } from '@/contexts/LayoutContext';
|
||||
import { toast } from '@/hooks/useToast';
|
||||
import { isAdmin } from '@/lib/admins';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const RequestToVanishDialog = lazy(() => import('@/components/RequestToVanishDialog').then(m => ({ default: m.RequestToVanishDialog })));
|
||||
|
||||
@@ -16,6 +29,11 @@ interface SettingsSection {
|
||||
id: string;
|
||||
label: string;
|
||||
description: string;
|
||||
icon: ComponentType<{ className?: string }>;
|
||||
/** Colour applied to the icon circle background (Tailwind bg-* class). */
|
||||
iconBg: string;
|
||||
/** Colour applied to the icon itself (Tailwind text-* class). */
|
||||
iconColor: string;
|
||||
illustration?: string;
|
||||
path: string;
|
||||
requiresAuth?: boolean;
|
||||
@@ -28,6 +46,9 @@ const settingsSections: SettingsSection[] = [
|
||||
id: 'profile',
|
||||
label: 'Profile',
|
||||
description: 'Edit your display name, bio, and avatar',
|
||||
icon: User,
|
||||
iconBg: 'bg-primary/10',
|
||||
iconColor: 'text-primary',
|
||||
illustration: '/profile-intro.png',
|
||||
path: '/settings/profile',
|
||||
requiresAuth: true,
|
||||
@@ -35,21 +56,30 @@ const settingsSections: SettingsSection[] = [
|
||||
{
|
||||
id: 'appearance',
|
||||
label: 'Appearance',
|
||||
description: 'Switch between system, light, and dark mode',
|
||||
description: 'Switch between light, dark, and system themes',
|
||||
icon: Palette,
|
||||
iconBg: 'bg-violet-500/10',
|
||||
iconColor: 'text-violet-500',
|
||||
illustration: '/theme-intro.png',
|
||||
path: '/settings/appearance',
|
||||
},
|
||||
{
|
||||
id: 'feed',
|
||||
label: 'Home Feed',
|
||||
description: 'Choose what types of posts appear in your home feed',
|
||||
description: 'Tailor your feed to the causes and content you care about',
|
||||
icon: LayoutList,
|
||||
iconBg: 'bg-sky-500/10',
|
||||
iconColor: 'text-sky-500',
|
||||
illustration: '/community-intro.png',
|
||||
path: '/settings/feed',
|
||||
},
|
||||
{
|
||||
id: 'content',
|
||||
label: 'Content',
|
||||
description: 'Muted users, hashtags, and sensitive content settings',
|
||||
description: 'Muted accounts, hashtags, and sensitive content filters',
|
||||
icon: Filter,
|
||||
iconBg: 'bg-orange-500/10',
|
||||
iconColor: 'text-orange-500',
|
||||
illustration: '/mute-intro.png',
|
||||
path: '/settings/content',
|
||||
},
|
||||
@@ -57,6 +87,9 @@ const settingsSections: SettingsSection[] = [
|
||||
id: 'network',
|
||||
label: 'Network',
|
||||
description: 'Relays and file upload servers',
|
||||
icon: Radio,
|
||||
iconBg: 'bg-emerald-500/10',
|
||||
iconColor: 'text-emerald-500',
|
||||
illustration: '/relay-intro.png',
|
||||
path: '/settings/network',
|
||||
requiresAuth: true,
|
||||
@@ -65,6 +98,9 @@ const settingsSections: SettingsSection[] = [
|
||||
id: 'notifications',
|
||||
label: 'Notifications',
|
||||
description: 'Configure push notification preferences',
|
||||
icon: Bell,
|
||||
iconBg: 'bg-yellow-500/10',
|
||||
iconColor: 'text-yellow-500',
|
||||
illustration: '/notification-intro.png',
|
||||
path: '/settings/notifications',
|
||||
requiresAuth: true,
|
||||
@@ -73,6 +109,9 @@ const settingsSections: SettingsSection[] = [
|
||||
id: 'advanced',
|
||||
label: 'Advanced',
|
||||
description: 'Wallet, system, and power user settings',
|
||||
icon: Settings2,
|
||||
iconBg: 'bg-muted',
|
||||
iconColor: 'text-muted-foreground',
|
||||
illustration: '/advanced-intro.png',
|
||||
path: '/settings/advanced',
|
||||
},
|
||||
@@ -80,6 +119,9 @@ const settingsSections: SettingsSection[] = [
|
||||
id: 'magic',
|
||||
label: 'Magic',
|
||||
description: 'Enchanted cursor effects and mystical interface powers',
|
||||
icon: Sparkles,
|
||||
iconBg: 'bg-primary/10',
|
||||
iconColor: 'text-primary',
|
||||
illustration: '/magic-intro.png',
|
||||
path: '/settings/magic',
|
||||
},
|
||||
@@ -87,6 +129,9 @@ const settingsSections: SettingsSection[] = [
|
||||
id: 'organizers',
|
||||
label: 'Organizers',
|
||||
description: 'Appoint country organizers who can pin posts to country feeds',
|
||||
icon: Crown,
|
||||
iconBg: 'bg-amber-500/10',
|
||||
iconColor: 'text-amber-500',
|
||||
illustration: '/community-intro.png',
|
||||
path: '/organizers',
|
||||
requiresAuth: true,
|
||||
@@ -144,19 +189,11 @@ export function SettingsPage() {
|
||||
{/* Page header */}
|
||||
<PageHeader title="Settings" icon={<Settings className="size-5" />} backTo="/" />
|
||||
|
||||
{/* Codex heading + exposition */}
|
||||
<div className="px-7 pb-4 pt-4 text-center space-y-2.5">
|
||||
<p className="text-xs text-muted-foreground leading-relaxed select-none">
|
||||
Shape your identity, tune your feed, and manage how you connect to the Nostr network.<br />Everything you need to make this place feel like yours.
|
||||
{/* Intro */}
|
||||
<div className="px-7 pb-6 pt-4 text-center">
|
||||
<p className="text-sm text-muted-foreground leading-relaxed select-none">
|
||||
Manage your profile, privacy, and how Agora works for you.
|
||||
</p>
|
||||
<p className="text-[10px] tracking-[0.5em] uppercase text-primary/60 select-none pt-6">Codex of Configuration</p>
|
||||
</div>
|
||||
|
||||
{/* Tome ornament */}
|
||||
<div className="flex items-center gap-3 px-6 pb-5">
|
||||
<div className="h-px flex-1 bg-gradient-to-r from-transparent via-primary/40 to-primary/60" />
|
||||
<span className="text-primary/50 text-xs tracking-[0.3em] select-none">✦</span>
|
||||
<div className="h-px flex-1 bg-gradient-to-l from-transparent via-primary/40 to-primary/60" />
|
||||
</div>
|
||||
|
||||
{/* Settings menu */}
|
||||
@@ -165,13 +202,11 @@ export function SettingsPage() {
|
||||
return (
|
||||
<div key={section.id}>
|
||||
<div
|
||||
className="flex items-center gap-4 px-3 py-2 my-1 cursor-pointer rounded-xl transition-colors hover:bg-muted/60 active:bg-muted/80 group"
|
||||
className="flex items-center gap-4 px-3 py-3 my-0.5 cursor-pointer rounded-xl transition-colors hover:bg-muted/60 active:bg-muted/80 group"
|
||||
onClick={() => navigate(section.path)}
|
||||
>
|
||||
<div className="flex items-center justify-center size-20 shrink-0">
|
||||
{section.illustration && (
|
||||
<IntroImage src={section.illustration} size="w-22" />
|
||||
)}
|
||||
<div className={cn('flex items-center justify-center size-11 rounded-xl shrink-0', section.iconBg)}>
|
||||
<section.icon className={cn('size-5', section.iconColor)} />
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm font-semibold">{section.label}</p>
|
||||
@@ -179,7 +214,7 @@ export function SettingsPage() {
|
||||
{section.description}
|
||||
</p>
|
||||
</div>
|
||||
<ChevronRight className="size-4 text-primary/40 shrink-0 group-hover:text-primary/70 transition-colors" strokeWidth={4} />
|
||||
<ChevronRight className="size-4 text-muted-foreground/40 shrink-0 group-hover:text-primary/70 transition-colors" strokeWidth={2.5} />
|
||||
</div>
|
||||
{i < visibleSections.length - 1 && (
|
||||
<div className="mx-6 h-px bg-primary/10" />
|
||||
@@ -207,13 +242,6 @@ export function SettingsPage() {
|
||||
</Suspense>
|
||||
)}
|
||||
|
||||
{/* Bottom ornament */}
|
||||
<div className="flex items-center gap-3 px-6 pt-4 pb-2">
|
||||
<div className="h-px flex-1 bg-gradient-to-r from-transparent via-primary/20 to-primary/30" />
|
||||
<span className="text-primary/30 text-[10px] tracking-[0.4em] select-none">◆</span>
|
||||
<div className="h-px flex-1 bg-gradient-to-l from-transparent via-primary/20 to-primary/30" />
|
||||
</div>
|
||||
|
||||
{/* Version footer */}
|
||||
<Link to="/changelog" className="block text-center text-[11px] text-muted-foreground/50 hover:text-muted-foreground transition-colors select-none pt-1 pb-2">
|
||||
v{import.meta.env.VERSION}{import.meta.env.COMMIT_TAG ? '' : '+'} ({new Date(import.meta.env.BUILD_DATE).toLocaleDateString()})
|
||||
|
||||
Reference in New Issue
Block a user