web.ts (4200B)
1 import { IResultList } from "@radroots/types-bindings"; 2 import { err_msg, handle_err, type ResolveError, type ResolveStatus } from "@radroots/utils"; 3 import { cl_notifications_error } from "./error.js"; 4 import type { 5 IClientNotifications, 6 IClientNotificationsAlertResolve, 7 IClientNotificationsConfig, 8 IClientNotificationsConfirmResolve, 9 IClientNotificationsDialogConfirmOpts, 10 IClientNotificationsNotifyInitResolve, 11 IClientNotificationsNotifySendOptions, 12 IClientNotificationsNotifySendResolve 13 } from "./types.js"; 14 15 export interface IWebNotifications extends IClientNotifications { } 16 17 export class WebNotifications implements IWebNotifications { 18 private _config: IClientNotificationsConfig; 19 20 constructor(config: IClientNotificationsConfig = { app_name: "Radroots" }) { 21 this._config = config; 22 } 23 public async alert(opts: string, title?: string, kind?: ResolveStatus): Promise<IClientNotificationsAlertResolve> { 24 try { 25 const msg = title ? `${title}\n\n${opts}` : opts; 26 window.alert(msg); 27 return true; 28 } catch (e) { 29 handle_err(e); 30 return false; 31 } 32 } 33 34 public async confirm(opts: IClientNotificationsDialogConfirmOpts): Promise<IClientNotificationsConfirmResolve> { 35 try { 36 const msg = typeof opts === 'string' ? opts : opts.message 37 return window.confirm(msg); 38 } catch (e) { 39 handle_err(e); 40 return false; 41 } 42 } 43 44 public async notify_init(): Promise<IClientNotificationsNotifyInitResolve> { 45 try { 46 if (!("Notification" in window)) return "unavailable"; 47 if (Notification.permission === 'granted') return "granted"; 48 return await Notification.requestPermission(); 49 } catch (e) { 50 return handle_err(e); 51 } 52 } 53 54 public async notify_send(opts: string | IClientNotificationsNotifySendOptions): Promise<IClientNotificationsNotifySendResolve> { 55 try { 56 if (!("Notification" in window)) return err_msg(cl_notifications_error.unavailable); 57 if (Notification.permission !== "granted") { 58 const permission = await this.notify_init(); 59 if (permission !== "granted") return err_msg(cl_notifications_error.unavailable); 60 } 61 if (typeof opts === "string") return new Notification(this._config.app_name, { body: opts }); 62 else return new Notification(opts.title || this._config.app_name, { body: opts.body }); 63 } catch (e) { 64 return handle_err(e); 65 } 66 } 67 68 private async read_photo_data(file: File): Promise<string> { 69 return await new Promise<string>((resolve, reject) => { 70 const reader = new FileReader(); 71 reader.onload = () => { 72 if (typeof reader.result === "string") return resolve(reader.result); 73 return reject(new Error(cl_notifications_error.read_failure)); 74 }; 75 reader.onerror = () => { 76 if (reader.error) return reject(reader.error); 77 return reject(new Error(cl_notifications_error.read_failure)); 78 }; 79 reader.readAsDataURL(file); 80 }); 81 } 82 83 public async open_photos(): Promise<ResolveError<IResultList<string> | undefined>> { 84 try { 85 const files = await new Promise<FileList | null>((resolve) => { 86 const input = document.createElement('input'); 87 input.type = 'file'; 88 input.multiple = true; 89 input.accept = 'image/png,image/jpg'; 90 input.onchange = () => resolve(input.files); 91 input.click(); 92 }); 93 if (!files) return; 94 const results: string[] = []; 95 for (let i = 0; i < files.length; i++) { 96 const file = files.item(i); 97 if (!file) continue; 98 const data_url = await this.read_photo_data(file); 99 results.push(data_url); 100 } 101 return { results }; 102 } catch (e) { 103 return handle_err(e); 104 } 105 } 106 }