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-553] ✨ Support uploading multiple files #400

Merged
merged 13 commits into from
Oct 30, 2023
Merged
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
}
Loading