()
| 70 | }; |
| 71 | |
| 72 | export const IOSIconPicker = (): ReactElement => { |
| 73 | const [appIcons, setAppIcons] = useState<AppIconOption[]>([]); |
| 74 | const [selectedIcon, setSelectedIcon] = useState<string | null>(null); |
| 75 | const [supportsAlternateIcons, setSupportsAlternateIcons] = useState(false); |
| 76 | const [appIconLoading, setAppIconLoading] = useState(false); |
| 77 | const [appIconError, setAppIconError] = useState<string | null>(null); |
| 78 | |
| 79 | const appIconOptions = useMemo<RadioItemProps[]>(() => { |
| 80 | return appIcons.map((icon) => { |
| 81 | const iconName = icon.name ?? 'main'; |
| 82 | const previewUrl = resolveAppIconPreview(iconName); |
| 83 | return { |
| 84 | label: ( |
| 85 | <span className="flex items-center gap-2"> |
| 86 | <span className="border-border-subtle h-7 w-7 overflow-hidden rounded-10 border bg-surface-primary"> |
| 87 | <img |
| 88 | src={previewUrl} |
| 89 | alt={`${icon.displayName} app icon`} |
| 90 | className="h-full w-full object-cover" |
| 91 | loading="lazy" |
| 92 | /> |
| 93 | </span> |
| 94 | <span>{icon.displayName}</span> |
| 95 | </span> |
| 96 | ), |
| 97 | value: icon.name ?? 'default', |
| 98 | }; |
| 99 | }); |
| 100 | }, [appIcons]); |
| 101 | |
| 102 | const parseEventDetail = <T,>(rawDetail: unknown): T | null => { |
| 103 | if (typeof rawDetail === 'string') { |
| 104 | return parseOrDefault<T>(rawDetail) as T; |
| 105 | } |
| 106 | return rawDetail as T; |
| 107 | }; |
| 108 | |
| 109 | const handleGetIconResult = (detail: AppIconResult): void => { |
| 110 | const selected = |
| 111 | detail.selectedName ?? |
| 112 | detail.icons?.find((icon) => icon.isSelected)?.name ?? |
| 113 | null; |
| 114 | |
| 115 | setAppIcons(detail.icons ?? []); |
| 116 | setSelectedIcon(selected); |
| 117 | setSupportsAlternateIcons(!!detail.supportsAlternateIcons); |
| 118 | setAppIconError(null); |
| 119 | setAppIconLoading(false); |
| 120 | }; |
| 121 | |
| 122 | const handleSetIconResult = (detail: AppIconResult): void => { |
| 123 | setSelectedIcon(detail.selectedName ?? null); |
| 124 | setAppIconError(null); |
| 125 | setAppIconLoading(false); |
| 126 | }; |
| 127 | |
| 128 | const handleAppIconResult = useCallback((event: Event) => { |
| 129 | const detail = parseEventDetail<AppIconResult>( |
nothing calls this directly
no test coverage detected