refactor: split online and offline create

This commit is contained in:
enes 2025-01-04 20:36:14 +01:00
parent 9a1d3d98bf
commit dbcc96aca2

View File

@ -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<ArrayBuffer | null> => {
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
</Button>
<ButtonUnderline onClick={handleCreateOffline}>
<FontAwesomeIcon icon={faDownload} />
Create and export locally
</ButtonUnderline>
{!!error && (
<FormHelperText error={!!error}>{error}</FormHelperText>
)}