release #306
@ -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>
|
||||
)}
|
||||
|
Loading…
x
Reference in New Issue
Block a user