Compare commits

..

2 Commits

Author SHA1 Message Date
Alex Kim
a35a807223 Remove redundant code 2026-06-02 19:17:44 +10:00
Alex Kim
c9dba8a1b7 fix(downloads): poster images not showing for downloaded content 2026-06-02 19:14:10 +10:00
2 changed files with 35 additions and 47 deletions

View File

@@ -1,3 +1,4 @@
import { File, Paths } from "expo-file-system";
import { useCallback } from "react";
import { storage } from "@/utils/mmkv";
@@ -12,36 +13,28 @@ const useImageStorage = () => {
}
}, []);
/**
* expo-file-system instead of fetch+Blob+FileReader: the latter silently
* resolves to an empty payload under RN's New Architecture.
*/
const image2Base64 = useCallback(async (url?: string | null) => {
if (!url) return null;
let blob: Blob;
const tmpFile = new File(
Paths.cache,
`img-${Date.now()}-${Math.random().toString(36).slice(2)}.jpg`,
);
try {
// Fetch the data from the URL
const response = await fetch(url);
blob = await response.blob();
const downloaded = await File.downloadFileAsync(url, tmpFile, {
idempotent: true,
});
return await downloaded.base64();
} catch (error) {
console.warn("Error fetching image:", error);
return null;
} finally {
if (tmpFile.exists) tmpFile.delete();
}
// Create a FileReader instance
const reader = new FileReader();
// Convert blob to base64
return new Promise<string>((resolve, reject) => {
reader.onloadend = () => {
if (typeof reader.result === "string") {
// Extract the base64 string (remove the data URL prefix)
const base64 = reader.result.split(",")[1];
resolve(base64);
} else {
reject(new Error("Failed to convert image to base64"));
}
};
reader.onerror = reject;
reader.readAsDataURL(blob);
});
}, []);
const saveImage = useCallback(

View File

@@ -109,35 +109,30 @@ export const usePlaybackManager = ({
staleTime: 0,
});
/**
* Derive prev/next from the current item's real position in the adjacent
* list rather than from the array length. `getEpisodes({ adjacentTo })` does
* not guarantee a fixed [prev, current, next] shape — at the first/last
* episode it can still return the current item as the first/last entry — so
* length-based indexing wrongly surfaces the current episode as "previous".
*/
const currentIndex = useMemo(
() => adjacentItems?.findIndex((e) => e.Id === item?.Id) ?? -1,
[adjacentItems, item],
);
/** A neighbour is only navigable if it has an actual media file (not a
* "Virtual"/missing episode placeholder, e.g. an absent Special). */
const isNavigable = (episode?: BaseItemDto | null): episode is BaseItemDto =>
!!episode && episode.Id !== item?.Id && episode.LocationType !== "Virtual";
const previousItem = useMemo(() => {
if (!adjacentItems || currentIndex <= 0) return null;
const candidate = adjacentItems[currentIndex - 1];
return isNavigable(candidate) ? candidate : null;
}, [adjacentItems, currentIndex, item]);
if (!adjacentItems || adjacentItems.length <= 1) {
return null;
}
if (adjacentItems.length === 2) {
return adjacentItems[0].Id === item?.Id ? null : adjacentItems[0];
}
return adjacentItems[0];
}, [adjacentItems, item]);
/** The next item in the series */
const nextItem = useMemo(() => {
if (!adjacentItems || currentIndex < 0) return null;
const candidate = adjacentItems[currentIndex + 1];
return isNavigable(candidate) ? candidate : null;
}, [adjacentItems, currentIndex, item]);
if (!adjacentItems || adjacentItems.length <= 1) {
return null;
}
if (adjacentItems.length === 2) {
return adjacentItems[1].Id === item?.Id ? null : adjacentItems[1];
}
return adjacentItems[2];
}, [adjacentItems, item]);
/**
* Reports playback progress.