Spaces:
Sleeping
Sleeping
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 }); | |
} | |