Skip to content
This repository was archived by the owner on Dec 5, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"file-saver": "^2.0.2",
"ipfs": "^0.52.2",
"js-ipfs-fetch": "^1.4.2",
"markdown-it-vue": "^1.1.6",
"register-service-worker": "^1.7.1",
"serialize-javascript": "^3.1.0",
"setimmediate": "^1.0.5",
Expand All @@ -62,8 +63,8 @@
"sass-loader": "^8.0.2",
"tslint": "^6.1.0",
"typescript": "^3.8.3",
"vue-debounce-decorator": "^1.0.1",
"vue-cli-plugin-i18n": "^1.0.1",
"vue-debounce-decorator": "^1.0.1",
"vue-template-compiler": "2.6.11"
}
}
68 changes: 56 additions & 12 deletions dashboard/src/components/rmrk/Gallery/AvailableActions.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@
</option>
</b-select>
</b-field>
<b-field v-if="showMeta" label="Meta">
<b-input v-model="meta"></b-input>
</b-field>
<component v-if="showMeta" :is="showMeta" @input="updateMeta" />
<b-button
v-if="showSubmit"
type="is-primary"
Expand All @@ -32,24 +30,34 @@ import Connector from '@vue-polkadot/vue-api';
import exec, { execResultValue } from '@/utils/transactionExecutor';
import { notificationTypes, showNotification } from '@/utils/notification';
import { getInstance, RmrkType } from '../service/RmrkService';
import { unpin } from '@/pinata';
import Consolidator from '../service/Consolidator';
import RmrkVersionMixin from '@/utils/mixins/rmrkVersionMixin';

const ownerActions = ['SEND', 'CONSUME', 'LIST'];
const buyActions = ['BUY'];

