Media: Fix stuck loading after page refresh (#4575)

This commit is contained in:
Alexander Zinchuk 2024-05-14 04:23:48 +02:00
parent c15c8a0eed
commit c610c5ca73
5 changed files with 30 additions and 9 deletions

View File

@ -134,3 +134,7 @@ export function selectCanAnimateInterface<T extends GlobalState>(global: T) {
export function selectIsContextMenuTranslucent<T extends GlobalState>(global: T) { export function selectIsContextMenuTranslucent<T extends GlobalState>(global: T) {
return selectPerformanceSettingsValue(global, 'contextMenuBlur'); return selectPerformanceSettingsValue(global, 'contextMenuBlur');
} }
export function selectIsSynced<T extends GlobalState>(global: T) {
return global.isSynced;
}

View File

@ -2,8 +2,10 @@ import { useEffect } from '../lib/teact/teact';
import { ApiMediaFormat } from '../api/types'; import { ApiMediaFormat } from '../api/types';
import { selectIsSynced } from '../global/selectors';
import * as mediaLoader from '../util/mediaLoader'; import * as mediaLoader from '../util/mediaLoader';
import useForceUpdate from './useForceUpdate'; import useForceUpdate from './useForceUpdate';
import useSelector from './useSelector';
const useMedia = ( const useMedia = (
mediaHash: string | false | undefined, mediaHash: string | false | undefined,
@ -13,9 +15,10 @@ const useMedia = (
) => { ) => {
const mediaData = mediaHash ? mediaLoader.getFromMemory(mediaHash) : undefined; const mediaData = mediaHash ? mediaLoader.getFromMemory(mediaHash) : undefined;
const forceUpdate = useForceUpdate(); const forceUpdate = useForceUpdate();
const isSynced = useSelector(selectIsSynced);
useEffect(() => { useEffect(() => {
if (!noLoad && mediaHash && !mediaData) { if (isSynced && !noLoad && mediaHash && !mediaData) {
const startedAt = Date.now(); const startedAt = Date.now();
mediaLoader.fetch(mediaHash, mediaFormat).then(() => { mediaLoader.fetch(mediaHash, mediaFormat).then(() => {
@ -27,7 +30,7 @@ const useMedia = (
} }
}); });
} }
}, [noLoad, mediaHash, mediaData, mediaFormat, forceUpdate, delay]); }, [noLoad, mediaHash, mediaData, mediaFormat, delay, isSynced]);
return mediaData; return mediaData;
}; };

View File

@ -4,10 +4,12 @@ import {
import { ApiMediaFormat } from '../api/types'; import { ApiMediaFormat } from '../api/types';
import { selectIsSynced } from '../global/selectors';
import * as mediaLoader from '../util/mediaLoader'; import * as mediaLoader from '../util/mediaLoader';
import { throttle } from '../util/schedulers'; import { throttle } from '../util/schedulers';
import { IS_PROGRESSIVE_SUPPORTED } from '../util/windowEnvironment'; import { IS_PROGRESSIVE_SUPPORTED } from '../util/windowEnvironment';
import useForceUpdate from './useForceUpdate'; import useForceUpdate from './useForceUpdate';
import useSelector from './useSelector';
import useUniqueId from './useUniqueId'; import useUniqueId from './useUniqueId';
const STREAMING_PROGRESS = 0.75; const STREAMING_PROGRESS = 0.75;
@ -24,6 +26,7 @@ export default function useMediaWithLoadProgress(
const mediaData = mediaHash ? mediaLoader.getFromMemory(mediaHash) : undefined; const mediaData = mediaHash ? mediaLoader.getFromMemory(mediaHash) : undefined;
const isStreaming = IS_PROGRESSIVE_SUPPORTED && mediaFormat === ApiMediaFormat.Progressive; const isStreaming = IS_PROGRESSIVE_SUPPORTED && mediaFormat === ApiMediaFormat.Progressive;
const forceUpdate = useForceUpdate(); const forceUpdate = useForceUpdate();
const isSynced = useSelector(selectIsSynced);
const id = useUniqueId(); const id = useUniqueId();
const [loadProgress, setLoadProgress] = useState(mediaData && !isStreaming ? 1 : 0); const [loadProgress, setLoadProgress] = useState(mediaData && !isStreaming ? 1 : 0);
const startedAtRef = useRef<number>(); const startedAtRef = useRef<number>();
@ -37,7 +40,7 @@ export default function useMediaWithLoadProgress(
}, [delay]); }, [delay]);
useEffect(() => { useEffect(() => {
if (!noLoad && mediaHash) { if (isSynced && !noLoad && mediaHash) {
if (!mediaData) { if (!mediaData) {
setLoadProgress(0); setLoadProgress(0);
@ -64,7 +67,7 @@ export default function useMediaWithLoadProgress(
} }
} }
}, [ }, [
noLoad, mediaHash, mediaData, mediaFormat, forceUpdate, isStreaming, delay, handleProgress, isHtmlAllowed, id, noLoad, mediaHash, mediaData, mediaFormat, isStreaming, delay, handleProgress, isHtmlAllowed, id, isSynced,
]); ]);
useEffect(() => { useEffect(() => {

11
src/hooks/useSelector.ts Normal file
View File

@ -0,0 +1,11 @@
import type { GlobalState } from '../global/types';
import useDerivedState from './useDerivedState';
import useSelectorSignal from './useSelectorSignal';
type Selector<T extends unknown> = (global: GlobalState) => T;
export default function useSelector<T extends unknown>(selector: Selector<T>) {
const selectorSignal = useSelectorSignal(selector);
return useDerivedState(selectorSignal);
}

View File

@ -13,15 +13,15 @@ import useEffectOnce from './useEffectOnce';
b) Return a signal instead of forcing a component update right away. b) Return a signal instead of forcing a component update right away.
*/ */
type Selector<T extends any> = (global: GlobalState) => T; type Selector<T extends unknown> = (global: GlobalState) => T;
interface State<T extends any> { interface State<T extends unknown> {
clientsCount: number; clientsCount: number;
getter: Signal<T>; getter: Signal<T>;
setter: SignalSetter; setter: SignalSetter;
} }
const bySelector = new Map<Selector<any>, State<any>>(); const bySelector = new Map<Selector<unknown>, State<unknown>>();
addCallback((global: GlobalState) => { addCallback((global: GlobalState) => {
for (const [selector, { setter }] of bySelector) { for (const [selector, { setter }] of bySelector) {
@ -29,7 +29,7 @@ addCallback((global: GlobalState) => {
} }
}); });
function useSelectorSignal<T extends any>(selector: Selector<T>): Signal<T> { function useSelectorSignal<T extends unknown>(selector: Selector<T>): Signal<T> {
let state = bySelector.get(selector); let state = bySelector.get(selector);
if (!state) { if (!state) {
@ -50,7 +50,7 @@ function useSelectorSignal<T extends any>(selector: Selector<T>): Signal<T> {
}; };
}); });
return state.getter; return state.getter as Signal<T>;
} }
export default useSelectorSignal; export default useSelectorSignal;