mirror of
https://github.com/streamyfin/streamyfin.git
synced 2025-08-20 18:37:18 +02:00
Library headers, filters and favorites
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
import { nestedTabPageScreenOptions } from "@/components/stacks/NestedTabPageStack";
|
||||
import { Stack } from "expo-router";
|
||||
import { Platform } from "react-native";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function SearchLayout() {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Stack>
|
||||
<Stack.Screen
|
||||
@@ -10,7 +12,7 @@ export default function SearchLayout() {
|
||||
options={{
|
||||
headerShown: true,
|
||||
headerLargeTitle: true,
|
||||
headerTitle: "Favorites",
|
||||
headerTitle: t("favorites.favorites_title"),
|
||||
headerBlurEffect: "prominent",
|
||||
headerTransparent: Platform.OS === "ios" ? true : false,
|
||||
headerShadowVisible: false,
|
||||
|
||||
@@ -33,6 +33,7 @@ import * as ScreenOrientation from "expo-screen-orientation";
|
||||
import { useAtom } from "jotai";
|
||||
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { FlatList, View } from "react-native";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const page: React.FC = () => {
|
||||
const searchParams = useLocalSearchParams();
|
||||
@@ -45,6 +46,8 @@ const page: React.FC = () => {
|
||||
ScreenOrientation.Orientation.PORTRAIT_UP
|
||||
);
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [selectedGenres, setSelectedGenres] = useAtom(genreFilterAtom);
|
||||
const [selectedYears, setSelectedYears] = useAtom(yearFilterAtom);
|
||||
const [selectedTags, setSelectedTags] = useAtom(tagsFilterAtom);
|
||||
@@ -244,7 +247,7 @@ const page: React.FC = () => {
|
||||
}}
|
||||
set={setSelectedGenres}
|
||||
values={selectedGenres}
|
||||
title="Genres"
|
||||
title={t("library.headers.genres")}
|
||||
renderItemLabel={(item) => item.toString()}
|
||||
searchFilter={(item, search) =>
|
||||
item.toLowerCase().includes(search.toLowerCase())
|
||||
@@ -271,7 +274,7 @@ const page: React.FC = () => {
|
||||
}}
|
||||
set={setSelectedYears}
|
||||
values={selectedYears}
|
||||
title="Years"
|
||||
title={t("library.headers.years")}
|
||||
renderItemLabel={(item) => item.toString()}
|
||||
searchFilter={(item, search) => item.includes(search)}
|
||||
/>
|
||||
@@ -296,7 +299,7 @@ const page: React.FC = () => {
|
||||
}}
|
||||
set={setSelectedTags}
|
||||
values={selectedTags}
|
||||
title="Tags"
|
||||
title={t("library.headers.tags")}
|
||||
renderItemLabel={(item) => item.toString()}
|
||||
searchFilter={(item, search) =>
|
||||
item.toLowerCase().includes(search.toLowerCase())
|
||||
@@ -314,7 +317,7 @@ const page: React.FC = () => {
|
||||
queryFn={async () => sortOptions.map((s) => s.key)}
|
||||
set={setSortBy}
|
||||
values={sortBy}
|
||||
title="Sort By"
|
||||
title={t("library.headers.sort_by")}
|
||||
renderItemLabel={(item) =>
|
||||
sortOptions.find((i) => i.key === item)?.value || ""
|
||||
}
|
||||
@@ -334,7 +337,7 @@ const page: React.FC = () => {
|
||||
queryFn={async () => sortOrderOptions.map((s) => s.key)}
|
||||
set={setSortOrder}
|
||||
values={sortOrder}
|
||||
title="Sort Order"
|
||||
title={t("library.headers.sort_order")}
|
||||
renderItemLabel={(item) =>
|
||||
sortOrderOptions.find((i) => i.key === item)?.value || ""
|
||||
}
|
||||
|
||||
@@ -302,7 +302,7 @@ const Page = () => {
|
||||
}}
|
||||
set={setSelectedGenres}
|
||||
values={selectedGenres}
|
||||
title="Genres"
|
||||
title={t("library.headers.genres")}
|
||||
renderItemLabel={(item) => item.toString()}
|
||||
searchFilter={(item, search) =>
|
||||
item.toLowerCase().includes(search.toLowerCase())
|
||||
@@ -329,7 +329,7 @@ const Page = () => {
|
||||
}}
|
||||
set={setSelectedYears}
|
||||
values={selectedYears}
|
||||
title="Years"
|
||||
title={t("library.headers.years")}
|
||||
renderItemLabel={(item) => item.toString()}
|
||||
searchFilter={(item, search) => item.includes(search)}
|
||||
/>
|
||||
@@ -354,7 +354,7 @@ const Page = () => {
|
||||
}}
|
||||
set={setSelectedTags}
|
||||
values={selectedTags}
|
||||
title="Tags"
|
||||
title={t("library.headers.tags")}
|
||||
renderItemLabel={(item) => item.toString()}
|
||||
searchFilter={(item, search) =>
|
||||
item.toLowerCase().includes(search.toLowerCase())
|
||||
@@ -372,7 +372,7 @@ const Page = () => {
|
||||
queryFn={async () => sortOptions.map((s) => s.key)}
|
||||
set={setSortBy}
|
||||
values={sortBy}
|
||||
title="Sort By"
|
||||
title={t("library.headers.sort_by")}
|
||||
renderItemLabel={(item) =>
|
||||
sortOptions.find((i) => i.key === item)?.value || ""
|
||||
}
|
||||
@@ -392,7 +392,7 @@ const Page = () => {
|
||||
queryFn={async () => sortOrderOptions.map((s) => s.key)}
|
||||
set={setSortOrder}
|
||||
values={sortOrder}
|
||||
title="Sort Order"
|
||||
title={t("library.headers.sort_order")}
|
||||
renderItemLabel={(item) =>
|
||||
sortOrderOptions.find((i) => i.key === item)?.value || ""
|
||||
}
|
||||
|
||||
@@ -42,11 +42,11 @@ export default function IndexLayout() {
|
||||
side={"bottom"}
|
||||
sideOffset={10}
|
||||
>
|
||||
<DropdownMenu.Label>Display</DropdownMenu.Label>
|
||||
<DropdownMenu.Label>{t("library.options.display")}</DropdownMenu.Label>
|
||||
<DropdownMenu.Group key="display-group">
|
||||
<DropdownMenu.Sub>
|
||||
<DropdownMenu.SubTrigger key="image-style-trigger">
|
||||
Display
|
||||
{t("library.options.display")}
|
||||
</DropdownMenu.SubTrigger>
|
||||
<DropdownMenu.SubContent
|
||||
alignOffset={-10}
|
||||
@@ -69,7 +69,7 @@ export default function IndexLayout() {
|
||||
>
|
||||
<DropdownMenu.ItemIndicator />
|
||||
<DropdownMenu.ItemTitle key="display-title-1">
|
||||
Row
|
||||
{t("library.options.row")}
|
||||
</DropdownMenu.ItemTitle>
|
||||
</DropdownMenu.CheckboxItem>
|
||||
<DropdownMenu.CheckboxItem
|
||||
@@ -86,14 +86,14 @@ export default function IndexLayout() {
|
||||
>
|
||||
<DropdownMenu.ItemIndicator />
|
||||
<DropdownMenu.ItemTitle key="display-title-2">
|
||||
List
|
||||
{t("library.options.list")}
|
||||
</DropdownMenu.ItemTitle>
|
||||
</DropdownMenu.CheckboxItem>
|
||||
</DropdownMenu.SubContent>
|
||||
</DropdownMenu.Sub>
|
||||
<DropdownMenu.Sub>
|
||||
<DropdownMenu.SubTrigger key="image-style-trigger">
|
||||
Image style
|
||||
{t("library.options.image_style")}
|
||||
</DropdownMenu.SubTrigger>
|
||||
<DropdownMenu.SubContent
|
||||
alignOffset={-10}
|
||||
@@ -116,7 +116,7 @@ export default function IndexLayout() {
|
||||
>
|
||||
<DropdownMenu.ItemIndicator />
|
||||
<DropdownMenu.ItemTitle key="poster-title">
|
||||
Poster
|
||||
{t("library.options.poster")}
|
||||
</DropdownMenu.ItemTitle>
|
||||
</DropdownMenu.CheckboxItem>
|
||||
<DropdownMenu.CheckboxItem
|
||||
@@ -133,7 +133,7 @@ export default function IndexLayout() {
|
||||
>
|
||||
<DropdownMenu.ItemIndicator />
|
||||
<DropdownMenu.ItemTitle key="cover-title">
|
||||
Cover
|
||||
{t("library.options.cover")}
|
||||
</DropdownMenu.ItemTitle>
|
||||
</DropdownMenu.CheckboxItem>
|
||||
</DropdownMenu.SubContent>
|
||||
@@ -157,7 +157,7 @@ export default function IndexLayout() {
|
||||
>
|
||||
<DropdownMenu.ItemIndicator />
|
||||
<DropdownMenu.ItemTitle key="show-titles-title">
|
||||
Show titles
|
||||
{t("library.options.show_titles")}
|
||||
</DropdownMenu.ItemTitle>
|
||||
</DropdownMenu.CheckboxItem>
|
||||
<DropdownMenu.CheckboxItem
|
||||
@@ -174,7 +174,7 @@ export default function IndexLayout() {
|
||||
>
|
||||
<DropdownMenu.ItemIndicator />
|
||||
<DropdownMenu.ItemTitle key="show-stats-title">
|
||||
Show stats
|
||||
{t("library.options.show_stats")}
|
||||
</DropdownMenu.ItemTitle>
|
||||
</DropdownMenu.CheckboxItem>
|
||||
</DropdownMenu.Group>
|
||||
|
||||
@@ -129,7 +129,7 @@ export default function search() {
|
||||
if (Platform.OS === "ios")
|
||||
navigation.setOptions({
|
||||
headerSearchBarOptions: {
|
||||
placeholder: "Search...",
|
||||
placeholder: t("search.search"),
|
||||
onChangeText: (e: any) => {
|
||||
router.setParams({ q: "" });
|
||||
setSearch(e.nativeEvent.text);
|
||||
@@ -308,7 +308,7 @@ export default function search() {
|
||||
autoCorrect={false}
|
||||
returnKeyType="done"
|
||||
keyboardType="web-search"
|
||||
placeholder={t("search.search_hint")}
|
||||
placeholder={t("search.search_here")}
|
||||
value={search}
|
||||
onChangeText={(text) => setSearch(text)}
|
||||
/>
|
||||
|
||||
@@ -73,7 +73,7 @@ export default function TabLayout() {
|
||||
<NativeTabs.Screen
|
||||
name="(favorites)"
|
||||
options={{
|
||||
title: "Favorites",
|
||||
title: t("tabs.favorites"),
|
||||
tabBarIcon:
|
||||
Platform.OS == "android"
|
||||
? ({ color, focused, size }) =>
|
||||
|
||||
@@ -19,6 +19,7 @@ import { StyleSheet, TouchableOpacity, View, ViewProps } from "react-native";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { Button } from "../Button";
|
||||
import { Input } from "../common/Input";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
interface Props<T> extends ViewProps {
|
||||
open: boolean;
|
||||
@@ -76,6 +77,7 @@ export const FilterSheet = <T,>({
|
||||
}: Props<T>) => {
|
||||
const bottomSheetModalRef = useRef<BottomSheetModal>(null);
|
||||
const snapPoints = useMemo(() => ["80%"], []);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [data, setData] = useState<T[]>([]);
|
||||
const [offset, setOffset] = useState<number>(0);
|
||||
@@ -156,7 +158,7 @@ export const FilterSheet = <T,>({
|
||||
<Text className="mb-2 text-neutral-500">{_data?.length} items</Text>
|
||||
{showSearch && (
|
||||
<Input
|
||||
placeholder="Search..."
|
||||
placeholder={t("search.search")}
|
||||
className="my-2"
|
||||
value={search}
|
||||
onChangeText={(text) => {
|
||||
|
||||
@@ -191,14 +191,35 @@
|
||||
},
|
||||
"search": {
|
||||
"search_title": "Search",
|
||||
"search_hint": "Search here...",
|
||||
"search_here": "Search here...",
|
||||
"search": "Search...",
|
||||
"no_results_found_for": "No results found for"
|
||||
},
|
||||
"library": {
|
||||
"library_title": "Library",
|
||||
"no_items_found": "No items found",
|
||||
"no_results": "No results",
|
||||
"no_libraries_found": "No libraries found"
|
||||
"no_libraries_found": "No libraries found",
|
||||
"options": {
|
||||
"display": "Display",
|
||||
"row": "Row",
|
||||
"list": "List",
|
||||
"image_style": "Image style",
|
||||
"poster": "Poster",
|
||||
"cover": "Cover",
|
||||
"show_titles": "Show titles",
|
||||
"show_stats": "Show stats"
|
||||
},
|
||||
"headers": {
|
||||
"genres": "Genres",
|
||||
"years": "Years",
|
||||
"sort_by": "Sort By",
|
||||
"sort_order": "Sort Order",
|
||||
"tags": "Tags"
|
||||
}
|
||||
},
|
||||
"favorites": {
|
||||
"favorites_title": "Favorites"
|
||||
},
|
||||
"player": {
|
||||
"error": "Error",
|
||||
@@ -229,6 +250,7 @@
|
||||
"home": "Home",
|
||||
"search": "Search",
|
||||
"library": "Library",
|
||||
"custom_links": "Custom Links"
|
||||
"custom_links": "Custom Links",
|
||||
"favorites": "Favorites"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,19 +49,41 @@
|
||||
},
|
||||
"search": {
|
||||
"search_title": "Recherche",
|
||||
"search_hint": "Rechercher ici...",
|
||||
"search_here": "Rechercher ici...",
|
||||
"search": "Rechercher...",
|
||||
"no_results_found_for": "Aucun résultat trouvé pour"
|
||||
},
|
||||
"library": {
|
||||
"library_title": "Bibliothèque",
|
||||
"no_items_found": "Aucun item trouvé",
|
||||
"no_results": "Aucun résultat",
|
||||
"no_libraries_found": "Aucune bibliothèque trouvée"
|
||||
"no_libraries_found": "Aucune bibliothèque trouvée",
|
||||
"options": {
|
||||
"display": "Affichage",
|
||||
"row": "Rangée",
|
||||
"list": "Liste",
|
||||
"image_style": "Style d'image",
|
||||
"poster": "Affiche",
|
||||
"cover": "Couverture",
|
||||
"show_titles": "Afficher les titres",
|
||||
"show_stats": "Afficher les statistiques"
|
||||
},
|
||||
"headers": {
|
||||
"genres": "Genres",
|
||||
"years": "Années",
|
||||
"sort_by": "Trier par",
|
||||
"sort_order": "Ordre de tri",
|
||||
"tags": "Tags"
|
||||
}
|
||||
},
|
||||
"favorites": {
|
||||
"favorites_title": "Favoris"
|
||||
},
|
||||
"tabs": {
|
||||
"home": "Accueil",
|
||||
"search": "Recherche",
|
||||
"library": "Bibliothèque",
|
||||
"custom_links": "Liens personalisés"
|
||||
"custom_links": "Liens personalisés",
|
||||
"favorites": "Favoris"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user