diff --git a/app/(auth)/(tabs)/(custom-links)/_layout.tsx b/app/(auth)/(tabs)/(custom-links)/_layout.tsx
new file mode 100644
index 00000000..ed0529d4
--- /dev/null
+++ b/app/(auth)/(tabs)/(custom-links)/_layout.tsx
@@ -0,0 +1,20 @@
+import {Stack} from "expo-router";
+import { Platform } from "react-native";
+
+export default function CustomMenuLayout() {
+ return (
+
+
+
+ );
+}
diff --git a/app/(auth)/(tabs)/(custom-links)/index.tsx b/app/(auth)/(tabs)/(custom-links)/index.tsx
new file mode 100644
index 00000000..76b10fb8
--- /dev/null
+++ b/app/(auth)/(tabs)/(custom-links)/index.tsx
@@ -0,0 +1,73 @@
+import {FlatList, TouchableOpacity, View} from "react-native";
+import {useSafeAreaInsets} from "react-native-safe-area-context";
+import React, {useCallback, useEffect, useState} from "react";
+import {useAtom} from "jotai/index";
+import {apiAtom} from "@/providers/JellyfinProvider";
+import {ListItem} from "@/components/ListItem";
+import * as WebBrowser from 'expo-web-browser';
+import Ionicons from '@expo/vector-icons/Ionicons';
+import {Text} from "@/components/common/Text";
+
+export interface MenuLink {
+ name: string,
+ url: string,
+ icon: string
+}
+
+export default function menuLinks() {
+ const [api] = useAtom(apiAtom);
+ const insets = useSafeAreaInsets()
+ const [menuLinks, setMenuLinks] = useState([])
+
+ const getMenuLinks = useCallback(async () => {
+ try {
+ const response = await api?.axiosInstance.get(api?.basePath + "/web/config.json")
+ const config = response?.data;
+
+ if (!config && !config.hasOwnProperty("menuLinks")) {
+ console.error("Menu links not found");
+ return;
+ }
+
+ setMenuLinks(config?.menuLinks as MenuLink[])
+ } catch (error) {
+ console.error("Failed to retrieve config:", error);
+ }
+ },
+ [api]
+ )
+
+ useEffect(() => { getMenuLinks() }, []);
+ return (
+ (
+ WebBrowser.openBrowserAsync(item.url) }>
+ }
+ />
+
+ )
+ }
+ ItemSeparatorComponent={() => (
+
+ )}
+ ListEmptyComponent={
+
+ No links
+
+ }
+ />
+ );
+}
\ No newline at end of file
diff --git a/app/(auth)/(tabs)/_layout.tsx b/app/(auth)/(tabs)/_layout.tsx
index e717d16d..01e7ea73 100644
--- a/app/(auth)/(tabs)/_layout.tsx
+++ b/app/(auth)/(tabs)/_layout.tsx
@@ -18,6 +18,7 @@ import type {
TabNavigationState,
} from "@react-navigation/native";
import { SystemBars } from "react-native-edge-to-edge";
+import {useSettings} from "@/utils/atoms/settings";
export const NativeTabs = withLayoutContext<
BottomTabNavigationOptions,
@@ -27,6 +28,7 @@ export const NativeTabs = withLayoutContext<
>(Navigator);
export default function TabLayout() {
+ const [settings] = useSettings();
return (
<>
@@ -71,6 +73,17 @@ export default function TabLayout() {
: () => ({ sfSymbol: "rectangle.stack" }),
}}
/>
+ require("@/assets/icons/list.png")
+ : () => ({sfSymbol: "list.dash"}),
+ tabBarButton: (p) =>
+ settings?.showCustomMenuLinks == true ? undefined : null,
+ }}
+ />
>
);
diff --git a/assets/icons/list.png b/assets/icons/list.png
new file mode 100644
index 00000000..3c548bb4
Binary files /dev/null and b/assets/icons/list.png differ
diff --git a/components/settings/SettingToggles.tsx b/components/settings/SettingToggles.tsx
index 9123f3d4..a2229c4f 100644
--- a/components/settings/SettingToggles.tsx
+++ b/components/settings/SettingToggles.tsx
@@ -403,6 +403,26 @@ export const SettingToggles: React.FC = ({ ...props }) => {
)}
+
+
+
+ Show Custom Menu Links
+
+ Show custom menu links defined inside your Jellyfin web config.json file
+
+
+ Linking.openURL("https://jellyfin.org/docs/general/clients/web-config/#custom-menu-links")
+ }
+ >
+ More info
+
+
+ updateSettings({ showCustomMenuLinks: value })}
+ />
+
diff --git a/utils/atoms/settings.ts b/utils/atoms/settings.ts
index 5d47c3dd..bb1f26ab 100644
--- a/utils/atoms/settings.ts
+++ b/utils/atoms/settings.ts
@@ -73,7 +73,8 @@ export type Settings = {
rewindSkipTime: number;
optimizedVersionsServerUrl?: string | null;
downloadMethod: "optimized" | "remux";
- autoDownload: boolean;
+ autoDownload: boolean,
+ showCustomMenuLinks: boolean;
};
const loadSettings = (): Settings => {
@@ -103,6 +104,7 @@ const loadSettings = (): Settings => {
optimizedVersionsServerUrl: null,
downloadMethod: "remux",
autoDownload: false,
+ showCustomMenuLinks: false,
};
try {