diff --git a/app/(auth)/(tabs)/(home)/_layout.tsx b/app/(auth)/(tabs)/(home)/_layout.tsx
index 3509be51..98ee5c0d 100644
--- a/app/(auth)/(tabs)/(home)/_layout.tsx
+++ b/app/(auth)/(tabs)/(home)/_layout.tsx
@@ -77,6 +77,12 @@ export default function IndexLayout() {
title: "",
}}
/>
+
{Object.entries(nestedTabPageScreenOptions).map(([name, options]) => (
))}
diff --git a/app/(auth)/(tabs)/(home)/settings/hide-libraries/page.tsx b/app/(auth)/(tabs)/(home)/settings/hide-libraries/page.tsx
new file mode 100644
index 00000000..bddf62b8
--- /dev/null
+++ b/app/(auth)/(tabs)/(home)/settings/hide-libraries/page.tsx
@@ -0,0 +1,60 @@
+import { Text } from "@/components/common/Text";
+import { ListGroup } from "@/components/list/ListGroup";
+import { ListItem } from "@/components/list/ListItem";
+import { Loader } from "@/components/Loader";
+import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
+import { useSettings } from "@/utils/atoms/settings";
+import { getUserViewsApi } from "@jellyfin/sdk/lib/utils/api";
+import { useQuery } from "@tanstack/react-query";
+import { useAtomValue } from "jotai";
+import { Switch, View } from "react-native";
+
+export default function page() {
+ const [settings, updateSettings] = useSettings();
+ const user = useAtomValue(userAtom);
+ const api = useAtomValue(apiAtom);
+
+ const { data, isLoading: isLoading } = useQuery({
+ queryKey: ["user-views", user?.Id],
+ queryFn: async () => {
+ const response = await getUserViewsApi(api!).getUserViews({
+ userId: user?.Id,
+ });
+
+ return response.data.Items || null;
+ },
+ });
+
+ if (!settings) return null;
+
+ if (isLoading)
+ return (
+
+
+
+ );
+
+ return (
+
+
+ {data?.map((view) => (
+ {}}>
+ {
+ updateSettings({
+ hiddenLibraries: value
+ ? [...(settings.hiddenLibraries || []), view.Id!]
+ : settings.hiddenLibraries?.filter((id) => id !== view.Id),
+ });
+ }}
+ />
+
+ ))}
+
+
+ Select the libraries you want to hide from the Library tab.
+
+
+ );
+}
diff --git a/app/(auth)/(tabs)/(libraries)/index.tsx b/app/(auth)/(tabs)/(libraries)/index.tsx
index ef729254..e8c3f766 100644
--- a/app/(auth)/(tabs)/(libraries)/index.tsx
+++ b/app/(auth)/(tabs)/(libraries)/index.tsx
@@ -10,7 +10,7 @@ import {
import { FlashList } from "@shopify/flash-list";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { useAtom } from "jotai";
-import { useEffect } from "react";
+import { useEffect, useMemo } from "react";
import { StyleSheet, View } from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";
@@ -23,20 +23,20 @@ export default function index() {
const { data, isLoading: isLoading } = useQuery({
queryKey: ["user-views", user?.Id],
queryFn: async () => {
- if (!api || !user?.Id) {
- return null;
- }
-
- const response = await getUserViewsApi(api).getUserViews({
- userId: user.Id,
+ const response = await getUserViewsApi(api!).getUserViews({
+ userId: user?.Id,
});
return response.data.Items || null;
},
- enabled: !!api && !!user?.Id,
- staleTime: 60 * 1000 * 60,
+ staleTime: 60,
});
+ const libraries = useMemo(
+ () => data?.filter((l) => !settings?.hiddenLibraries?.includes(l.Id!)),
+ [data, settings?.hiddenLibraries]
+ );
+
useEffect(() => {
for (const item of data || []) {
queryClient.prefetchQuery({
@@ -63,7 +63,7 @@ export default function index() {
);
- if (!data)
+ if (!libraries)
return (
No libraries found
@@ -81,7 +81,7 @@ export default function index() {
paddingLeft: insets.left,
paddingRight: insets.right,
}}
- data={data}
+ data={libraries}
renderItem={({ item }) => }
keyExtractor={(item) => item.Id || ""}
ItemSeparatorComponent={() =>
diff --git a/components/common/TouchableItemRouter.tsx b/components/common/TouchableItemRouter.tsx
index c13e9821..04d36dc7 100644
--- a/components/common/TouchableItemRouter.tsx
+++ b/components/common/TouchableItemRouter.tsx
@@ -4,9 +4,11 @@ import {
BaseItemPerson,
} from "@jellyfin/sdk/lib/generated-client/models";
import { useRouter, useSegments } from "expo-router";
-import { PropsWithChildren } from "react";
+import { PropsWithChildren, useCallback } from "react";
import { TouchableOpacity, TouchableOpacityProps } from "react-native";
import * as ContextMenu from "zeego/context-menu";
+import { useActionSheet } from "@expo/react-native-action-sheet";
+import * as Haptics from "expo-haptics";
interface Props extends TouchableOpacityProps {
item: BaseItemDto;
@@ -16,8 +18,6 @@ export const itemRouter = (
item: BaseItemDto | BaseItemPerson,
from: string
) => {
- console.log(item.Type, item?.CollectionType);
-
if ("CollectionType" in item && item.CollectionType === "livetv") {
return `/(auth)/(tabs)/${from}/livetv`;
}
@@ -68,10 +68,33 @@ export const TouchableItemRouter: React.FC> = ({
}) => {
const router = useRouter();
const segments = useSegments();
+ const { showActionSheetWithOptions } = useActionSheet();
+ const markAsPlayedStatus = useMarkAsPlayed(item);
const from = segments[2];
- const markAsPlayedStatus = useMarkAsPlayed(item);
+ const showActionSheet = useCallback(() => {
+ if (!(item.Type === "Movie" || item.Type === "Episode")) return;
+
+ const options = ["Mark as Played", "Mark as Not Played", "Cancel"];
+ const cancelButtonIndex = 2;
+
+ showActionSheetWithOptions(
+ {
+ options,
+ cancelButtonIndex,
+ },
+ async (selectedIndex) => {
+ if (selectedIndex === 0) {
+ await markAsPlayedStatus(true);
+ Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
+ } else if (selectedIndex === 1) {
+ await markAsPlayedStatus(false);
+ Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
+ }
+ }
+ );
+ }, [showActionSheetWithOptions, markAsPlayedStatus]);
if (
from === "(home)" ||
@@ -80,78 +103,16 @@ export const TouchableItemRouter: React.FC> = ({
from === "(favorites)"
)
return (
-
-
- {
- const url = itemRouter(item, from);
- // @ts-ignore
- router.push(url);
- }}
- {...props}
- >
- {children}
-
-
-
- Actions
- {
- markAsPlayedStatus(true);
- }}
- shouldDismissMenuOnSelect
- >
-
- Mark as watched
-
-
-
- {
- markAsPlayedStatus(false);
- }}
- shouldDismissMenuOnSelect
- destructive
- >
-
- Mark as not watched
-
-
-
-
-
+ {
+ const url = itemRouter(item, from);
+ // @ts-expect-error
+ router.push(url);
+ }}
+ {...props}
+ >
+ {children}
+
);
};
diff --git a/components/settings/OtherSettings.tsx b/components/settings/OtherSettings.tsx
index d280a167..8987379a 100644
--- a/components/settings/OtherSettings.tsx
+++ b/components/settings/OtherSettings.tsx
@@ -15,10 +15,12 @@ import * as DropdownMenu from "zeego/dropdown-menu";
import { Text } from "../common/Text";
import { ListGroup } from "../list/ListGroup";
import { ListItem } from "../list/ListItem";
+import { useRouter } from "expo-router";
interface Props extends ViewProps {}
export const OtherSettings: React.FC = () => {
+ const router = useRouter();
const [settings, updateSettings] = useSettings();
/********************
@@ -54,7 +56,7 @@ export const OtherSettings: React.FC = () => {
if (!settings) return null;
return (
-
+
{
}
/>
+ router.push("/settings/hide-libraries/page")}
+ title="Hide Libraries"
+ showArrow
+ />
);
};
diff --git a/utils/atoms/settings.ts b/utils/atoms/settings.ts
index c37dd4eb..fa201610 100644
--- a/utils/atoms/settings.ts
+++ b/utils/atoms/settings.ts
@@ -88,6 +88,7 @@ export type Settings = {
remuxConcurrentLimit: 1 | 2 | 3 | 4;
safeAreaInControlsEnabled: boolean;
jellyseerrServerUrl?: string;
+ hiddenLibraries?: string[];
};
const loadSettings = (): Settings => {
@@ -126,6 +127,7 @@ const loadSettings = (): Settings => {
remuxConcurrentLimit: 1,
safeAreaInControlsEnabled: true,
jellyseerrServerUrl: undefined,
+ hiddenLibraries: [],
};
try {