const needMeta: Record<string, boolean> = {
SEND: true,
LIST: true
const needMeta: Record<string, string> = {
SEND: 'AddressInput',
LIST: 'BalanceInput'
};

@Component({})
const components = {
BalanceInput: () => import('@/components/shared/BalanceInput.vue'),
AddressInput: () => import('@/components/shared/AddressInput.vue')
}

@Component({ components })
export default class AvailableActions extends Mixins(RmrkVersionMixin) {
@Prop() public currentOwnerId!: string;
@Prop() public accountId!: string;
@Prop() public price!: string;
@Prop() public nftId!: string;
@Prop() public imageHash!: string;
@Prop() public metadataHash!: string;
@Prop() public animationHash!: string;
private selectedAction: string = '';
private meta: string = '';
private meta: string | number = '';
private isLoading: boolean = false;

get actions() {
Expand Down Expand Up @@ -90,20 +98,27 @@ export default class AvailableActions extends Mixins(RmrkVersionMixin) {
}`;
}

private async submit() {
protected updateMeta(value: string | number) {
this.meta = value;
}

protected async submit() {
const { api } = Connector.getInstance();
const rmrkService = getInstance();
const rmrk = this.constructRmrk();
try {
showNotification(rmrk)
console.log('submit', rmrk);
const isSend = this.selectedAction === 'SEND';
const cb = isSend ? api.tx.utility.batch : api.tx.system.remark
const arg = isSend ? [api.tx.system.remark(rmrk), api.tx.balances.transfer(this.currentOwnerId, this.price)] : rmrk
const isBuy = this.selectedAction === 'BUY';
const cb = isBuy ? api.tx.utility.batch : api.tx.system.remark
const arg = isBuy ? [api.tx.system.remark(rmrk), api.tx.balances.transfer(this.currentOwnerId, this.price)] : rmrk
const tx = await exec(this.accountId, '', cb, [arg]);
showNotification(execResultValue(tx), notificationTypes.success)
console.warn('TX IN', tx);
const persisted = await rmrkService?.resolve(rmrk, this.accountId);
if (this.selectedAction === 'CONSUME') {
this.unpinNFT()
}
console.log(persisted)
console.log('SAVED', persisted?._id);
showNotification(`[TEXTILE] ${persisted?._id}`, notificationTypes.success)
Expand All @@ -112,5 +127,34 @@ export default class AvailableActions extends Mixins(RmrkVersionMixin) {
console.error(e);
}
}

protected unpinNFT() {
[this.imageHash, this.metadataHash, this.animationHash]
.forEach(async hash => {
if (hash) {
try {
await unpin(hash)
} catch (e) {
console.warn(`[ACTIONS] Cannot Unpin ${hash} because: ${e}`)
}
}
})

}

protected async consolidate(): Promise<boolean> {
const rmrkService = getInstance();
await rmrkService?.checkExpiredOrElseRefresh()

if (!rmrkService) {
console.warn('NO RMRK SERVICE, Live your life on the edge')
return true;
}

const nft = await rmrkService?.getNFT(this.nftId)
return Consolidator.consolidate(this.selectedAction, nft, this.currentOwnerId, this.accountId)
}


}
</script>
11 changes: 9 additions & 2 deletions dashboard/src/components/rmrk/Gallery/GalleryItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@
</b-tag>
<p v-if="!isLoading"
class="subtitle is-size-5">
<!-- <markdown-it-vue class="md-body" :content="nft.description" /> -->
<!-- <markdown-it-vue-light class="md-body" :content="nft.description" /> -->
{{ nft.description }}
</p>
<b-skeleton :count="3" size="is-large" :active="isLoading"></b-skeleton>
Expand All @@ -71,6 +73,9 @@
<script lang="ts" >
import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
import { getInstance } from '@/components/rmrk/service/RmrkService';
// import MarkdownItVue from 'markdown-it-vue';
// import MarkdownItVueLight from 'markdown-it-vue/dist/markdown-it-vue-light.umd.min.js';
import 'markdown-it-vue/dist/markdown-it-vue.css'
import { NFTWithMeta, NFT } from '../service/scheme';
import { fetchNFTMetadata, sanitizeIpfsUrl } from '../utils';
import { emptyObject } from '@/utils/empty';
Expand All @@ -87,6 +92,7 @@ import api from '@/fetch';
import { resolveMedia } from '../utils';
import { MediaType } from '../types';
import { MetaInfo } from 'vue-meta';
// import { VueConstructor } from 'vue';

type NFTType = NFTWithMeta;

Expand Down Expand Up @@ -115,10 +121,11 @@ type NFTType = NFTWithMeta;
components: {
AccountSelect,
AvailableActions,
Money,
Sharing,
Facts,
// MarkdownItVue: MarkdownItVue as VueConstructor<Vue>,
Money,
Name,
Sharing,
Appreciation: () => import('./Appreciation.vue'),
MediaResolver: () => import('../Media/MediaResolver.vue'),
PackSaver: () => import('../Pack/PackSaver.vue')
Expand Down
27 changes: 24 additions & 3 deletions dashboard/src/components/rmrk/service/Consolidator.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { RmrkType } from './RmrkService';
import { RmrkInteraction } from '../types';
import { RmrkEvent, RmrkInteraction } from '../types';
import { Collection, NFT } from './scheme';
import { u8aToHex } from '@polkadot/util';
import { decodeAddress } from '@polkadot/keyring';
Expand All @@ -26,9 +26,30 @@ export default class Consolidator {
}
}

// private static canBuy() {
public static isAvailableForSale(nft: NFT, previousOwner: string) {
return Consolidator.callerEquals(nft.currentOwner, previousOwner)
}

// }
public static consolidate(action: RmrkEvent | string, nft: NFT, previousOwner: string, caller: string): boolean {
switch(action) {
case RmrkEvent.BUY:
return Consolidator.canBuy(nft, previousOwner)
case RmrkEvent.CONSUME:
return Consolidator.canConsume(nft, caller)
default:
console.warn(`[CONSOLIDATOR] NO consolidation for interaction ${action}`)
}

return true
}

private static canBuy(nft: NFT, previousOwner: string) {
return Consolidator.isAvailableForSale(nft, previousOwner);
}

private static canConsume(nft: NFT, caller: string) {
return Consolidator.callerEquals(nft.currentOwner, caller)
}

// private static canMintNft() {

Expand Down
3 changes: 2 additions & 1 deletion dashboard/src/components/rmrk/service/NftUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { RmrkEvent, RMRK, RmrkInteraction } from '../types';
import { SQUARE } from '../utils'
import { generateId } from '../service/Consolidator'
import { Collection, NFT } from './scheme';
import slugify from 'slugify';

class NFTUtils {
public static decode(value: string) {
Expand Down Expand Up @@ -54,7 +55,7 @@ class NFTUtils {
return {
id: generateId(nft.currentOwner, symbol),
_id: '',
symbol,
symbol: slugify(symbol, '_').toUpperCase(),
issuer: nft.currentOwner,
version,
name: nft.name,
Expand Down
2 changes: 1 addition & 1 deletion dashboard/src/components/rmrk/service/RmrkService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export class RmrkService extends TextileService<RmrkType> implements State {
return this._client.context.withKeyInfo(keysToTheKingdom)
}

protected async checkExpiredOrElseRefresh() {
public async checkExpiredOrElseRefresh() {
console.log('checkExpiredOrElseRefresh', this.isAuthExpired)
if (this.isAuthExpired) {
try {
Expand Down
38 changes: 38 additions & 0 deletions dashboard/src/components/shared/AddressInput.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<template>
<div>
<b-field :label="$t(label)">
<b-input type="is-danger" v-model="value" @input="handleInput" :message="err"></b-input>
</b-field>
</div>
</template>

<script lang="ts">
import { checkAddress } from '@polkadot/util-crypto';
import { Debounce } from 'vue-debounce-decorator';
import { Component, Emit, Prop, Vue } from 'vue-property-decorator';

@Component({})
export default class AddressInput extends Vue {

private value: string = '';
private err: string | null = '';
@Prop({ default: 'insert address' }) public label!: string;

@Debounce(500)
@Emit('input')
protected handleInput(value: string) {
const [valid, err] = checkAddress(value, this.ss58Format);
this.err = err;

if (valid) {
return value
}

return ''
}

get ss58Format(): number {
return this.$store.getters.getChainProperties?.ss58Format
}
}
</script>
70 changes: 62 additions & 8 deletions dashboard/src/components/shared/BalanceInput.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
<template>
<div>
<Balance
:argument="{ name: 'balance', type: 'balance' }"
@selected="handleValue"
/>
<div class="arguments-wrapper">
<b-field :label="$t('balance')" class="balance">
<b-input v-model="value" @input="handleInput" type="number" step="0.001" min="0"/>
<p class="control balance">
<b-select v-model="selectedUnit" @input="handleInput">
<option v-for="u in units" v-bind:key="u.value" v-bind:value="u.value">
{{ u.name }}
</option>
</b-select>
</p>
</b-field>
</div>
</template>

<script lang="ts" >
import { Component, Prop, Vue, Watch, Emit } from 'vue-property-decorator';
import Balance from '@/params/components/Balance.vue';
import { units as defaultUnits } from '@/params/constants';
import { Unit } from '@/params/types';
import shouldUpdate from '@/utils/shouldUpdate';
import { Debounce } from 'vue-debounce-decorator';

const components = { Balance }

Expand All @@ -19,11 +29,55 @@ type BalanceType = {

@Component({ components })
export default class BalanceInput extends Vue {
@Prop() public value!: number;
private value: number = 0;
protected units: Unit[] = defaultUnits;
private selectedUnit: number = 1;


get chainProperties() {
return this.$store.getters.getChainProperties;
}

get decimals(): number {
return this.chainProperties.tokenDecimals
}

get unit(): string {
return this.chainProperties.tokenSymbol
}

get calculatedBalance() {
return this.value * (10**this.decimals) * this.selectedUnit
}

protected mapper(unit: Unit) {
if (unit.name === '-') {
return { ...unit, name: this.unit }
}
return unit
}

public mounted() {
this.units = defaultUnits.map(this.mapper);
}

// @Watch('$store.getters.getChainProperties.tokenSymbol')
// protected updateUnit(val: string, oldVal: string) {
// console.log('@Watch(unit)', val, oldVal)
// if (shouldUpdate(val, oldVal)) {
// this.units = defaultUnits.map(u => {
// if (u.name === '-') {
// return { ...u, name: val }
// }
// return u
// })
// }
// }

@Debounce(200)
@Emit('input')
public handleValue(value: BalanceType) {
return value.balance;
public handleInput() {
return this.calculatedBalance;
}
}
</script>
Loading