From 118bf5f49e63ab83f9b5c5fb1debafc519044a78 Mon Sep 17 00:00:00 2001 From: William Chong Date: Tue, 20 Feb 2024 00:15:11 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Add=20UI=20logic=20for=20uploading?= =?UTF-8?q?=20modified=20epub=20files?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/IscnUploadForm.vue | 151 +++++++++++++++++++++++++--------- locales/en.json | 1 + 2 files changed, 112 insertions(+), 40 deletions(-) diff --git a/components/IscnUploadForm.vue b/components/IscnUploadForm.vue index 0cd8f715..e69fc6e2 100644 --- a/components/IscnUploadForm.vue +++ b/components/IscnUploadForm.vue @@ -48,7 +48,7 @@ + + {{ $t('UploadForm.label.insertISCNPage') }} +
file.fileType === 'application/epub+zip') + } + + get modifiedFileRecords() { + if (!this.isAddISCNPageToEpub || this.mode !== MODE.EDIT) return this.fileRecords + return this.fileRecords.map((record) => { + if (record.fileType === 'application/epub+zip') { + const modifiedEpubRecord = this.modifiedEpubMap[record.ipfsHash] + if (modifiedEpubRecord) { + return modifiedEpubRecord + } + } + return record + }) + } + @Watch('error') showWarning(errormsg: any) { if (errormsg) this.isOpenWarningSnackbar = true @@ -385,6 +408,32 @@ export default class IscnUploadForm extends Vue { } } + // eslint-disable-next-line class-methods-use-this + async getFileInfo(file: Blob) { + const fileBytes = (await fileToArrayBuffer( + file, + )) as unknown as ArrayBuffer + if (fileBytes) { + const [ + fileSHA256, + imageType, + ipfsHash, + // eslint-disable-next-line no-await-in-loop + ] = await Promise.all([ + digestFileSHA256(fileBytes), + readImageType(fileBytes), + Hash.of(Buffer.from(fileBytes)), + ]) + return { + fileBytes, + fileSHA256, + imageType, + ipfsHash, + } + } + return null + } + async onFileUpload(event: DragEvent) { logTrackerEvent(this, 'ISCNCreate', 'SelectFile', '', 1) this.isSizeExceeded = false @@ -412,21 +461,14 @@ export default class IscnUploadForm extends Vue { reader.readAsDataURL(file) // eslint-disable-next-line no-await-in-loop - const fileBytes = (await fileToArrayBuffer( - file, - )) as unknown as ArrayBuffer - if (fileBytes) { - const [ + const info = await this.getFileInfo(file) + if (info) { + const { + fileBytes, fileSHA256, imageType, ipfsHash, - // eslint-disable-next-line no-await-in-loop - ] = await Promise.all([ - digestFileSHA256(fileBytes), - readImageType(fileBytes), - Hash.of(Buffer.from(fileBytes)), - ]) - + } = info fileRecord = { ...fileRecord, fileName: file.name, @@ -453,7 +495,7 @@ export default class IscnUploadForm extends Vue { } if (file.type === 'application/epub+zip') { // eslint-disable-next-line no-await-in-loop - await this.processEPub({ buffer: fileBytes, file }) + await this.processEPub({ ipfsHash, buffer: fileBytes, file }) } } } else { @@ -464,10 +506,41 @@ export default class IscnUploadForm extends Vue { } } - async processEPub({ buffer, file }: { buffer: ArrayBuffer; file: File }) { + async processEPub({ ipfsHash, buffer, file }: { ipfsHash: string, buffer: ArrayBuffer; file: File }) { try { const book = ePub(buffer) await book.ready + if (this.mode === MODE.EDIT) { + const modifiedEpub = await injectISCNQRCodePage(buffer, book, this.iscnId || '') + + // eslint-disable-next-line no-await-in-loop + const info = await this.getFileInfo(modifiedEpub) + if (info) { + const { + fileSHA256: modifiedEpubSHA256, + ipfsHash: modifiedEpubIpfsHash, + } = info + + const modifiedEpubRecord: any = { + fileName: file.name, + fileSize: modifiedEpub.size, + fileType: modifiedEpub.type, + fileBlob: modifiedEpub, + ipfsHash: modifiedEpubIpfsHash, + fileSHA256: modifiedEpubSHA256, + isFileImage: false, + } + + const epubReader = new FileReader() + epubReader.onload = (e) => { + if (!e.target) return + modifiedEpubRecord.fileData = e.target.result as string + Vue.set(this.modifiedEpubMap, ipfsHash, modifiedEpubRecord) + } + epubReader.readAsDataURL(modifiedEpub) + } + } + const epubMetadata: any = {} // Get metadata @@ -510,24 +583,19 @@ export default class IscnUploadForm extends Vue { type: 'image/jpeg', }, ) - const fileBytes = (await fileToArrayBuffer( - coverFile, - )) as unknown as ArrayBuffer - if (fileBytes) { - const [ + + // eslint-disable-next-line no-await-in-loop + const coverInfo = await this.getFileInfo(coverFile) + if (coverInfo) { + const { fileSHA256, imageType, - ipfsHash, - // eslint-disable-next-line no-await-in-loop - ] = await Promise.all([ - digestFileSHA256(fileBytes), - readImageType(fileBytes), - Hash.of(Buffer.from(fileBytes)), - ]) + ipfsHash: ipfsThumbnailHash, + } = coverInfo - epubMetadata.thumbnailIpfsHash = ipfsHash + epubMetadata.thumbnailIpfsHash = ipfsThumbnailHash - const fileRecord: any = { + const coverFileRecord: any = { fileName: coverFile.name, fileSize: coverFile.size, fileType: coverFile.type, @@ -536,13 +604,13 @@ export default class IscnUploadForm extends Vue { fileSHA256, isFileImage: !!imageType, } - const reader = new FileReader() - reader.onload = (e) => { + const coverReader = new FileReader() + coverReader.onload = (e) => { if (!e.target) return - fileRecord.fileData = e.target.result as string - this.fileRecords.push(fileRecord) + coverFileRecord.fileData = e.target.result as string + this.fileRecords.push(coverFileRecord) } - reader.readAsDataURL(coverFile) + coverReader.readAsDataURL(coverFile) } } } @@ -589,6 +657,9 @@ export default class IscnUploadForm extends Vue { handleDeleteFile(index: number) { const deletedFile = this.fileRecords[index]; + if (this.modifiedEpubMap[deletedFile.ipfsHash]) { + delete this.modifiedEpubMap[deletedFile.ipfsHash] + } this.fileRecords.splice(index, 1); const indexToDelete = this.epubMetadataList.findIndex(item => item.thumbnailIpfsHash === deletedFile.ipfsHashList); @@ -616,7 +687,7 @@ export default class IscnUploadForm extends Vue { async estimateArweaveFee(): Promise { try { const results = await Promise.all( - this.fileRecords.map(async (record) => { + this.modifiedFileRecords.map(async (record) => { const priceResult = await estimateBundlrFilePrice({ fileSize: record.fileBlob?.size || 0, ipfsHash: record.ipfsHash, @@ -753,7 +824,7 @@ export default class IscnUploadForm extends Vue { this.uploadStatus = '' return } - if (!this.fileRecords.some(file => file.fileBlob)) { + if (!this.modifiedFileRecords.some(file => file.fileBlob)) { this.error = 'NO_FILE_TO_UPLOAD' this.isOpenWarningSnackbar = true this.uploadStatus = '' @@ -763,7 +834,7 @@ export default class IscnUploadForm extends Vue { try { this.uploadStatus = 'uploading'; // eslint-disable-next-line no-restricted-syntax - for (const record of this.fileRecords) { + for (const record of this.modifiedFileRecords) { // eslint-disable-next-line no-await-in-loop await this.submitToArweave(record); } @@ -779,17 +850,17 @@ export default class IscnUploadForm extends Vue { } const uploadArweaveIdList = Array.from(this.sentArweaveTransactionInfo.values()).map(entry => entry.arweaveId); - this.fileRecords.forEach((record: any, index:number) => { + this.modifiedFileRecords.forEach((record: any, index:number) => { if (this.sentArweaveTransactionInfo.has(record.ipfsHash)) { const arweaveId = this.sentArweaveTransactionInfo.get( record.ipfsHash, )?.arweaveId if (arweaveId) { - this.fileRecords[index].arweaveId = arweaveId + this.modifiedFileRecords[index].arweaveId = arweaveId } } }) - this.$emit('submit', { fileRecords: this.fileRecords, arweaveIds: uploadArweaveIdList, epubMetadata: this.epubMetadataList[0] }) + this.$emit('submit', { fileRecords: this.modifiedFileRecords, arweaveIds: uploadArweaveIdList, epubMetadata: this.epubMetadataList[0] }) } handleSignDialogClose() { diff --git a/locales/en.json b/locales/en.json index 536eb396..b825e76e 100644 --- a/locales/en.json +++ b/locales/en.json @@ -373,6 +373,7 @@ "UploadForm.button.selectFile": "Select a file", "UploadForm.button.skip": "Skip Upload", "UploadForm.button": "Start Upload", + "UploadForm.label.insertISCNPage": "Add ISCN Page to Epub", "UploadForm.guide.dropFile": "Drop your file here, or", "UploadForm.guide.selectFile": "Select your file and publish to IPFS", "UploadForm.title.registerISCN": "Register ISCN",