Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TAS-1122] ✨ Add insert iscn page into epub on edit #443

Merged
merged 8 commits into from
Feb 28, 2024
Merged
156 changes: 114 additions & 42 deletions components/IscnUploadForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
<tbody class="w-full">
<tr
v-for="(
{ isFileImage, fileData, fileName, fileSize, exifInfo},
{ isFileImage, fileData, fileName, fileSize, exifInfo },
index
) of fileRecords"
:key="fileName"
Expand Down Expand Up @@ -104,6 +104,9 @@
</table>
</div>
</div>
<FormField v-if="showAddISCNPageOption">
<CheckBox v-model="isAddISCNPageToEpub">{{ $t('UploadForm.label.insertISCNPage') }}</CheckBox>
</FormField>
<!-- upload field__Submit -->
<div class="flex gap-[8px] justify-end text-medium-gray mt-[24px]">
<NuxtLink
Expand Down Expand Up @@ -242,6 +245,7 @@
} from '~/utils/misc'
import { DEFAULT_TRANSFER_FEE, sendLIKE } from '~/utils/cosmos/sign';
import { getAccountBalance } from '~/utils/cosmos'
import { injectISCNQRCodePage } from '~/utils/epub/iscn'

const walletModule = namespace('wallet')
type UploadStatus = '' | 'loading' | 'signing' | 'uploading';
Expand All @@ -255,6 +259,7 @@
export default class IscnUploadForm extends Vue {
@Prop(Number) readonly step: number | undefined
@Prop({ default: MODE.REGISTER }) readonly mode: string | undefined
@Prop(String) readonly iscnId: string | undefined

@walletModule.Getter('getSigner') signer!: OfflineSigner | null
@walletModule.Action('initIfNecessary') initIfNecessary!: () => Promise<any>
Expand All @@ -281,6 +286,7 @@
isOpenWarningSnackbar = false
isOpenKeplr = true
isSizeExceeded = false
isAddISCNPageToEpub = false

uploadSizeLimit: number = UPLOAD_FILESIZE_MAX
uploadStatus: UploadStatus = '';
Expand All @@ -300,6 +306,7 @@
balance = new BigNumber(0)

epubMetadataList: any[] = []
modifiedEpubMap: any = {}

get formClasses() {
return [
Expand Down Expand Up @@ -369,12 +376,29 @@
}
}

get showAddISCNPageOption() {
return this.mode === MODE.EDIT && this.fileRecords.some(file => 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
}

@Watch('fileRecords')
@Watch('modifiedFileRecords')
async estimateArFee(fileRecords: any) {
if (fileRecords.length) {
this.uploadStatus = 'loading'
Expand All @@ -385,6 +409,32 @@
}
}

// 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
Expand Down Expand Up @@ -412,21 +462,14 @@
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,
Expand All @@ -453,7 +496,7 @@
}
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 {
Expand All @@ -464,10 +507,41 @@
}
}

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
Expand Down Expand Up @@ -510,45 +584,40 @@
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,
fileBlob: coverFile,
ipfsHash,
ipfsHash: ipfsThumbnailHash,
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)
}
}
}
this.epubMetadataList.push(epubMetadata)
} catch (err) {
console.error(err)

Check warning on line 620 in components/IscnUploadForm.vue

View workflow job for this annotation

GitHub Actions / ci (ubuntu-latest, 18)

Unexpected console statement
}
}

Expand Down Expand Up @@ -589,6 +658,9 @@

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);
Expand Down Expand Up @@ -616,7 +688,7 @@
async estimateArweaveFee(): Promise<void> {
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,
Expand Down Expand Up @@ -755,7 +827,7 @@
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 = ''
Expand All @@ -765,7 +837,7 @@
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);
}
Expand All @@ -781,17 +853,17 @@
}

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() {
Expand Down
1 change: 1 addition & 0 deletions locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,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",
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"form-data": "^4.0.0",
"image-type": "^4.1.0",
"ipfs-only-hash": "^4.0.0",
"jszip": "^3.10.1",
"lodash.chunk": "^4.2.0",
"lodash.debounce": "^4.0.8",
"mime-types": "^2.1.34",
Expand All @@ -58,6 +59,7 @@
"puppeteer-extra": "^3.3.4",
"puppeteer-extra-plugin-anonymize-ua": "^2.4.4",
"puppeteer-extra-plugin-stealth": "^2.11.1",
"qrcode": "^1.5.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"uuid": "^8.3.2",
Expand All @@ -80,6 +82,7 @@
"@types/mime-types": "^2.1.1",
"@types/multer": "^1.4.7",
"@types/p5": "^1.3.0",
"@types/qrcode": "^1.5.5",
"@types/uuid": "^8.3.1",
"@vue/test-utils": "^1.2.1",
"@vue/vue2-jest": "^27.0.0",
Expand Down
5 changes: 3 additions & 2 deletions pages/edit/_iscnId.vue
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
>
<IscnUploadForm
mode="edit"
:iscn-id="iscnId"
@submit="onSubmitUpload"
@arweaveUploaded="onArweaveIdUpload"
/>
Expand Down Expand Up @@ -281,10 +282,10 @@ export default class EditIscnPage extends Vue {
description: this.description,
keywords: this.contentMetadata.keywords,
url: this.contentMetadata.url,
contentFingerprints: [
contentFingerprints: Array.from(new Set([
...this.contentFingerprints,
...this.customContentFingerprints,
],
])),
stakeholders: this.iscnRecord?.stakeholders,
type: this.contentMetadata['@type'],
usageInfo: this.contentMetadata.usageInfo,
Expand Down
Loading
Loading