feat(settings): unify Streamystats + Marlin URL inputs via the resolver

- jellyfinProbe (/System/Info/Public, ProductName check) + reachabilityProbe (services with no health route).

- ServerUrlStatusText: compact resolver status for ListItem-row layouts.

- Streamystats + Marlin: resolve the URL on blur (https-first, http fallback) and store the canonical URL; inline status feedback.
This commit is contained in:
Gauvain
2026-06-04 20:44:39 +02:00
parent 0f29457ff8
commit b54b0c670b
6 changed files with 133 additions and 0 deletions

View File

@@ -11,12 +11,15 @@ import {
} from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { toast } from "sonner-native";
import { ServerUrlStatusText } from "@/components/common/ServerUrlStatusText";
import { Text } from "@/components/common/Text";
import { ListGroup } from "@/components/list/ListGroup";
import { ListItem } from "@/components/list/ListItem";
import DisabledSetting from "@/components/settings/DisabledSetting";
import { useNetworkAwareQueryClient } from "@/hooks/useNetworkAwareQueryClient";
import { useServerUrlResolver } from "@/hooks/useServerUrlResolver";
import { useSettings } from "@/utils/atoms/settings";
import { reachabilityProbe } from "@/utils/serverUrl/probes/reachability";
export default function MarlinSearchPage() {
const navigation = useNavigation();
@@ -29,6 +32,7 @@ export default function MarlinSearchPage() {
const queryClient = useNetworkAwareQueryClient();
const [value, setValue] = useState<string>(settings?.marlinServerUrl || "");
const urlResolver = useServerUrlResolver(reachabilityProbe);
const onSave = (val: string) => {
updateSettings({
@@ -127,8 +131,17 @@ export default function MarlinSearchPage() {
autoCapitalize='none'
textContentType='URL'
onChangeText={(text) => setValue(text)}
onBlur={() => {
const candidate = value.trim();
if (candidate) {
urlResolver.resolve(candidate).then((r) => {
if (r.ok) setValue(r.url);
});
}
}}
/>
</View>
<ServerUrlStatusText state={urlResolver} className='mt-1' />
</DisabledSetting>
<Text className='px-4 text-xs text-neutral-500 mt-1'>
{t("home.settings.plugins.marlin_search.marlin_search_hint")}{" "}

View File

@@ -11,11 +11,14 @@ import {
} from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { toast } from "sonner-native";
import { ServerUrlStatusText } from "@/components/common/ServerUrlStatusText";
import { Text } from "@/components/common/Text";
import { ListGroup } from "@/components/list/ListGroup";
import { ListItem } from "@/components/list/ListItem";
import { useNetworkAwareQueryClient } from "@/hooks/useNetworkAwareQueryClient";
import { useServerUrlResolver } from "@/hooks/useServerUrlResolver";
import { useSettings } from "@/utils/atoms/settings";
import { reachabilityProbe } from "@/utils/serverUrl/probes/reachability";
export default function StreamystatsPage() {
const { t } = useTranslation();
@@ -32,6 +35,7 @@ export default function StreamystatsPage() {
// Local state for all editable fields
const [url, setUrl] = useState<string>(settings?.streamyStatsServerUrl || "");
const urlResolver = useServerUrlResolver(reachabilityProbe);
const [useForSearch, setUseForSearch] = useState<boolean>(
settings?.searchEngine === "Streamystats",
);
@@ -152,9 +156,20 @@ export default function StreamystatsPage() {
autoCapitalize='none'
textContentType='URL'
onChangeText={setUrl}
onBlur={() => {
const candidate = url.trim();
if (candidate) {
urlResolver.resolve(candidate).then((r) => {
if (r.ok) setUrl(r.url);
});
}
}}
/>
</ListItem>
</ListGroup>
<View className='px-4 mt-1'>
<ServerUrlStatusText state={urlResolver} />
</View>
<Text className='px-4 text-xs text-neutral-500 mt-1'>
{t("home.settings.plugins.streamystats.streamystats_search_hint")}{" "}