Skip to content

Commit

Permalink
✨ Support uploading multiple files (#400)
Browse files Browse the repository at this point in the history
* ✨ Support uploading multiple files in Step1

* ✨ Support uploading multiple Arweave hash to ISCN in Step 2

* 🚸 Store arweave payment tx hashes in Map

* 💄 Add Info button to display Exif info

* 🎨 Support upload mutiple files to numbers

* 🩹 Change exinfo default type to object

* ✏️ Improve formatting & naming

* ✏️ Reset data array

* 🐛 Prevent duplicate Arweave uploads

* 🎨 Increase ISCN gas fee

* 🎨 Update ipfsHash and arweaveId types

* 🎨 Update sentArweaveTransactionHashes types
  • Loading branch information
AuroraHuang22 committed Oct 30, 2023
1 parent c39666b commit b05c1c6
Show file tree
Hide file tree
Showing 9 changed files with 590 additions and 405 deletions.
443 changes: 292 additions & 151 deletions components/IscnRegisterForm.vue

Large diffs are not rendered by default.

379 changes: 200 additions & 179 deletions components/IscnUploadForm.vue

Large diffs are not rendered by default.

9 changes: 6 additions & 3 deletions components/Previewer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@
:class="[
'flex',
'justify-center',
'w-[138px]',
{ 'w-[138px]': size === 'large' },
{ 'w-[48px]': size === 'small' },
'mr-[16px]',
'overflow-hidden'
]"
>
<img
v-if="isImage"
:class="[
'w-full',
'max-h-[150px]',
'object-cover',
'h-auto',
'object-contain',
'rounded-[8px]',
]"
:src="fileData"
Expand All @@ -28,5 +30,6 @@ import { Vue, Component, Prop } from 'vue-property-decorator'
export default class Previewer extends Vue {
@Prop({ default: false }) readonly isImage!: boolean
@Prop(String) readonly fileData: string | undefined
@Prop({ default: 'large' }) readonly size: string | undefined
}
</script>
1 change: 1 addition & 0 deletions constant/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export const IPFS_VIEW_GATEWAY_URL = 'https://ipfs.io/ipfs';
export const ISCN_MIN_BALANCE = 0.01;

export const ISCN_GAS_FEE = 200000;
export const ISCN_GAS_MULTIPLIER = 1.5;

export const ISCN_REGISTRY_NAME = 'likecoin-chain';

Expand Down
4 changes: 2 additions & 2 deletions locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@
"IscnRegisterForm.label.fileName": "File name",
"IscnRegisterForm.label.fileType": "File type",
"IscnRegisterForm.label.numbersProtocol": "Numbers Protocol",
"IscnRegisterForm.label.numbersProtocol.details": "Register your asset in {link}",
"IscnRegisterForm.label.numbersProtocol.details": "Register your image asset in {link}",
"IscnRegisterForm.label.numbersProtocol.details.link": "Numbers Protocol",
"IscnRegisterForm.label.registrant": "Registrant",
"IscnRegisterForm.label.tags": "Tags",
Expand Down Expand Up @@ -360,7 +360,7 @@
"UploadForm.title.registerISCN": "Register ISCN",
"UploadForm.url.placeholder": "Use existing IPFS content",
"UploadForm.view.file.button": "View File Info",
"UploadForm.warning": "Oops, file is too large. The file size should not exceed 100MB",
"UploadForm.warning": "Oops, file is too large. The file size should not exceed {size}MB",
"WorksPage.empty.label": "No works",
"WorksPage.label.search.result": "Search result for :",
"WorksPage.label.portfolio": "View my Writing NFT Portfolio",
Expand Down
89 changes: 38 additions & 51 deletions pages/new/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,16 @@
/>
<IscnRegisterForm
v-else-if="state === 'iscn'"
:ipfs-hash="ipfsHash"
:file-records="formattedFileRecords"

:ipfs-hash="urlIpfsHash"
:arweave-id="arweaveId"
:file-data="fileData"
:file-type="fileType"
:file-size="fileSize"
:file-s-h-a256="fileSHA256"
:file-blob="fileBlob"
:is-image="isImage"

