import type { components } from '@pipu/api-client'; import { unwrap } from '@pipu/core'; import { SUPPORTED_LOCALES, type Locale } from '@pipu/i18n'; import Constants from 'expo-constants'; import * as Linking from 'expo-linking'; import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Pressable, ScrollView, Switch, Text, View } from 'react-native'; import { playerCache } from '../../lib/prefs'; import { useSession, useSessionCtx, useUser } from '../../lib/session'; import { updater } from '../../lib/update'; import { downloads } from '../../offline/downloads'; import { player } from '../../player/controller'; import { radius, useColors } from '../../theme'; import { Button, H1, Icon, Muted, Screen, Segmented } from '../../ui/kit'; import { useDownloads } from '../../ui/downloads'; import { PasswordSheet, QualitySheet, useQualityPrefs } from '../../ui/settings'; import { useUpdateState } from '../../ui/UpdateDialog'; type UiPrefs = components['schemas']['UiPrefs']; const MB = 1024 * 1024; export default function ProfileScreen() { const colors = useColors(); const { t, i18n } = useTranslation(); const session = useSession(); const user = useUser(); const { server, disconnect } = useSessionCtx(); const staff = user.role !== 'user'; const update = useUpdateState(); const dl = useDownloads(); const quality = useQualityPrefs(); const [sheet, setSheet] = useState<'quality' | 'password' | null>(null); const [cache, setCache] = useState<{ bytes: number; pending: boolean } | null>(null); useEffect(() => { void Promise.all([playerCache.size(), playerCache.clearPending()]).then(([bytes, pending]) => setCache({ bytes, pending })); }, []); // Оптимистично, как в вебе: настройка применяется сразу, сервер подтверждает. const setPrefs = async (prefs: UiPrefs) => { session.setUser({ ...user, ui_prefs: { ...user.ui_prefs, ...prefs } }); try { session.setUser(unwrap(await session.client.PATCH('/api/v1/me', { body: { ui_prefs: prefs } }))); } catch { session.setUser(user); } }; const clearCache = async () => { await playerCache.requestClear(); setCache((c) => (c ? { ...c, pending: true } : c)); }; const setLocale = async (l: Locale) => { await i18n.changeLanguage(l); try { session.setUser(unwrap(await session.client.PATCH('/api/v1/me', { body: { locale: l } }))); } catch { // язык уже сменился локально } }; const logout = async () => { await player.reset(); await session.logout(); }; return (

{user.display_name ?? user.username}

@{user.username} · {t(`roles.${user.role}`)}
value={(SUPPORTED_LOCALES as readonly string[]).includes(i18n.language) ? (i18n.language as Locale) : 'en'} options={SUPPORTED_LOCALES.map((l) => ({ value: l, label: l.toUpperCase() }))} onChange={(l) => void setLocale(l)} />
> value={user.ui_prefs.theme ?? 'system'} options={[ { value: 'dark', label: t('settings.theme_dark') }, { value: 'light', label: t('settings.theme_light') }, { value: 'system', label: t('settings.theme_system') }, ]} onChange={(v) => void setPrefs({ theme: v })} /> > value={user.ui_prefs.wave_variant ?? 'orb'} options={[ { value: 'orb', label: t('settings.wave_orb') }, { value: 'liquid', label: t('settings.wave_liquid') }, ]} onChange={(v) => void setPrefs({ wave_variant: v })} /> > value={user.ui_prefs.mobile_player ?? 'cover'} options={[ { value: 'cover', label: t('mobile.player_cover') }, { value: 'wave', label: t('mobile.player_wave') }, ]} onChange={(v) => void setPrefs({ mobile_player: v })} />
setSheet('quality')} accessibilityRole="button" style={{ paddingVertical: 4 }}> {t(`mobile.q_${quality.wifi}`)} · {t(`mobile.q_${quality.cellular}`)} {cache?.pending ? ( {t('mobile.cache_pending')} ) : ( void clearCache()} accessibilityRole="button" hitSlop={8}> {t('mobile.cache_clear', { size: ((cache?.bytes ?? 0) / MB).toFixed(0) })} )} void downloads.setWifiOnly(v)} /> void downloads.setAutoLikes(v)} />
{server?.replace(/^https?:\/\//, '')} {staff && server && ( void Linking.openURL(`${server}/admin/users`)} accessibilityRole="link" style={{ flexDirection: 'row', alignItems: 'center', gap: 10, paddingVertical: 12 }}> {t('mobile.open_admin')} )}