fix(app): refresh favorite gif previews that point at a provider page (#1925)

This commit is contained in:
Hampus authored and GitHub committed 2026-08-24 12:46:40 +02:00
1 parent fbd653d8e0
commit 1300e5a690
4 files changed
+94 -30

No files matched your search

@@ -1,5 +1,7 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {inferFormatContentType, PREVIEW_FORMAT_PRIORITY} from '@fluxer/schema/src/domains/gif/GifMediaFormatKeys';
export interface FavoriteGifMediaFormat {
src: string;
proxy_src: string;
@@ -17,41 +19,90 @@ export interface FavoriteGifEntry {
placeholder: string | null;
}
const PREVIEW_FORMAT_PRIORITY = ['webm', 'mp4', 'tinywebm', 'tinymp4', 'webp', 'gif', 'tinygif', 'nanogif'] as const;
const VIDEO_FORMAT_KEYS = new Set(['webm', 'mp4', 'tinywebm', 'tinymp4']);
const FORMAT_CONTENT_TYPES: Record<string, string> = {
webm: 'video/webm',
tinywebm: 'video/webm',
mp4: 'video/mp4',
tinymp4: 'video/mp4',
webp: 'image/webp',
gif: 'image/gif',
tinygif: 'image/gif',
nanogif: 'image/gif',
};
export const PREVIEW_TILE_CSS_WIDTH = 200;
const PLAYABLE_CLASS = 0;
const STILL_CLASS = 1;
const UNKNOWN_CLASS = 2;
interface PreviewCandidate {
key: string;
format: FavoriteGifMediaFormat;
formatClass: number;
priority: number;
}
export interface PreviewTileBox {
cssWidth?: number;
devicePixelRatio?: number;
}
function isUsablePreviewFormat(format: FavoriteGifMediaFormat | undefined): format is FavoriteGifMediaFormat {
return Boolean(format?.src && format.proxy_src && format.width > 0 && format.height > 0);
}
function previewFormatClass(formatKey: string): number {
const contentType = inferFormatContentType(formatKey);
if (contentType.startsWith('video/')) return PLAYABLE_CLASS;
if (contentType.startsWith('image/')) return STILL_CLASS;
return UNKNOWN_CLASS;
}
function previewFormatPriority(formatKey: string): number {
const index = PREVIEW_FORMAT_PRIORITY.indexOf(formatKey as (typeof PREVIEW_FORMAT_PRIORITY)[number]);
return index === -1 ? PREVIEW_FORMAT_PRIORITY.length : index;
}
function currentDevicePixelRatio(): number {
const ratio = (globalThis as {devicePixelRatio?: number}).devicePixelRatio;
return typeof ratio === 'number' && Number.isFinite(ratio) && ratio > 0 ? ratio : 1;
}
export function previewTileDeviceWidth(tile: PreviewTileBox = {}): number {
return (tile.cssWidth ?? PREVIEW_TILE_CSS_WIDTH) * (tile.devicePixelRatio ?? currentDevicePixelRatio());
}
export function pickBestPreviewFormat(
media: Record<string, FavoriteGifMediaFormat> | null | undefined,
kind: 'any' | 'image' = 'any',
tile: PreviewTileBox = {},
): {key: string; format: FavoriteGifMediaFormat} | null {
if (!media) return null;
const deviceWidth = previewTileDeviceWidth(tile);
const candidates: Array<PreviewCandidate> = [];
for (const [key, format] of Object.entries(media)) {
if (!isUsablePreviewFormat(format)) continue;
const formatClass = previewFormatClass(key);
if (kind === 'image' && formatClass !== STILL_CLASS) continue;
candidates.push({key, format, formatClass, priority: previewFormatPriority(key)});
}
if (candidates.length === 0) return null;
const filling = candidates.filter((candidate) => candidate.format.width >= deviceWidth);
const pool = filling.length > 0 ? filling : candidates;
const widthDirection = filling.length > 0 ? 1 : -1;
pool.sort(
(a, b) =>
a.formatClass - b.formatClass ||
widthDirection * (a.format.width - b.format.width) ||
a.priority - b.priority ||
a.key.localeCompare(b.key),
);
const best = pool[0];
return {key: best.key, format: best.format};
}
export function pickCanonicalPreviewFormat(
media: Record<string, FavoriteGifMediaFormat> | null | undefined,
): {key: string; format: FavoriteGifMediaFormat} | null {
if (!media) return null;
for (const key of PREVIEW_FORMAT_PRIORITY) {
const format = media[key];
if (format?.src && format.proxy_src && format.width > 0 && format.height > 0) {
return {key, format};
}
if (isUsablePreviewFormat(format)) return {key, format};
}
for (const [key, format] of Object.entries(media)) {
if (format?.src && format.proxy_src && format.width > 0 && format.height > 0) {
return {key, format};
}
if (isUsablePreviewFormat(format)) return {key, format};
}
return null;
}
export function inferFormatContentType(formatKey: string): string {
return FORMAT_CONTENT_TYPES[formatKey] ?? '';
}
export function isVideoFormatKey(formatKey: string): boolean {
return VIDEO_FORMAT_KEYS.has(formatKey);
}
export {inferFormatContentType};