diff --git a/app/(auth)/(tabs)/(home)/settings/plugins/marlin-search/page.tsx b/app/(auth)/(tabs)/(home)/settings/plugins/marlin-search/page.tsx index d9e737ec9..fe011a7cd 100644 --- a/app/(auth)/(tabs)/(home)/settings/plugins/marlin-search/page.tsx +++ b/app/(auth)/(tabs)/(home)/settings/plugins/marlin-search/page.tsx @@ -28,7 +28,9 @@ export default function MarlinSearchPage() { const searchEngineLocked = pluginSettings?.searchEngine?.locked === true; const marlinUrlLocked = pluginSettings?.marlinServerUrl?.locked === true; - const hasStreamystats = !!pluginSettings?.streamyStatsServerUrl?.value; + // Effective (user/admin merged) URL, same source the search screen uses — + // the raw plugin value misses a user-configured Streamystats. + const hasStreamystats = !!settings?.streamyStatsServerUrl; const onSave = (val: string) => { updateSettings({ @@ -74,6 +76,9 @@ export default function MarlinSearchPage() { "home.settings.plugins.marlin_search.enable_marlin_search", )} disabledByAdmin={searchEngineLocked} + // Streamystats owns the search engine while configured — block the + // row tap too, not just the Switch, so it can't force "Jellyfin". + disabled={hasStreamystats} onPress={() => { updateSettings({ searchEngine: "Jellyfin" }); queryClient.invalidateQueries({ queryKey: ["search"] }); diff --git a/app/(auth)/(tabs)/(home)/settings/plugins/page.tsx b/app/(auth)/(tabs)/(home)/settings/plugins/page.tsx index 441588c0e..590bdf61b 100644 --- a/app/(auth)/(tabs)/(home)/settings/plugins/page.tsx +++ b/app/(auth)/(tabs)/(home)/settings/plugins/page.tsx @@ -13,8 +13,15 @@ export default function PluginsPage() { const { refreshStreamyfinPluginSettings } = useSettings(); const handleRefreshFromServer = useCallback(async () => { - await refreshStreamyfinPluginSettings(); - toast.success(t("home.settings.plugins.streamystats.toasts.refreshed")); + // Returns undefined when the API call fails (handled internally). + const refreshed = await refreshStreamyfinPluginSettings(); + if (refreshed) { + toast.success(t("home.settings.plugins.streamystats.toasts.refreshed")); + } else { + toast.error( + t("home.settings.plugins.streamystats.toasts.refresh_failed"), + ); + } }, [refreshStreamyfinPluginSettings, t]); return ( diff --git a/components/list/ListItem.tsx b/components/list/ListItem.tsx index fed62315c..7aa07c527 100644 --- a/components/list/ListItem.tsx +++ b/components/list/ListItem.tsx @@ -1,5 +1,6 @@ import { Ionicons } from "@expo/vector-icons"; import type { PropsWithChildren, ReactNode } from "react"; +import { useTranslation } from "react-i18next"; import { TouchableOpacity, View, type ViewProps } from "react-native"; import { Text } from "../common/Text"; @@ -32,7 +33,10 @@ export const ListItem: React.FC> = ({ disabledByAdmin = false, ...viewProps }) => { - const effectiveSubtitle = disabledByAdmin ? "Disabled by admin" : subtitle; + const { t } = useTranslation(); + const effectiveSubtitle = disabledByAdmin + ? t("home.settings.disabled_by_admin") + : subtitle; const isDisabled = disabled || disabledByAdmin; if (onPress) return ( diff --git a/components/settings/Jellyseerr.tsx b/components/settings/Jellyseerr.tsx index 30fbb92b1..c7dcfdd81 100644 --- a/components/settings/Jellyseerr.tsx +++ b/components/settings/Jellyseerr.tsx @@ -33,22 +33,25 @@ export const JellyseerrSettings = () => { string | undefined >(settings?.jellyseerrServerUrl || undefined); + // When the URL is admin-pinned, ignore any locally-typed value so the login + // targets the same server the (read-only) input displays. + const effectiveServerUrl = urlLocked + ? settings?.jellyseerrServerUrl + : jellyseerrServerUrl || settings?.jellyseerrServerUrl; + const loginToJellyseerrMutation = useMutation({ mutationFn: async () => { - if (!jellyseerrServerUrl && !settings?.jellyseerrServerUrl) - throw new Error("Missing server url"); + if (!effectiveServerUrl) throw new Error("Missing server url"); if (!user?.Name) throw new Error("Missing required information for login"); - const jellyseerrTempApi = new JellyseerrApi( - jellyseerrServerUrl || settings.jellyseerrServerUrl || "", - ); + const jellyseerrTempApi = new JellyseerrApi(effectiveServerUrl); const testResult = await jellyseerrTempApi.test(); if (!testResult.isValid) throw new Error("Invalid server url"); return jellyseerrTempApi.login(user.Name, jellyseerrPassword || ""); }, onSuccess: (user) => { setJellyseerrUser(user); - updateSettings({ jellyseerrServerUrl }); + updateSettings({ jellyseerrServerUrl: effectiveServerUrl }); }, onError: () => { toast.error(t("jellyseerr.failed_to_login")); @@ -149,7 +152,7 @@ export const JellyseerrSettings = () => { /> {urlLocked && ( - Disabled by admin + {t("home.settings.disabled_by_admin")} )} diff --git a/translations/en.json b/translations/en.json index ba3ffcebf..81320dbd8 100644 --- a/translations/en.json +++ b/translations/en.json @@ -118,6 +118,7 @@ }, "settings": { "settings_title": "Settings", + "disabled_by_admin": "Disabled by admin", "log_out_button": "Log out", "switch_user": { "title": "Switch user", @@ -370,6 +371,7 @@ "toasts": { "saved": "Saved", "refreshed": "Settings refreshed from server", + "refresh_failed": "Couldn't refresh settings from server", "disabled": "Streamystats disabled" }, "refresh_from_server": "Refresh settings from server"