From dbcc96aca22e2d70996d9fc84a26592bc7d92055 Mon Sep 17 00:00:00 2001 From: enes Date: Sat, 4 Jan 2025 20:36:14 +0100 Subject: [PATCH] refactor: split online and offline create --- src/pages/create/index.tsx | 295 +++++++++++++++++++++---------------- 1 file changed, 164 insertions(+), 131 deletions(-) diff --git a/src/pages/create/index.tsx b/src/pages/create/index.tsx index bd3893e..3364c83 100644 --- a/src/pages/create/index.tsx +++ b/src/pages/create/index.tsx @@ -44,7 +44,6 @@ import { generateKeysFile, getHash, hexToNpub, - isOnline, unixNow, npubToHex, queryNip05, @@ -65,6 +64,7 @@ import { Mark } from '../../types/mark.ts' import { StickySideColumns } from '../../layouts/StickySideColumns.tsx' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { + faDownload, faEllipsis, faEye, faFile, @@ -84,6 +84,7 @@ import _, { truncate } from 'lodash' import * as React from 'react' import { AvatarIconButton } from '../../components/UserAvatarIconButton' import { useImmer } from 'use-immer' +import { ButtonUnderline } from '../../components/ButtonUnderline/index.tsx' type FoundUser = Event & { npub: string } @@ -774,30 +775,6 @@ export const CreatePage = () => { .catch(handleUploadError) } - // Manage offline scenarios for signing or viewing the file - const handleOfflineFlow = async ( - encryptedArrayBuffer: ArrayBuffer, - encryptionKey: string - ) => { - const finalZipFile = await createFinalZipFile( - encryptedArrayBuffer, - encryptionKey - ) - - if (!finalZipFile) { - setIsLoading(false) - return - } - - saveAs(finalZipFile, `request-${unixNow()}.sigit.zip`) - - // If user is the next signer, we can navigate directly to sign page - if (signers[0].pubkey === usersPubkey) { - navigate(appPrivateRoutes.sign, { state: { uploadedZip: finalZipFile } }) - } - setIsLoading(false) - } - const generateFilesZip = async (): Promise => { const zip = new JSZip() selectedFiles.forEach((file) => { @@ -863,7 +840,7 @@ export const CreatePage = () => { return e.id } - const handleCreate = async () => { + const initCreation = async () => { try { if (!validateInputs()) return @@ -875,132 +852,183 @@ export const CreatePage = () => { setLoadingSpinnerDesc('Generating encryption key') const encryptionKey = await generateEncryptionKey() - if (await isOnline()) { - setLoadingSpinnerDesc('generating files.zip') - const arrayBuffer = await generateFilesZip() - if (!arrayBuffer) return + setLoadingSpinnerDesc('Creating marks') + const markConfig = createMarks(fileHashes) - setLoadingSpinnerDesc('Encrypting files.zip') - const encryptedArrayBuffer = await encryptZipFile( - arrayBuffer, - encryptionKey - ) + return { + encryptionKey, + markConfig, + fileHashes + } + } catch (error) { + if (error instanceof Error) { + toast.error(error.message) + } + console.error(error) + setIsLoading(false) + } + } - const markConfig = createMarks(fileHashes) + const handleCreate = async () => { + try { + const result = await initCreation() + if (!result) return - setLoadingSpinnerDesc('Uploading files.zip to file storage') - const fileUrl = await uploadFile(encryptedArrayBuffer) - if (!fileUrl) return + const { encryptionKey, markConfig, fileHashes } = result - setLoadingSpinnerDesc('Generating create signature') - const createSignature = await generateCreateSignature( - markConfig, - fileHashes, - fileUrl - ) - if (!createSignature) return + setLoadingSpinnerDesc('generating files.zip') + const arrayBuffer = await generateFilesZip() + if (!arrayBuffer) return - setLoadingSpinnerDesc('Generating keys for decryption') + setLoadingSpinnerDesc('Encrypting files.zip') + const encryptedArrayBuffer = await encryptZipFile( + arrayBuffer, + encryptionKey + ) - // generate key pairs for decryption - const pubkeys = users.map((user) => user.pubkey) - // also add creator in the list - if (pubkeys.includes(usersPubkey!)) { - pubkeys.push(usersPubkey!) - } + setLoadingSpinnerDesc('Uploading files.zip to file storage') + const fileUrl = await uploadFile(encryptedArrayBuffer) + if (!fileUrl) return - const keys = await generateKeys(pubkeys, encryptionKey) - if (!keys) return + setLoadingSpinnerDesc('Generating create signature') + const createSignature = await generateCreateSignature( + markConfig, + fileHashes, + fileUrl + ) + if (!createSignature) return - setLoadingSpinnerDesc('Generating an open timestamp.') + setLoadingSpinnerDesc('Generating keys for decryption') - const timestamp = await generateTimestamp( - extractNostrId(createSignature) - ) + // generate key pairs for decryption + const pubkeys = users.map((user) => user.pubkey) + // also add creator in the list + if (pubkeys.includes(usersPubkey!)) { + pubkeys.push(usersPubkey!) + } - const meta: Meta = { - createSignature, - keys, - modifiedAt: unixNow(), - docSignatures: {} - } + const keys = await generateKeys(pubkeys, encryptionKey) + if (!keys) return - if (timestamp) { - meta.timestamps = [timestamp] - } + setLoadingSpinnerDesc('Generating an open timestamp.') - setLoadingSpinnerDesc('Updating user app data') + const timestamp = await generateTimestamp(extractNostrId(createSignature)) - const event = await updateUsersAppData(meta) - if (!event) return + const meta: Meta = { + createSignature, + keys, + modifiedAt: unixNow(), + docSignatures: {} + } - const metaUrl = await uploadMetaToFileStorage(meta, encryptionKey) + if (timestamp) { + meta.timestamps = [timestamp] + } - setLoadingSpinnerDesc('Sending notifications to counterparties') - const promises = sendNotifications({ - metaUrl, - keys: meta.keys + setLoadingSpinnerDesc('Updating user app data') + + const event = await updateUsersAppData(meta) + if (!event) return + + const metaUrl = await uploadMetaToFileStorage(meta, encryptionKey) + + setLoadingSpinnerDesc('Sending notifications to counterparties') + const promises = sendNotifications({ + metaUrl, + keys: meta.keys + }) + + await Promise.all(promises) + .then(() => { + toast.success('Notifications sent successfully') + }) + .catch(() => { + toast.error('Failed to publish notifications') }) - await Promise.all(promises) - .then(() => { - toast.success('Notifications sent successfully') - }) - .catch(() => { - toast.error('Failed to publish notifications') - }) + const isFirstSigner = signers[0].pubkey === usersPubkey - const isFirstSigner = signers[0].pubkey === usersPubkey - - if (isFirstSigner) { - navigate(appPrivateRoutes.sign, { state: { meta } }) - } else { - const createSignatureJson = JSON.parse(createSignature) - navigate(`${appPublicRoutes.verify}/${createSignatureJson.id}`) - } + if (isFirstSigner) { + navigate(appPrivateRoutes.sign, { state: { meta } }) } else { - const zip = new JSZip() + const createSignatureJson = JSON.parse(createSignature) + navigate(`${appPublicRoutes.verify}/${createSignatureJson.id}`) + } + } catch (error) { + if (error instanceof Error) { + toast.error(error.message) + } + console.error(error) + } finally { + setIsLoading(false) + } + } - selectedFiles.forEach((file) => { - zip.file(`files/${file.name}`, file) + const handleCreateOffline = async () => { + try { + const result = await initCreation() + if (!result) return + + const { encryptionKey, markConfig, fileHashes } = result + + const zip = new JSZip() + + selectedFiles.forEach((file) => { + zip.file(`files/${file.name}`, file) + }) + + setLoadingSpinnerDesc('Generating create signature') + const createSignature = await generateCreateSignature( + markConfig, + fileHashes, + '' + ) + if (!createSignature) return + + const meta: Meta = { + createSignature, + modifiedAt: unixNow(), + docSignatures: {} + } + + // add meta to zip + try { + const stringifiedMeta = JSON.stringify(meta, null, 2) + zip.file('meta.json', stringifiedMeta) + } catch (err) { + console.error(err) + toast.error('An error occurred in converting meta json to string') + return null + } + + const arrayBuffer = await generateZipFile(zip) + if (!arrayBuffer) return + + setLoadingSpinnerDesc('Encrypting zip file') + const encryptedArrayBuffer = await encryptZipFile( + arrayBuffer, + encryptionKey + ) + + const finalZipFile = await createFinalZipFile( + encryptedArrayBuffer, + encryptionKey + ) + + if (!finalZipFile) { + setIsLoading(false) + return + } + + saveAs(finalZipFile, `request-${unixNow()}.sigit.zip`) + + // If user is the next signer, we can navigate directly to sign page + if (signers[0].pubkey === usersPubkey) { + navigate(appPrivateRoutes.sign, { + state: { uploadedZip: finalZipFile } }) - - const markConfig = createMarks(fileHashes) - - setLoadingSpinnerDesc('Generating create signature') - const createSignature = await generateCreateSignature( - markConfig, - fileHashes, - '' - ) - if (!createSignature) return - - const meta: Meta = { - createSignature, - modifiedAt: unixNow(), - docSignatures: {} - } - - // add meta to zip - try { - const stringifiedMeta = JSON.stringify(meta, null, 2) - zip.file('meta.json', stringifiedMeta) - } catch (err) { - console.error(err) - toast.error('An error occurred in converting meta json to string') - return null - } - - const arrayBuffer = await generateZipFile(zip) - if (!arrayBuffer) return - - setLoadingSpinnerDesc('Encrypting zip file') - const encryptedArrayBuffer = await encryptZipFile( - arrayBuffer, - encryptionKey - ) - - await handleOfflineFlow(encryptedArrayBuffer, encryptionKey) + } else { + navigate(appPrivateRoutes.homePage) } } catch (error) { if (error instanceof Error) { @@ -1258,6 +1286,11 @@ export const CreatePage = () => { Publish + + + Create and export locally + + {!!error && ( {error} )}