import { SyntheticEvent, useEffect, useState } from 'react' export function dataURItoBlob(dataURI: string) { const mime = dataURI.split(',')[0].split(':')[1].split(';')[0] const binary = atob(dataURI.split(',')[1]) const array = [] for (let i = 0; i < binary.length; i += 1) { array.push(binary.charCodeAt(i)) } return new Blob([new Uint8Array(array)], { type: mime }) } // const dataURItoBlob = (dataURI: string) => { // const bytes = // dataURI.split(',')[0].indexOf('base64') >= 0 // ? atob(dataURI.split(',')[1]) // : unescape(dataURI.split(',')[1]) // const mime = dataURI.split(',')[0].split(':')[1].split(';')[0] // const max = bytes.length // const ia = new Uint8Array(max) // for (var i = 0; i < max; i++) ia[i] = bytes.charCodeAt(i) // return new Blob([ia], { type: mime }) // } export function downloadImage(uri: string, name: string) { const link = document.createElement('a') link.href = uri link.download = name // this is necessary as link.click() does not work on the latest firefox link.dispatchEvent( new MouseEvent('click', { bubbles: true, cancelable: true, view: window, }) ) setTimeout(() => { // For Firefox it is necessary to delay revoking the ObjectURL // window.URL.revokeObjectURL(base64) link.remove() }, 100) } export function shareImage(base64: string, name: string) { const blob = dataURItoBlob(base64) const filesArray = [new File([blob], name, { type: 'image/jpeg' })] const shareData = { files: filesArray, } // eslint-disable-nextline const nav: any = navigator const canShare = nav.canShare && nav.canShare(shareData) const userAgent = navigator.userAgent || navigator.vendor const isMobile = /android|iPad|iPhone|iPod/i.test(userAgent) if (canShare && isMobile) { navigator.share(shareData) return true } return false } export function loadImage(image: HTMLImageElement, src: string) { return new Promise((resolve, reject) => { const initSRC = image.src const img = image img.onload = resolve img.onerror = err => { img.src = initSRC reject(err) } img.src = src }) } export function useImage(file?: File): [HTMLImageElement, boolean] { const [image] = useState(new Image()) const [isLoaded, setIsLoaded] = useState(false) useEffect(() => { if (file === undefined) { return } image.onload = () => { setIsLoaded(true) } setIsLoaded(false) image.src = URL.createObjectURL(file) return () => { image.onload = null } }, [file, image]) return [image, isLoaded] } // https://stackoverflow.com/questions/23945494/use-html5-to-resize-an-image-before-upload interface ResizeImageFileResult { file: File resized: boolean originalWidth?: number originalHeight?: number } export function resizeImageFile( file: File, maxSize: number ): Promise { const reader = new FileReader() const image = new Image() const canvas = document.createElement('canvas') const resize = (): ResizeImageFileResult => { let { width, height } = image if (width > height) { if (width > maxSize) { height *= maxSize / width width = maxSize } } else if (height > maxSize) { width *= maxSize / height height = maxSize } if (width === image.width && height === image.height) { return { file, resized: false } } canvas.width = width canvas.height = height const ctx = canvas.getContext('2d') if (!ctx) { throw new Error('could not get context') } canvas.getContext('2d')?.drawImage(image, 0, 0, width, height) const dataUrl = canvas.toDataURL('image/jpeg') const blob = dataURItoBlob(dataUrl) const f = new File([blob], file.name, { type: file.type, }) return { file: f, resized: true, originalWidth: image.width, originalHeight: image.height, } } return new Promise((resolve, reject) => { if (!file.type.match(/image.*/)) { reject(new Error('Not an image')) return } reader.onload = (readerEvent: any) => { image.onload = () => resolve(resize()) image.src = readerEvent.target.result } reader.readAsDataURL(file) }) } export function keepGUIAlive() { async function getRequest(url = '') { const response = await fetch(url, { method: 'GET', cache: 'no-cache', }) return response.json() } const keepAliveServer = () => { const url = document.location const route = '/flaskwebgui-keep-server-alive' getRequest(url + route).then(data => { return data }) } const intervalRequest = 3 * 1000 keepAliveServer() setInterval(keepAliveServer, intervalRequest) } export function isRightClick(ev: SyntheticEvent) { const mouseEvent = ev.nativeEvent as MouseEvent return mouseEvent.button === 2 } export function isMidClick(ev: SyntheticEvent) { const mouseEvent = ev.nativeEvent as MouseEvent return mouseEvent.button === 1 } export function srcToFile(src: string, fileName: string, mimeType: string) { return fetch(src) .then(function (res) { return res.arrayBuffer() }) .then(function (buf) { return new File([buf], fileName, { type: mimeType }) }) } export async function askWritePermission() { try { // The clipboard-write permission is granted automatically to pages // when they are the active tab. So it's not required, but it's more safe. const { state } = await navigator.permissions.query({ name: 'clipboard-write' as PermissionName, }) return state === 'granted' } catch (error) { // Browser compatibility / Security error (ONLY HTTPS) ... return false } } function canvasToBlob(canvas: HTMLCanvasElement, mime: string): Promise { return new Promise((resolve, reject) => canvas.toBlob(async d => { if (d) { resolve(d) } else { reject(new Error('Expected toBlob() to be defined')) } }, mime) ) } const setToClipboard = async (blob: any) => { const data = [new ClipboardItem({ [blob.type]: blob })] await navigator.clipboard.write(data) } export async function copyCanvasImage(canvas: HTMLCanvasElement) { const blob = await canvasToBlob(canvas, 'image/png') try { await setToClipboard(blob) } catch { console.log('Copy image failed!') } }