:is-upload-only="isUploadOnly"
:exif-info="exifInfo"
:step="step"
@arweaveUploaded="onArweaveIdUpdate"
@txBroadcasted="onISCNTxInfo"
@fileUploaded="onFileOnlyUpload"
@fileUploaded="fileUploaded"
@handleSubmit="isSubmit = true"
@handleQuit="isSubmit = false"
/>
Expand Down Expand Up @@ -121,7 +117,7 @@ export default class NewIndexPage extends Vue {
) => ISCNRecordWithID[] | PromiseLike<ISCNRecordWithID[]>
state = 'init'
ipfsHash = this.$route.query.ipfs_hash || ''
urlIpfsHash = this.$route.query.ipfs_hash || ''
arweaveId = this.$route.query.arweave_id || ''
isUploadOnly = this.$route.query.upload_only === '1'
fileSHA256 = ''
Expand All @@ -131,12 +127,15 @@ export default class NewIndexPage extends Vue {
iscnId = ''
iscnTxHash = ''
iscnTimestamp = ''
isImage = false
isFileImage = false
fileBlob: Blob | null = null
exifInfo: any | null = null
isSubmit = false
record: ISCNRecordWithID | null = null
uploadFileRecords: any[] = []
urlFileRecords: any[] = []
get shouldSkipToMintNFT(): boolean {
return this.$route.query.mint === '1'
}
Expand All @@ -157,55 +156,43 @@ export default class NewIndexPage extends Vue {
}
}
get formattedFileRecords() {
if (this.uploadFileRecords.length) {
return this.uploadFileRecords
}
if (this.urlFileRecords.length) {
return this.urlFileRecords
}
return[]
}
async mounted() {
if ((this.ipfsHash || this.arweaveId) && this.shouldSkipToMintNFT) {
if ((this.urlIpfsHash || this.arweaveId) && this.shouldSkipToMintNFT) {
this.state = 'iscn';
let url;
if (this.arweaveId) url = `https://arweave.net/${this.arweaveId}`;
else if (this.ipfsHash) url = `https://ipfs.io/ipfs/${this.ipfsHash}`;
else if (this.urlIpfsHash) url = `https://ipfs.io/ipfs/${this.urlIpfsHash}`;
if (url) {
const { data, headers } = await this.$axios.get(url, { responseType: 'blob' })
this.fileBlob = data as Blob
this.isImage = headers['content-type'].startsWith('image')
this.fileType = headers['content-type']
this.fileSize = headers['content-length']
this.fileData = `data:${this.fileType};base64,${Buffer.from(await data.arrayBuffer(), 'binary').toString('base64')}`
this.urlFileRecords = [{
fileBlob: data as Blob,
ipfsHash: this.urlIpfsHash,
arweaveId: this.arweaveId,
isFileImage: headers['content-type'].startsWith('image'),
fileType: headers['content-type'],
fileSize: headers['content-length'],
fileData: `data:${this.fileType};base64,${Buffer.from(await data.arrayBuffer(), 'binary').toString('base64')}`,
}]
}
}
}
onSubmitUpload({
ipfsHash,
arweaveId,
fileData,
fileSHA256,
isImage,
fileBlob,
exifInfo,
fileType,
fileSize,
}: {
ipfsHash: string
arweaveId: string
fileData: string
fileSHA256: string
isImage: boolean
fileBlob: Blob | null
exifInfo: any
fileType: string
fileSize: string
}) {
this.ipfsHash = ipfsHash
this.arweaveId = arweaveId
this.fileData = fileData
this.fileSHA256 = fileSHA256
this.isImage = isImage
this.fileBlob = fileBlob
this.exifInfo = exifInfo
this.fileType = fileType
this.fileSize = fileSize
onSubmitUpload(fileRecords: any[] | []) {
if (fileRecords && fileRecords.length) {
this.uploadFileRecords = [...fileRecords]
}
this.state = 'iscn'
logTrackerEvent(this, 'ISCNCreate', 'ISCNConfirmFile', this.fileType, 1);
logTrackerEvent(this, 'ISCNCreate', 'ISCNConfirmFile', '', 1);
}
onArweaveIdUpdate({ arweaveId }: { arweaveId: string }) {
Expand Down Expand Up @@ -245,7 +232,7 @@ export default class NewIndexPage extends Vue {
handleCreateAnotherButtonClick() {
this.state = 'init'
this.ipfsHash = ''
this.urlIpfsHash = ''
this.arweaveId = ''
this.fileSHA256 = ''
this.fileData = ''
Expand All @@ -254,7 +241,7 @@ export default class NewIndexPage extends Vue {
this.iscnId = ''
this.iscnTxHash = ''
this.iscnTimestamp = ''
this.isImage = false
this.isFileImage = false
this.fileBlob = null
this.exifInfo = null
this.isSubmit = false
Expand Down
4 changes: 2 additions & 2 deletions utils/cosmos/iscn/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ export async function signISCNTx(
tx: ISCNSignPayload,
signer: OfflineSigner,
address: string,
{ iscnId, memo }: { iscnId?: string, memo?: string } = {},
{ iscnId, memo, gas}: { iscnId?: string, memo?: string, gas?: string } = {},
) {
const client = await getQueryClient();
const res = await sign(tx, signer, address, { memo, iscnId });
const res = await sign(tx, signer, address, { memo, iscnId, gas });
const [newIscnId] = await client.queryISCNIdsByTx(res.transactionHash);
return {
iscnId: newIscnId,
Expand Down
8 changes: 4 additions & 4 deletions utils/cosmos/iscn/iscn.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ export interface ISCNRegisterPayload {
url: string;
exifInfo: any;
license: string;
ipfsHash: string;
arweaveId: string;
numbersProtocolAssetId: string;
fileSHA256: string;
ipfsHash: string | string[];
arweaveId: string | string[];
numbersProtocolAssetId: string | string[];
fileSHA256: string | string[];
type: string;
publisher?: string,
author: string;
Expand Down
58 changes: 45 additions & 13 deletions utils/cosmos/iscn/sign.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ISCNSignPayload, ISCNSigningClient } from '@likecoin/iscn-js';
import network from '@/constant/network';
import { DeliverTxResponse } from '@cosmjs/stargate';
import { ISCNRegisterPayload } from './iscn.type';
import { WALLET_TYPE_REPLACER } from '~/constant'
import { WALLET_TYPE_REPLACER, ISCN_GAS_FEE, DEFAULT_GAS_PRICE } from '~/constant'
import { getPublisherISCNPayload } from '.';
import { ISCN_PUBLISHERS } from '~/constant/iscn';

Expand Down Expand Up @@ -72,10 +72,19 @@ export function formatISCNTxPayload(payload: ISCNRegisterPayload): ISCNSignPaylo
rewardProportion = Math.max(0, rewardProportion);
}
}
if (fileSHA256) contentFingerprints.push(`hash://sha256/${fileSHA256}`)
if (ipfsHash) contentFingerprints.push(`ipfs://${ipfsHash}`)
if (arweaveId) contentFingerprints.push(`ar://${arweaveId}`);
if (numbersProtocolAssetId) contentFingerprints.push(`num://${numbersProtocolAssetId}`);

const pushContentFingerprint = (value: any, prefix:string) => {
if (Array.isArray(value)) {
value.forEach(item => contentFingerprints.push(`${prefix}${item}`));
} else if (typeof value === 'string' && value.length) {
contentFingerprints.push(`${prefix}${value}`);
}
};
pushContentFingerprint(fileSHA256, 'hash://sha256/');
pushContentFingerprint(ipfsHash, 'ipfs://');
pushContentFingerprint(arweaveId, 'ar://');
pushContentFingerprint(numbersProtocolAssetId, 'num://');

if (authorNames.length) {
for (let i = 0; i < authorNames.length; i += 1) {
const authorName: string = authorNames[i]
Expand Down Expand Up @@ -146,13 +155,36 @@ export async function signISCN(
tx: ISCNSignPayload,
signer: OfflineSigner,
address: string,
{ iscnId, memo }: { iscnId?: string, memo?: string } = {},
{
iscnId,
memo,
gas = ISCN_GAS_FEE.toString(),
}: { iscnId?: string, memo?: string, gas?: string } = {},
) {
const isUpdate = !!iscnId;
const signingClient = await getSigningClient();
await signingClient.connectWithSigner(network.rpcURL, signer);
const signingPromise = isUpdate ? signingClient.updateISCNRecord(address, iscnId as string, tx, { memo: memo || 'app.like.co' })
: signingClient.createISCNRecord(address, tx, { memo: memo || 'app.like.co' });
const res = await signingPromise;
return res as DeliverTxResponse;
const isUpdate = !!iscnId
const signingClient = await getSigningClient()
await signingClient.connectWithSigner(network.rpcURL, signer)
const signingPromise = isUpdate
? signingClient.updateISCNRecord(address, iscnId as string, tx, {
memo: memo || 'app.like.co',
fee: {
gas,
amount: [{
denom: DEFAULT_GAS_PRICE[0].denom,
amount: DEFAULT_GAS_PRICE[0].amount.toString(),
}],
},
})
: signingClient.createISCNRecord(address, tx, {
memo: memo || 'app.like.co',
fee: {
gas,
amount: [{
denom: DEFAULT_GAS_PRICE[0].denom,
amount: DEFAULT_GAS_PRICE[0].amount.toString(),
}],
},
})
const res = await signingPromise
return res as DeliverTxResponse
}

0 comments on commit b05c1c6

Please sign in to comment.