feat(settings): catalog-driven index + migrate QuickConnect to expo-ui sheet

This commit is contained in:
Gauvain
2026-06-03 23:27:46 +02:00
parent 25c0663d2f
commit 4e3f5a72e0
3 changed files with 133 additions and 161 deletions

View File

@@ -1,38 +1,37 @@
import { useNavigation } from "expo-router";
import { t } from "i18next";
import { useAtom } from "jotai";
import { useEffect } from "react";
import { useEffect, useRef } from "react";
import { Platform, ScrollView, TouchableOpacity, View } from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { Text } from "@/components/common/Text";
import { ListGroup } from "@/components/list/ListGroup";
import { ListItem } from "@/components/list/ListItem";
import { AppLanguageSelector } from "@/components/settings/AppLanguageSelector";
import { QuickConnect } from "@/components/settings/QuickConnect";
import { SettingsRow } from "@/components/settings/index/SettingsRow";
import { SettingsSection } from "@/components/settings/index/SettingsSection";
import {
SETTINGS_CATALOG,
type SettingsEntry,
} from "@/components/settings/index/settingsCatalog";
import { QuickConnectSheet } from "@/components/settings/QuickConnect";
import { StorageSettings } from "@/components/settings/StorageSettings";
import { UserInfo } from "@/components/settings/UserInfo";
import useRouter from "@/hooks/useAppRouter";
import { useJellyfin, userAtom } from "@/providers/JellyfinProvider";
import { useJellyfin } from "@/providers/JellyfinProvider";
import type { BottomSheetMethods } from "@/utils/expoUiBottomSheet";
// TV-specific settings component
const SettingsTV = Platform.isTV ? require("./settings.tv").default : null;
// Mobile settings component
function SettingsMobile() {
const router = useRouter();
const insets = useSafeAreaInsets();
const [_user] = useAtom(userAtom);
const { logout } = useJellyfin();
const navigation = useNavigation();
const quickConnectRef = useRef<BottomSheetMethods>(null);
const os: "ios" | "android" = Platform.OS === "ios" ? "ios" : "android";
useEffect(() => {
navigation.setOptions({
headerRight: () => (
<TouchableOpacity
onPress={() => {
logout();
}}
>
<TouchableOpacity onPress={() => logout()}>
<Text className='text-red-600 px-2'>
{t("home.settings.log_out_button")}
</Text>
@@ -41,98 +40,64 @@ function SettingsMobile() {
});
}, []);
const handleEntry = (entry: SettingsEntry) => {
if (entry.target.type === "action") {
if (entry.target.action === "quickConnect") {
quickConnectRef.current?.present();
}
return;
}
router.push(entry.target.route as any);
};
return (
<ScrollView
contentInsetAdjustmentBehavior='automatic'
contentContainerStyle={{
paddingLeft: insets.left,
paddingRight: insets.right,
paddingTop: 8,
paddingBottom: 32,
}}
>
<View
className='p-4 flex flex-col'
style={{ paddingTop: Platform.OS === "android" ? 10 : 0 }}
>
<View className='mb-4'>
<UserInfo />
</View>
<QuickConnect className='mb-4' />
{Platform.OS !== "ios" && (
<View className='mb-4'>
<ListGroup title={t("pairing.pair_with_phone_title")}>
<ListItem
onPress={() =>
router.push("/(auth)/(tabs)/(home)/companion-login")
}
title={t("pairing.pair_with_phone")}
textColor='blue'
/>
</ListGroup>
</View>
)}
<View className='mb-4'>
<AppLanguageSelector />
</View>
<View className='mb-4'>
<ListGroup title={t("home.settings.categories.title")}>
<ListItem
onPress={() => router.push("/settings/playback-controls/page")}
showArrow
title={t("home.settings.playback_controls.title")}
/>
<ListItem
onPress={() => router.push("/settings/audio-subtitles/page")}
showArrow
title={t("home.settings.audio_subtitles.title")}
/>
<ListItem
onPress={() => router.push("/settings/music/page")}
showArrow
title={t("home.settings.music.title")}
/>
<ListItem
onPress={() => router.push("/settings/appearance/page")}
showArrow
title={t("home.settings.appearance.title")}
/>
<ListItem
onPress={() => router.push("/settings/plugins/page")}
showArrow
title={t("home.settings.plugins.plugins_title")}
/>
<ListItem
onPress={() => router.push("/settings/intro/page")}
showArrow
title={t("home.settings.intro.title")}
/>
<ListItem
onPress={() => router.push("/settings/network/page")}
showArrow
title={t("home.settings.network.title")}
/>
<ListItem
onPress={() => router.push("/settings/logs/page")}
showArrow
title={t("home.settings.logs.logs_title")}
/>
</ListGroup>
</View>
<StorageSettings />
<View className='mx-3 mb-5'>
<AppLanguageSelector />
</View>
{SETTINGS_CATALOG.map((section) => {
const entries = section.entries.filter(
(e) => !e.platforms || e.platforms.includes(os),
);
if (entries.length === 0) return null;
return (
<SettingsSection key={section.id} title={t(section.titleKey)}>
{entries.map((e, i) => (
<SettingsRow
key={e.id}
title={t(e.titleKey)}
icon={e.icon}
onPress={() => handleEntry(e)}
isLast={i === entries.length - 1}
/>
))}
</SettingsSection>
);
})}
<SettingsSection>
<View className='p-3'>
<StorageSettings />
</View>
</SettingsSection>
<QuickConnectSheet ref={quickConnectRef} />
</ScrollView>
);
}
export default function settings() {
// Use TV settings component on TV platforms
if (Platform.isTV && SettingsTV) {
return <SettingsTV />;
}
return <SettingsMobile />;
}