In offline mode create a wrapper zip file #110
@ -421,38 +421,39 @@ export const CreatePage = () => {
|
||||
return finalZipFile
|
||||
}
|
||||
|
||||
// Handle file upload and further actions based on online/offline status
|
||||
const handleFileUpload = async (file: File, arrayBuffer: ArrayBuffer) => {
|
||||
if (await isOnline()) {
|
||||
const fileUrl = await uploadFile(file)
|
||||
const handleOnlineFlow = async (
|
||||
encryptedArrayBuffer: ArrayBuffer,
|
||||
encryptionKey: string
|
||||
) => {
|
||||
const unixNow = Math.floor(Date.now() / 1000)
|
||||
const blob = new Blob([encryptedArrayBuffer])
|
||||
// Create a File object with the Blob data
|
||||
const file = new File([blob], `compressed-${unixNow}.sigit`, {
|
||||
type: 'application/sigit'
|
||||
})
|
||||
|
||||
if (!fileUrl) return
|
||||
const fileUrl = await uploadFile(file)
|
||||
if (!fileUrl) return
|
||||
|
||||
await sendDMs(fileUrl)
|
||||
setIsLoading(false)
|
||||
|
||||
navigate(appPrivateRoutes.sign, { state: { arrayBuffer } })
|
||||
} else {
|
||||
handleOffline(file, arrayBuffer)
|
||||
}
|
||||
await sendDMs(fileUrl, encryptionKey)
|
||||
}
|
||||
|
||||
// Handle errors during file upload
|
||||
const handleUploadError = (err: any) => {
|
||||
console.log('Error in upload:>> ', err)
|
||||
setIsLoading(false)
|
||||
toast.error(err.message || 'Error occurred in uploading zip file')
|
||||
toast.error(err.message || 'Error occurred in uploading file')
|
||||
return null
|
||||
}
|
||||
|
||||
// Upload the file to the storage and send DMs to signers/viewers
|
||||
// Upload the file to the storage
|
||||
const uploadFile = async (file: File): Promise<string | null> => {
|
||||
setIsLoading(true)
|
||||
setLoadingSpinnerDesc('Uploading zip file to file storage.')
|
||||
setLoadingSpinnerDesc('Uploading sigit to file storage.')
|
||||
|
||||
const fileUrl = await uploadToFileStorage(file, nostrController)
|
||||
.then((url) => {
|
||||
toast.success('zip file uploaded to file storage')
|
||||
toast.success('Sigit uploaded to file storage')
|
||||
return url
|
||||
})
|
||||
.catch(handleUploadError)
|
||||
@ -461,7 +462,7 @@ export const CreatePage = () => {
|
||||
}
|
||||
|
||||
// Send DMs to signers and viewers with the file URL
|
||||
const sendDMs = async (fileUrl: string) => {
|
||||
const sendDMs = async (fileUrl: string, encryptionKey: string) => {
|
||||
setLoadingSpinnerDesc('Sending DM to signers/viewers')
|
||||
|
||||
const signers = users.filter((user) => user.role === UserRole.signer)
|
||||
@ -470,6 +471,7 @@ export const CreatePage = () => {
|
||||
if (signers.length > 0) {
|
||||
await sendDM(
|
||||
fileUrl,
|
||||
encryptionKey,
|
||||
signers[0].pubkey,
|
||||
nostrController,
|
||||
true,
|
||||
@ -477,15 +479,31 @@ export const CreatePage = () => {
|
||||
)
|
||||
} else {
|
||||
for (const viewer of viewers) {
|
||||
await sendDM(fileUrl, viewer.pubkey, nostrController, false, setAuthUrl)
|
||||
await sendDM(
|
||||
fileUrl,
|
||||
encryptionKey,
|
||||
viewer.pubkey,
|
||||
nostrController,
|
||||
false,
|
||||
setAuthUrl
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Manage offline scenarios for signing or viewing the file
|
||||
const handleOffline = (file: File, arrayBuffer: ArrayBuffer) => {
|
||||
saveAs(file, 'request.sigit.zip')
|
||||
navigate(appPrivateRoutes.sign, { state: { arrayBuffer } })
|
||||
const handleOfflineFlow = async (
|
||||
encryptedArrayBuffer: ArrayBuffer,
|
||||
encryptionKey: string
|
||||
) => {
|
||||
const finalZipFile = await createFinalZipFile(
|
||||
encryptedArrayBuffer,
|
||||
encryptionKey
|
||||
)
|
||||
|
||||
if (!finalZipFile) return
|
||||
|
||||
saveAs(finalZipFile, 'request.sigit.zip')
|
||||
}
|
||||
|
||||
const handleCreate = async () => {
|
||||
@ -507,25 +525,24 @@ export const CreatePage = () => {
|
||||
|
||||
setLoadingSpinnerDesc('Generating zip file')
|
||||
|
||||
const arraybuffer = await generateZipFile(zip)
|
||||
if (!arraybuffer) return
|
||||
const arrayBuffer = await generateZipFile(zip)
|
||||
if (!arrayBuffer) return
|
||||
|
||||
const encryptionKey = await generateEncryptionKey()
|
||||
|
||||
setLoadingSpinnerDesc('Encrypting zip file')
|
||||
const encryptedArrayBuffer = await encryptZipFile(
|
||||
arraybuffer,
|
||||
arrayBuffer,
|
||||
encryptionKey
|
||||
)
|
||||
|
||||
const finalZipFile = await createFinalZipFile(
|
||||
encryptedArrayBuffer,
|
||||
encryptionKey
|
||||
)
|
||||
if (await isOnline()) {
|
||||
await handleOnlineFlow(encryptedArrayBuffer, encryptionKey)
|
||||
} else {
|
||||
await handleOfflineFlow(encryptedArrayBuffer, encryptionKey)
|
||||
}
|
||||
|
||||
if (!finalZipFile) return
|
||||
|
||||
return await handleFileUpload(finalZipFile, arraybuffer)
|
||||
navigate(appPrivateRoutes.sign, { state: { arrayBuffer } })
|
||||
}
|
||||
|
||||
if (authUrl) {
|
||||
|
@ -42,7 +42,7 @@ export const SignPage = () => {
|
||||
const location = useLocation()
|
||||
const { arrayBuffer: decryptedArrayBuffer } = location.state || {}
|
||||
|
||||
const [searchParams] = useSearchParams()
|
||||
const [searchParams, setSearchParams] = useSearchParams()
|
||||
|
||||
const [displayInput, setDisplayInput] = useState(false)
|
||||
|
||||
@ -160,8 +160,9 @@ export const SignPage = () => {
|
||||
|
||||
useEffect(() => {
|
||||
const fileUrl = searchParams.get('file')
|
||||
const key = searchParams.get('key')
|
||||
|
||||
if (fileUrl) {
|
||||
if (fileUrl && key) {
|
||||
setIsLoading(true)
|
||||
setLoadingSpinnerDesc('Fetching file from file server')
|
||||
|
||||
@ -169,13 +170,22 @@ export const SignPage = () => {
|
||||
.get(fileUrl, {
|
||||
responseType: 'arraybuffer'
|
||||
})
|
||||
.then((res) => {
|
||||
.then(async (res) => {
|
||||
const fileName = fileUrl.split('/').pop()
|
||||
const file = new File([res.data], fileName!)
|
||||
|
||||
decrypt(file).then((arrayBuffer) => {
|
||||
if (arrayBuffer) handleDecryptedArrayBuffer(arrayBuffer)
|
||||
const encryptedArrayBuffer = await file.arrayBuffer()
|
||||
|
||||
const arrayBuffer = await decryptArrayBuffer(
|
||||
encryptedArrayBuffer,
|
||||
key
|
||||
).catch((err) => {
|
||||
console.log('err in decryption:>> ', err)
|
||||
toast.error(err.message || 'An error occurred in decrypting file.')
|
||||
return null
|
||||
})
|
||||
|
||||
if (arrayBuffer) handleDecryptedArrayBuffer(arrayBuffer)
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(`error occurred in getting file from ${fileUrl}`, err)
|
||||
@ -268,8 +278,6 @@ export const SignPage = () => {
|
||||
setAuthUrl(undefined) // Clear authentication URL
|
||||
})
|
||||
|
||||
console.log('encryptionKey :>> ', encryptionKey)
|
||||
|
||||
// Return if encryption failed
|
||||
if (!encryptionKey) continue
|
||||
|
||||
@ -438,15 +446,11 @@ export const SignPage = () => {
|
||||
setLoadingSpinnerDesc('Encrypting zip file')
|
||||
const encryptedArrayBuffer = await encryptArrayBuffer(arrayBuffer, key)
|
||||
|
||||
const finalZipFile = await createFinalZipFile(encryptedArrayBuffer, key)
|
||||
|
||||
if (!finalZipFile) return
|
||||
|
||||
if (await isOnline()) {
|
||||
await handleOnlineFlow(finalZipFile)
|
||||
await handleOnlineFlow(encryptedArrayBuffer, key)
|
||||
} else {
|
||||
handleDecryptedArrayBuffer(arrayBuffer).finally(() => setIsLoading(false))
|
||||
}
|
||||
|
||||
handleDecryptedArrayBuffer(arrayBuffer).finally(() => setIsLoading(false))
|
||||
}
|
||||
|
||||
// Read the content of the hashes.json file
|
||||
@ -600,35 +604,56 @@ export const SignPage = () => {
|
||||
}
|
||||
|
||||
// Handle the online flow: upload file and send DMs
|
||||
const handleOnlineFlow = async (file: File) => {
|
||||
const fileUrl = await uploadZipFile(file)
|
||||
const handleOnlineFlow = async (
|
||||
encryptedArrayBuffer: ArrayBuffer,
|
||||
encryptionKey: string
|
||||
) => {
|
||||
const unixNow = Math.floor(Date.now() / 1000)
|
||||
const blob = new Blob([encryptedArrayBuffer])
|
||||
// Create a File object with the Blob data
|
||||
const file = new File([blob], `compressed-${unixNow}.sigit`, {
|
||||
type: 'application/sigit'
|
||||
})
|
||||
|
||||
const fileUrl = await uploadFile(file)
|
||||
if (!fileUrl) return
|
||||
|
||||
const isLastSigner = checkIsLastSigner(signers)
|
||||
|
||||
if (isLastSigner) {
|
||||
await sendDMToAllUsers(fileUrl)
|
||||
await sendDMToAllUsers(fileUrl, encryptionKey)
|
||||
} else {
|
||||
await sendDMToNextSigner(fileUrl)
|
||||
await sendDMToNextSigner(fileUrl, encryptionKey)
|
||||
}
|
||||
|
||||
// update search params with updated file url and encryption key
|
||||
setSearchParams({
|
||||
file: fileUrl,
|
||||
key: encryptionKey
|
||||
})
|
||||
|
||||
setIsLoading(false)
|
||||
}
|
||||
|
||||
// Upload the zip file to file storage
|
||||
const uploadZipFile = async (file: File): Promise<string | null> => {
|
||||
setLoadingSpinnerDesc('Uploading zip file to file storage.')
|
||||
// Handle errors during file upload
|
||||
const handleUploadError = (err: any) => {
|
||||
console.log('Error in upload:>> ', err)
|
||||
setIsLoading(false)
|
||||
toast.error(err.message || 'Error occurred in uploading file')
|
||||
return null
|
||||
}
|
||||
|
||||
// Upload the file to file storage
|
||||
const uploadFile = async (file: File): Promise<string | null> => {
|
||||
setIsLoading(true)
|
||||
setLoadingSpinnerDesc('Uploading sigit file to file storage.')
|
||||
|
||||
const fileUrl = await uploadToFileStorage(file, nostrController)
|
||||
.then((url) => {
|
||||
toast.success('Zip file uploaded to file storage')
|
||||
toast.success('Sigit uploaded to file storage')
|
||||
return url
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log('Error uploading file:', err)
|
||||
setIsLoading(false)
|
||||
toast.error(err.message || 'Error uploading file')
|
||||
return null
|
||||
})
|
||||
.catch(handleUploadError)
|
||||
|
||||
return fileUrl
|
||||
}
|
||||
@ -642,7 +667,7 @@ export const SignPage = () => {
|
||||
}
|
||||
|
||||
// Send DM to all users (signers and viewers)
|
||||
const sendDMToAllUsers = async (fileUrl: string) => {
|
||||
const sendDMToAllUsers = async (fileUrl: string, encryptionKey: string) => {
|
||||
const userSet = new Set<`npub1${string}`>()
|
||||
|
||||
if (submittedBy) {
|
||||
@ -662,6 +687,7 @@ export const SignPage = () => {
|
||||
for (const user of users) {
|
||||
await sendDM(
|
||||
fileUrl,
|
||||
encryptionKey,
|
||||
npubToHex(user)!,
|
||||
nostrController,
|
||||
false,
|
||||
@ -671,12 +697,13 @@ export const SignPage = () => {
|
||||
}
|
||||
|
||||
// Send DM to the next signer
|
||||
const sendDMToNextSigner = async (fileUrl: string) => {
|
||||
const sendDMToNextSigner = async (fileUrl: string, encryptionKey: string) => {
|
||||
const usersNpub = hexToNpub(usersPubkey!)
|
||||
const signerIndex = signers.indexOf(usersNpub)
|
||||
const nextSigner = signers[signerIndex + 1]
|
||||
await sendDM(
|
||||
fileUrl,
|
||||
encryptionKey,
|
||||
npubToHex(nextSigner)!,
|
||||
nostrController,
|
||||
true,
|
||||
@ -776,8 +803,8 @@ export const SignPage = () => {
|
||||
const finalZipFile = await createFinalZipFile(encryptedArrayBuffer, key)
|
||||
|
||||
if (!finalZipFile) return
|
||||
|
||||
saveAs(finalZipFile, 'exported.sigit.zip')
|
||||
const unixNow = Math.floor(Date.now() / 1000)
|
||||
saveAs(finalZipFile, `exported-${unixNow}.sigit.zip`)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -915,14 +942,14 @@ export const SignPage = () => {
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{isSignerOrCreator &&
|
||||
signedStatus === SignedStatus.User_Is_Not_Next_Signer && (
|
||||
<Box sx={{ mt: 1, display: 'flex', justifyContent: 'center' }}>
|
||||
<Button onClick={handleExportSigit} variant="contained">
|
||||
Export Sigit
|
||||
</Button>
|
||||
</Box>
|
||||
)}
|
||||
{/* todo: In offline mode export sigit is not visible after last signer has signed*/}
|
||||
{isSignerOrCreator && (
|
||||
<Box sx={{ mt: 1, display: 'flex', justifyContent: 'center' }}>
|
||||
<Button onClick={handleExportSigit} variant="contained">
|
||||
Export Sigit
|
||||
</Button>
|
||||
</Box>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
|
@ -56,6 +56,7 @@ export const uploadToFileStorage = async (
|
||||
/**
|
||||
* Sends a Direct Message (DM) to a recipient, encrypting the content and handling authentication.
|
||||
* @param fileUrl The URL of the encrypted zip file to be included in the DM.
|
||||
* @param encryptionKey The encryption key used to decrypt the zip file to be included in the DM.
|
||||
* @param pubkey The public key of the recipient.
|
||||
* @param nostrController The NostrController instance for handling authentication and encryption.
|
||||
* @param isSigner Boolean indicating whether the recipient is a signer or viewer.
|
||||
@ -63,6 +64,7 @@ export const uploadToFileStorage = async (
|
||||
*/
|
||||
export const sendDM = async (
|
||||
fileUrl: string,
|
||||
encryptionKey: string,
|
||||
pubkey: string,
|
||||
nostrController: NostrController,
|
||||
isSigner: boolean,
|
||||
@ -75,7 +77,9 @@ export const sendDM = async (
|
||||
|
||||
const decryptionUrl = `${window.location.origin}/#${
|
||||
appPrivateRoutes.sign
|
||||
}?file=${encodeURIComponent(fileUrl)}`
|
||||
}?file=${encodeURIComponent(fileUrl)}&key=${encodeURIComponent(
|
||||
encryptionKey
|
||||
)}`
|
||||
|
||||
const content = `${initialLine}\n\n${decryptionUrl}`
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user