({
currentPubkey,
fallbackDisplayName,
isUpdatingDesktopNotifications,
notificationErrorMessage,
notificationPermission,
notificationSettings,
onClose,
onSectionChange,
onSetDesktopNotificationsEnabled,
onSetHomeBadgeEnabled,
onSetSlotAlertsEnabled,
onSetNotifyWhileViewing,
onSetAllSlotAlertsEnabled,
onSetSoundForSlot,
section,
}: SettingsViewProps)
| 109 | } |
| 110 | |
| 111 | export function SettingsView({ |
| 112 | currentPubkey, |
| 113 | fallbackDisplayName, |
| 114 | isUpdatingDesktopNotifications, |
| 115 | notificationErrorMessage, |
| 116 | notificationPermission, |
| 117 | notificationSettings, |
| 118 | onClose, |
| 119 | onSectionChange, |
| 120 | onSetDesktopNotificationsEnabled, |
| 121 | onSetHomeBadgeEnabled, |
| 122 | onSetSlotAlertsEnabled, |
| 123 | onSetNotifyWhileViewing, |
| 124 | onSetAllSlotAlertsEnabled, |
| 125 | onSetSoundForSlot, |
| 126 | section, |
| 127 | }: SettingsViewProps) { |
| 128 | const { isMobile, open: sidebarOpen, setOpen: setSidebarOpen } = useSidebar(); |
| 129 | const myMembershipQuery = useMyRelayMembershipQuery(); |
| 130 | const featureState = useFeatureSnapshot(); |
| 131 | const visibleSections = React.useMemo(() => { |
| 132 | const membership = myMembershipQuery.data; |
| 133 | |
| 134 | return settingsSections.filter((s) => { |
| 135 | // Feature gate check. Manifest is preview-only — if the gate id is in |
| 136 | // the manifest, it's preview and needs an opt-in; if it's not, it's |
| 137 | // stable and renders unconditionally (fail-open). |
| 138 | if (s.featureGate) { |
| 139 | const feature = getFeature(s.featureGate); |
| 140 | if (feature && !resolveEnabled(s.featureGate, featureState)) { |
| 141 | return false; |
| 142 | } |
| 143 | } |
| 144 | // Relay members requires admin/owner role |
| 145 | if (s.value === "relay-members") { |
| 146 | return ( |
| 147 | membership != null && |
| 148 | (membership.role === "owner" || membership.role === "admin") |
| 149 | ); |
| 150 | } |
| 151 | return true; |
| 152 | }); |
| 153 | }, [myMembershipQuery.data, featureState]); |
| 154 | |
| 155 | const [isLoaded, setIsLoaded] = React.useState(false); |
| 156 | const [appVersion, setAppVersion] = React.useState<string | null>(null); |
| 157 | |
| 158 | React.useEffect(() => { |
| 159 | const frameId = window.requestAnimationFrame(() => setIsLoaded(true)); |
| 160 | return () => window.cancelAnimationFrame(frameId); |
| 161 | }, []); |
| 162 | |
| 163 | React.useEffect(() => { |
| 164 | void getVersion().then(setAppVersion); |
| 165 | }, []); |
| 166 | |
| 167 | React.useEffect(() => { |
| 168 | if (!visibleSections.some((entry) => entry.value === section)) { |
nothing calls this directly
no test coverage detected