111 lines
3.4 KiB
TypeScript
111 lines
3.4 KiB
TypeScript
import type { FC } from '../../lib/teact/teact';
|
|
import { memo, useEffect } from '../../lib/teact/teact';
|
|
import { getActions, getGlobal, withGlobal } from '../../global';
|
|
|
|
import type { TabState } from '../../global/types';
|
|
import { ApiMediaFormat } from '../../api/types';
|
|
|
|
import { selectTabState } from '../../global/selectors';
|
|
import { IS_OPFS_SUPPORTED, IS_SERVICE_WORKER_SUPPORTED, MAX_BUFFER_SIZE } from '../../util/browser/windowEnvironment';
|
|
import download from '../../util/download';
|
|
import generateUniqueId from '../../util/generateUniqueId';
|
|
import * as mediaLoader from '../../util/mediaLoader';
|
|
|
|
import useLastCallback from '../../hooks/useLastCallback';
|
|
import useRunDebounced from '../../hooks/useRunDebounced';
|
|
|
|
type StateProps = {
|
|
activeDownloads: TabState['activeDownloads'];
|
|
};
|
|
|
|
const GLOBAL_UPDATE_DEBOUNCE = 1000;
|
|
|
|
const processedHashes = new Set<string>();
|
|
const downloadedHashes = new Set<string>();
|
|
|
|
const DownloadManager: FC<StateProps> = ({
|
|
activeDownloads,
|
|
}) => {
|
|
const { cancelMediaHashDownloads, showNotification } = getActions();
|
|
|
|
const runDebounced = useRunDebounced(GLOBAL_UPDATE_DEBOUNCE, true);
|
|
|
|
const handleMediaDownloaded = useLastCallback((hash: string) => {
|
|
downloadedHashes.add(hash);
|
|
runDebounced(() => {
|
|
if (downloadedHashes.size) {
|
|
cancelMediaHashDownloads({ mediaHashes: Array.from(downloadedHashes) });
|
|
downloadedHashes.clear();
|
|
}
|
|
});
|
|
});
|
|
|
|
useEffect(() => {
|
|
if (!Object.keys(activeDownloads).length) {
|
|
processedHashes.clear();
|
|
return;
|
|
}
|
|
|
|
Object.entries(activeDownloads).forEach(([mediaHash, metadata]) => {
|
|
if (processedHashes.has(mediaHash)) {
|
|
return;
|
|
}
|
|
processedHashes.add(mediaHash);
|
|
|
|
const { size, filename, format: mediaFormat } = metadata;
|
|
|
|
const mediaData = mediaLoader.getFromMemory(mediaHash);
|
|
|
|
if (mediaData) {
|
|
download(mediaData, filename);
|
|
handleMediaDownloaded(mediaHash);
|
|
return;
|
|
}
|
|
|
|
if (size > MAX_BUFFER_SIZE && !IS_OPFS_SUPPORTED && !IS_SERVICE_WORKER_SUPPORTED) {
|
|
showNotification({
|
|
message: 'Downloading files bigger than 2GB is not supported in your browser.',
|
|
});
|
|
handleMediaDownloaded(mediaHash);
|
|
return;
|
|
}
|
|
|
|
const handleProgress = () => {
|
|
const currentDownloads = selectTabState(getGlobal()).activeDownloads;
|
|
if (!currentDownloads[mediaHash]) {
|
|
mediaLoader.cancelProgress(handleProgress);
|
|
}
|
|
};
|
|
|
|
mediaLoader.fetch(mediaHash, mediaFormat, true, handleProgress, generateUniqueId()).then((result) => {
|
|
if (mediaFormat === ApiMediaFormat.DownloadUrl) {
|
|
const url = new URL(result, window.document.baseURI);
|
|
url.searchParams.set('filename', encodeURIComponent(filename));
|
|
const downloadWindow = window.open(url.toString());
|
|
downloadWindow?.addEventListener('beforeunload', () => {
|
|
showNotification({
|
|
message: 'Download started. Please, do not close the app before it is finished.',
|
|
});
|
|
});
|
|
} else if (result) {
|
|
download(result, filename);
|
|
}
|
|
|
|
handleMediaDownloaded(mediaHash);
|
|
});
|
|
});
|
|
}, [activeDownloads]);
|
|
|
|
return undefined;
|
|
};
|
|
|
|
export default memo(withGlobal(
|
|
(global): StateProps => {
|
|
const activeDownloads = selectTabState(global).activeDownloads;
|
|
|
|
return {
|
|
activeDownloads,
|
|
};
|
|
},
|
|
)(DownloadManager));
|