122 lines
4.0 KiB
TypeScript
122 lines
4.0 KiB
TypeScript
import type { NativeCapability } from "./types.ts";
|
|
|
|
type ShareOptions = { title?: string; text?: string; url?: string };
|
|
type PositionOptions = globalThis.PositionOptions;
|
|
|
|
export const browserCapabilities: Record<string, NativeCapability> = {
|
|
"clipboard.write": {
|
|
browser: {
|
|
supported: () => typeof navigator !== "undefined" && !!navigator.clipboard?.writeText,
|
|
run: (options) =>
|
|
navigator.clipboard.writeText(String((options as { text?: unknown })?.text ?? "")),
|
|
},
|
|
},
|
|
share: {
|
|
browser: {
|
|
supported: () => typeof navigator !== "undefined" && typeof navigator.share === "function",
|
|
run: (options) => navigator.share(options as ShareOptions),
|
|
},
|
|
},
|
|
geolocation: {
|
|
browser: {
|
|
supported: () => typeof navigator !== "undefined" && !!navigator.geolocation,
|
|
run: (options) =>
|
|
new Promise((resolve, reject) =>
|
|
navigator.geolocation.getCurrentPosition(resolve, reject, options as PositionOptions),
|
|
),
|
|
},
|
|
},
|
|
network: {
|
|
browser: {
|
|
supported: () => typeof navigator !== "undefined",
|
|
run: () => ({ connected: navigator.onLine }),
|
|
},
|
|
},
|
|
camera: {
|
|
browser: {
|
|
supported: () => typeof navigator !== "undefined" && !!navigator.mediaDevices?.getUserMedia,
|
|
run: (options) =>
|
|
navigator.mediaDevices.getUserMedia({ video: options ?? true, audio: false }),
|
|
},
|
|
},
|
|
"storage.get": {
|
|
browser: {
|
|
supported: () => typeof localStorage !== "undefined",
|
|
run: (options) => ({
|
|
value: localStorage.getItem(String((options as { key?: unknown })?.key ?? "")),
|
|
}),
|
|
},
|
|
},
|
|
"storage.set": {
|
|
browser: {
|
|
supported: () => typeof localStorage !== "undefined",
|
|
run: (options) => {
|
|
const value = options as { key?: unknown; value?: unknown };
|
|
localStorage.setItem(String(value?.key ?? ""), String(value?.value ?? ""));
|
|
},
|
|
},
|
|
},
|
|
"notifications.schedule": {
|
|
browser: {
|
|
supported: () => typeof Notification !== "undefined",
|
|
run: async (options) => {
|
|
if (Notification.permission === "default") await Notification.requestPermission();
|
|
if (Notification.permission !== "granted")
|
|
throw new Error("Notification permission was not granted");
|
|
const value = options as { title?: string; body?: string };
|
|
return new Notification(value?.title ?? "Notification", { body: value?.body });
|
|
},
|
|
},
|
|
},
|
|
"device.info": {
|
|
browser: {
|
|
supported: () => typeof navigator !== "undefined",
|
|
run: () => ({
|
|
platform: "web",
|
|
userAgent: navigator.userAgent,
|
|
language: navigator.language,
|
|
}),
|
|
},
|
|
},
|
|
haptics: {
|
|
browser: {
|
|
supported: () => typeof navigator !== "undefined" && typeof navigator.vibrate === "function",
|
|
run: (options) => navigator.vibrate((options as { duration?: number })?.duration ?? 20),
|
|
},
|
|
},
|
|
"filesystem.read": {
|
|
browser: {
|
|
supported: () => typeof window !== "undefined" && "showOpenFilePicker" in window,
|
|
run: async () => {
|
|
const picker = (
|
|
window as unknown as Window & {
|
|
showOpenFilePicker(): Promise<Array<{ getFile(): Promise<File> }>>;
|
|
}
|
|
).showOpenFilePicker;
|
|
const [handle] = await picker.call(window);
|
|
return handle?.getFile();
|
|
},
|
|
},
|
|
},
|
|
"filesystem.write": {
|
|
browser: {
|
|
supported: () => typeof window !== "undefined" && "showSaveFilePicker" in window,
|
|
run: async (options) => {
|
|
const picker = (
|
|
window as unknown as Window & {
|
|
showSaveFilePicker(): Promise<{
|
|
createWritable(): Promise<{
|
|
write(value: unknown): Promise<void>;
|
|
close(): Promise<void>;
|
|
}>;
|
|
}>;
|
|
}
|
|
).showSaveFilePicker;
|
|
const writable = await (await picker.call(window)).createWritable();
|
|
await writable.write((options as { data?: unknown })?.data ?? "");
|
|
await writable.close();
|
|
},
|
|
},
|
|
},
|
|
};
|