import { get } from "svelte/store"; import settings from "$lib/state/settings"; import { device } from "$lib/device"; import { t } from "$lib/i18n/translations"; import { createDialog } from "$lib/state/dialogs"; import type { DialogInfo } from "$lib/types/dialog"; import type { CobaltFileUrlType } from "$lib/types/api"; type DownloadFileParams = { url?: string, file?: File, urlType?: CobaltFileUrlType, } type SavingDialogParams = { url?: string, file?: File, body?: string, urlType?: CobaltFileUrlType, } const openSavingDialog = ({ url, file, body, urlType }: SavingDialogParams) => { const dialogData: DialogInfo = { type: "saving", id: "saving", file, url, urlType, } if (body) dialogData.bodyText = body; createDialog(dialogData) } export const openFile = (file: File) => { const a = document.createElement("a"); const url = URL.createObjectURL(file); a.href = url; a.download = file.name; a.click(); URL.revokeObjectURL(url); } export const shareFile = async (file: File) => { return await navigator?.share({ files: [ new File([file], file.name, { type: file.type, }), ], }); } export const openURL = (url: string) => { if (!['http:', 'https:'].includes(new URL(url).protocol)) { return alert('error: invalid url!'); } const open = window.open(url, "_blank"); /* if new tab got blocked by user agent, show a saving dialog */ if (!open) { return openSavingDialog({ url, body: get(t)("dialog.saving.blocked") }); } } export const shareURL = async (url: string) => { return await navigator?.share({ url }); } export const copyURL = async (url: string) => { return await navigator?.clipboard?.writeText(url); } export const downloadFile = ({ url, file, urlType }: DownloadFileParams) => { if (!url && !file) throw new Error("attempted to download void"); const pref = get(settings).save.savingMethod; if (pref === "ask") { return openSavingDialog({ url, file, urlType }); } /* user actions (such as invoke share, open new tab) have expiration. in webkit, for example, that timeout is 5 seconds. https://github.com/WebKit/WebKit/blob/b838f8bb/Source/WebCore/page/LocalDOMWindow.cpp#L167 navigator.userActivation.isActive makes sure that we're still able to invoke an action without the user agent interrupting it. if not, we show a saving dialog for user to re-invoke that action. if browser is old or doesn't support this API, we just assume that it expired. */ if (!navigator?.userActivation?.isActive) { return openSavingDialog({ url, file, body: get(t)("dialog.saving.timeout"), urlType }); } try { if (file) { if (pref === "share" && device.supports.share) { return shareFile(file); } else if (pref === "download" && device.supports.directDownload) { return openFile(file); } } if (url) { if (pref === "share" && device.supports.share) { return shareURL(url); } else if (pref === "download" && device.supports.directDownload && !(device.is.iOS && urlType === "redirect")) { return openURL(url); } else if (pref === "copy" && !file) { return copyURL(url); } } } catch { /* catch & ignore */ } return openSavingDialog({ url, file, urlType }); }