diff --git a/app/(auth)/(tabs)/(home)/settings/plugins/streamystats/page.tsx b/app/(auth)/(tabs)/(home)/settings/plugins/streamystats/page.tsx index b0383468..e78c3781 100644 --- a/app/(auth)/(tabs)/(home)/settings/plugins/streamystats/page.tsx +++ b/app/(auth)/(tabs)/(home)/settings/plugins/streamystats/page.tsx @@ -58,7 +58,12 @@ export default function StreamystatsPage() { pluginSettings?.streamyStatsPromotedWatchlists?.locked === true; const hideWatchlistsTabLocked = pluginSettings?.hideWatchlistsTab?.locked === true; - const isStreamystatsEnabled = !!url; + // The input renders the locked admin URL; enablement must follow the same + // effective value or every toggle stays disabled until local state syncs. + const effectiveUrl = isUrlLocked + ? (settings?.streamyStatsServerUrl ?? "") + : url; + const isStreamystatsEnabled = !!effectiveUrl; const onSave = useCallback(() => { const cleanUrl = url.endsWith("/") ? url.slice(0, -1) : url; @@ -155,9 +160,7 @@ export default function StreamystatsPage() { placeholder={t( "home.settings.plugins.streamystats.server_url_placeholder", )} - value={ - isUrlLocked ? (settings?.streamyStatsServerUrl ?? "") : url - } + value={effectiveUrl} keyboardType='url' returnKeyType='done' autoCapitalize='none' diff --git a/components/PendingAccountSaveModal.tsx b/components/PendingAccountSaveModal.tsx index 95d68a6e..9c2def07 100644 --- a/components/PendingAccountSaveModal.tsx +++ b/components/PendingAccountSaveModal.tsx @@ -37,7 +37,7 @@ export const PendingAccountSaveModal: React.FC = () => { const serverName = pending?.serverName; setPending(null); saveCurrentAccount({ securityType, pinCode, serverName }).catch( - () => {}, + (error) => console.warn("Failed to save account:", error), ); }} /> diff --git a/components/downloads/DownloadCard.tsx b/components/downloads/DownloadCard.tsx index da5263c4..2d1b85a0 100644 --- a/components/downloads/DownloadCard.tsx +++ b/components/downloads/DownloadCard.tsx @@ -1,7 +1,7 @@ import { Ionicons } from "@expo/vector-icons"; import { Image } from "expo-image"; -import { t } from "i18next"; import { useMemo } from "react"; +import { useTranslation } from "react-i18next"; import { ActivityIndicator, TouchableOpacity, @@ -35,6 +35,7 @@ interface DownloadCardProps extends TouchableOpacityProps { } export const DownloadCard = ({ process, ...props }: DownloadCardProps) => { + const { t } = useTranslation(); const { cancelDownload } = useDownload(); const router = useRouter(); const queryClient = useNetworkAwareQueryClient(); diff --git a/components/filters/FilterSheet.tsx b/components/filters/FilterSheet.tsx index fd2e4f4d..8f87ac22 100644 --- a/components/filters/FilterSheet.tsx +++ b/components/filters/FilterSheet.tsx @@ -193,15 +193,19 @@ export const FilterSheet = ({ { + // Match the deep-equality rule used to render the selected + // state below — option objects are recreated across renders, + // so reference checks would re-add an already selected item. + const isSelected = values.some((value) => isEqual(value, item)); if (multiple) { - if (!values.includes(item)) set(values.concat(item)); - else set(values.filter((v) => v !== item)); + if (!isSelected) set(values.concat(item)); + else set(values.filter((value) => !isEqual(value, item))); setTimeout(() => { setOpen(false); }, 250); } else { - if (!values.includes(item)) { + if (!isSelected) { set([item]); setTimeout(() => { setOpen(false); diff --git a/providers/Downloads/fileOperations.ts b/providers/Downloads/fileOperations.ts index 6a038740..05240998 100644 --- a/providers/Downloads/fileOperations.ts +++ b/providers/Downloads/fileOperations.ts @@ -97,6 +97,9 @@ export function getDownloadedItemSize(id: string): number { export function calculateTotalDownloadedSize(): number { const items = getAllDownloadedItems(); return items.reduce((sum, item) => { + // Trickplay bytes count too — getDownloadedItemSize models per-item size + // as video + trickplay, the total must match. + const trickplaySize = item.trickPlayData?.size ?? 0; // Read the live file size on disk so the total reflects actual usage and // self-heals items whose stored videoFileSize is 0 (old schema, or // `fileInfo.size` was undefined at download time). Fall back to the stored @@ -105,12 +108,12 @@ export function calculateTotalDownloadedSize(): number { try { const file = new File(filePathToUri(item.videoFilePath)); if (file.exists) { - return sum + (file.size ?? item.videoFileSize ?? 0); + return sum + (file.size ?? item.videoFileSize ?? 0) + trickplaySize; } } catch (error) { console.warn("Failed to stat downloaded file for size:", error); } } - return sum + (item.videoFileSize ?? 0); + return sum + (item.videoFileSize ?? 0) + trickplaySize; }, 0); }