index.tsx raw
1 import { Button } from '@/components/ui/button'
2 import { Label } from '@/components/ui/label'
3 import { Switch } from '@/components/ui/switch'
4 import { useSocialGraphFilter } from '@/providers/SocialGraphFilterProvider'
5 import { Loader2, Minus, Plus } from 'lucide-react'
6 import { useTranslation } from 'react-i18next'
7
8 const DEPTH_LABELS: Record<number, string> = {
9 1: 'Direct follows',
10 2: 'Follows of follows'
11 }
12
13 interface SocialGraphFilterProps {
14 temporaryProximity: number | null
15 temporaryIncludeMode: boolean
16 onTemporaryProximityChange: (level: number | null) => void
17 onTemporaryIncludeModeChange: (include: boolean) => void
18 }
19
20 export default function SocialGraphFilter({
21 temporaryProximity,
22 temporaryIncludeMode,
23 onTemporaryProximityChange,
24 onTemporaryIncludeModeChange
25 }: SocialGraphFilterProps) {
26 const { t } = useTranslation()
27 const { graphPubkeyCount, isLoading } = useSocialGraphFilter()
28
29 const isEnabled = temporaryProximity !== null
30 const depth = temporaryProximity ?? 1
31
32 const handleToggle = (enabled: boolean) => {
33 onTemporaryProximityChange(enabled ? 1 : null)
34 }
35
36 const handleIncrease = () => {
37 if (depth < 2) {
38 onTemporaryProximityChange(depth + 1)
39 }
40 }
41
42 const handleDecrease = () => {
43 if (depth > 1) {
44 onTemporaryProximityChange(depth - 1)
45 }
46 }
47
48 return (
49 <div className="space-y-3">
50 <div className="flex items-center justify-between">
51 <Label htmlFor="social-graph-filter" className="font-medium">
52 {t('Social graph filter')}
53 </Label>
54 <Switch id="social-graph-filter" checked={isEnabled} onCheckedChange={handleToggle} />
55 </div>
56
57 {isEnabled && (
58 <>
59 {/* Include/Exclude toggle */}
60 <div className="flex items-center gap-2">
61 <Button
62 variant={temporaryIncludeMode ? 'default' : 'outline'}
63 size="sm"
64 className="flex-1"
65 onClick={() => onTemporaryIncludeModeChange(true)}
66 >
67 {t('Include')}
68 </Button>
69 <Button
70 variant={!temporaryIncludeMode ? 'default' : 'outline'}
71 size="sm"
72 className="flex-1"
73 onClick={() => onTemporaryIncludeModeChange(false)}
74 >
75 {t('Exclude')}
76 </Button>
77 </div>
78
79 {/* Depth stepper */}
80 <div className="flex items-center justify-between rounded-lg border px-3 py-2">
81 <div className="flex-1">
82 <p className="text-sm font-medium">{t(DEPTH_LABELS[depth])}</p>
83 <p className="text-xs text-muted-foreground">
84 {isLoading ? (
85 <span className="flex items-center gap-1">
86 <Loader2 className="h-3 w-3 animate-spin" />
87 {t('Loading...')}
88 </span>
89 ) : (
90 t('{{count}} users', { count: graphPubkeyCount })
91 )}
92 </p>
93 </div>
94 <div className="flex items-center gap-1">
95 <Button
96 variant="outline"
97 size="icon"
98 className="h-8 w-8"
99 onClick={handleDecrease}
100 disabled={depth <= 1}
101 >
102 <Minus className="h-4 w-4" />
103 </Button>
104 <span className="w-6 text-center text-sm font-medium">{depth}</span>
105 <Button
106 variant="outline"
107 size="icon"
108 className="h-8 w-8"
109 onClick={handleIncrease}
110 disabled={depth >= 2}
111 >
112 <Plus className="h-4 w-4" />
113 </Button>
114 </div>
115 </div>
116
117 {/* Mode description */}
118 <p className="text-xs text-muted-foreground">
119 {temporaryIncludeMode
120 ? t('Only show notes from users in your social graph')
121 : t('Hide notes from users in your social graph')}
122 </p>
123 </>
124 )}
125 </div>
126 )
127 }
128