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

feat: redesign update price and transfer item on gallery #4652

Merged
merged 16 commits into from
Jan 11, 2023
Merged
12 changes: 10 additions & 2 deletions components/gallery/GalleryItemAction/GalleryItemAction.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,18 @@
class="mt-5" />

<!-- change price as an owner -->
<GalleryItemPriceRelist v-if="isOwner" />
<GalleryItemPriceRelist
v-if="isOwner && nft?.id && nft?.price && nft?.collection.id"
:collection-id="nft.collection.id"
:nft-id="nft.id"
:nft-price="nft.price"
class="mt-5" />

<!-- transfer item as an owner -->
<GalleryItemPriceTransfer v-if="isOwner" class="mt-5" />
<GalleryItemPriceTransfer
v-if="isOwner && nft?.id"
:nft-id="nft.id"
class="mt-5" />
</div>
</template>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,62 +1,71 @@
<template>
<GalleryItemPriceSection v-if="nft?.price" title="Price" :price="nft?.price">
<GalleryItemActionSlides
v-if="Number(nft.price)"
ref="actionRef"
:active="active">
<template #action>
<NeoButton
label="Change Price"
size="large"
fixed-width
no-shadow
@click.native="toggleActive" />
</template>

<template #content>
<div>
<input type="number" placeholder="Your New Price" />
</div>
</template>
</GalleryItemActionSlides>
<GalleryItemActionSlides v-else :active="active">
<template #action>
<NeoButton
label="List"
size="large"
fixed-width
no-shadow
variant="k-accent"
@click.native="toggleActive" />
</template>

<template #content>
<div>
<input type="number" placeholder="Your Listing Price" />
</div>
</template>
</GalleryItemActionSlides>
</GalleryItemPriceSection>
<div>
<Loader v-model="isLoading" :status="status" />
<GalleryItemPriceSection title="Price" :price="nftPrice">
<GalleryItemActionSlides ref="actionRef" :active="active">
<template #action>
<NeoButton
:label="isListed ? 'Change Price' : 'List'"
size="large"
fixed-width
no-shadow
:variant="isListed ? 'k-accent' : 'primary'"
@click.native="updatePrice" />
</template>

<template #content>
<div>
<input
v-model="price"
type="number"
:placeholder="
isListed ? 'Your New Price' : 'Your Listing Price'
" />
</div>
</template>
</GalleryItemActionSlides>
</GalleryItemPriceSection>
</div>
</template>

<script setup lang="ts">
import { NeoButton } from '@kodadot1/brick'
import { onClickOutside } from '@vueuse/core'
import { NeoButton } from '@kodadot1/brick'
import { calculateBalance } from '@/utils/format/balance'

import { useGalleryItem } from '../../useGalleryItem'
import GalleryItemPriceSection from '../GalleryItemActionSection.vue'
import GalleryItemActionSlides from '../GalleryItemActionSlides.vue'
import { useGalleryItemAction } from './useGalleryItemAction'
import { Interaction } from '@kodadot1/minimark'

const { nft } = useGalleryItem()
const { transaction, status, isLoading } = useGalleryItemAction()

const active = ref(false)
const props = defineProps<{
collectionId: string
nftId: string
nftPrice: string
}>()

function toggleActive() {
active.value = !active.value
}
const active = ref(false)
const price = ref()
const isListed = computed(() => Boolean(props.nftPrice))

const actionRef = ref(null)
onClickOutside(actionRef, () => (active.value = false))

function updatePrice() {
if (active.value === false) {
active.value = true
} else {
transaction({
interaction: Interaction.LIST,
price: String(calculateBalance(price.value)),
nftId: props.nftId,
successMessage: 'Price updated',
errorMessage: 'Price update failed',
})
}
}
</script>

<style lang="scss" scoped></style>
Original file line number Diff line number Diff line change
@@ -1,35 +1,59 @@
<template>
<div class="is-flex is-justify-content-space-between">
<div>&nbsp;</div>
<GalleryItemActionSlides ref="actionRef" :active="active">
<template #action>
<NeoButton
label="Transfer"
size="large"
fixed-width
no-shadow
@click.native="toggleActive" />
</template>

<template #content>
<div>
<input type="number" placeholder="Transfer To:" />
</div>
</template>
</GalleryItemActionSlides>
<div>
<Loader v-model="isLoading" :status="status" />
<div class="is-flex is-justify-content-space-between">
<div>&nbsp;</div>
<GalleryItemActionSlides ref="actionRef" :active="active">
<template #action>
<NeoButton
label="Transfer"
size="large"
fixed-width
no-shadow
@click.native="sendItem" />
</template>

<template #content>
<div>
<input v-model="address" type="text" placeholder="Transfer To:" />
</div>
</template>
</GalleryItemActionSlides>
</div>
</div>
</template>

<script setup lang="ts">
import { NeoButton } from '@kodadot1/brick'
import { onClickOutside } from '@vueuse/core'
import { NeoButton } from '@kodadot1/brick'

import GalleryItemActionSlides from '../GalleryItemActionSlides.vue'
import { useGalleryItemAction } from './useGalleryItemAction'
import { Interaction } from '@kodadot1/minimark'

const { transaction, status, isLoading } = useGalleryItemAction()
const { $route } = useNuxtApp()

const props = defineProps<{
nftId: string
}>()

const active = ref(false)
const address = ref()

function toggleActive() {
active.value = !active.value
function sendItem() {
if (active.value === false) {
active.value = true
} else {
transaction({
interaction: Interaction.SEND,
address: address.value,
tokenId: $route.params.id,
nftId: props.nftId,
successMessage: 'Item sent',
errorMessage: 'Failed to send item',
})
}
}

const actionRef = ref(null)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import { createInteraction } from '@kodadot1/minimark'
import { Interaction } from '@kodadot1/minimark'
import { checkAddress, isAddress } from '@polkadot/util-crypto'

import { dangerMessage, infoMessage } from '@/utils/notification'
import { bsxParamResolver, getApiCall } from '@/utils/gallery/abstractCalls'
import { tokenIdToRoute } from '@/components/unique/utils'
import { ss58Of } from '@/utils/config/chain.config'
import correctFormat from '@/utils/ss58Format'

type ActionList = {
interaction: Interaction.LIST
price: string
nftId: string
successMessage?: string
errorMessage?: string
}

type ActionSend = {
interaction: Interaction.SEND
tokenId: string
address: string
nftId: string
successMessage?: string
errorMessage?: string
}

type Actions = ActionList | ActionSend

function constructTransactionList(
urlPrefix,
api,
item: ActionList,
executeTransaction
) {
const meta = item.price

if (!meta) {
return dangerMessage('Price is not valid')
}

if (urlPrefix === 'rmrk') {
return executeTransaction({
cb: api.tx.system.remark,
arg: [createInteraction(Interaction.LIST, '1.0.0', item.nftId, meta)],
})
}

if (urlPrefix === 'snek' || urlPrefix === 'bsx') {
return executeTransaction({
cb: getApiCall(api, urlPrefix, Interaction.LIST),
arg: bsxParamResolver(item.nftId, Interaction.LIST, meta),
})
}

return dangerMessage('Unknown prefix')
}

function constructTransactionSend(
urlPrefix,
api,
item: ActionSend,
executeTransaction
) {
const { id, item: token } = tokenIdToRoute(item.tokenId)
const [, err] = checkAddress(
item.address,
correctFormat(ss58Of(urlPrefix.value))
)

if (!isAddress(item.address)) {
return dangerMessage('Invalid address')
}

if (err) {
return dangerMessage(err)
}

if (urlPrefix === 'rmrk') {
return executeTransaction({
cb: api.tx.system.remark,
arg: [
createInteraction(Interaction.SEND, '1.0.0', item.nftId, item.address),
],
})
}

if (urlPrefix === 'snek' || urlPrefix === 'bsx') {
return executeTransaction({
cb: api.tx.utility.batchAll,
arg: [
[
api.tx.marketplace.setPrice(id, token, 0),
api.tx.nft.transfer(id, token, item.address),
],
],
})
}
}

const useExecuteTransaction = () => {
const { accountId } = useAuth()
const { howAboutToExecute, isLoading, status, initTransactionLoader } =
useMetaTransaction()

const executeTransaction = ({
cb,
arg,
successMessage = 'Success!',
errorMessage = 'Failed!',
}) => {
initTransactionLoader()
howAboutToExecute(
accountId.value,
cb,
arg,
() => {
infoMessage(successMessage)
},
() => {
dangerMessage(errorMessage)
}
)
}

return {
isLoading,
status,
executeTransaction,
}
}

export const useGalleryItemAction = () => {
const { urlPrefix } = usePrefix()
const { apiInstance } = useApi()
const { isLoading, status, executeTransaction } = useExecuteTransaction()

const transaction = async (item: Actions) => {
const api = await apiInstance.value

if (item.interaction === Interaction.LIST) {
constructTransactionList(urlPrefix.value, api, item, executeTransaction)
}

if (item.interaction === Interaction.SEND) {
constructTransactionSend(urlPrefix.value, api, item, executeTransaction)
}
}

return {
isLoading,
status,
transaction,
}
}
1 change: 1 addition & 0 deletions composables/useMetaTransaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ function useMetaTransaction() {
} = useTransactionStatus()
const { apiInstance } = useAPI()
const tx = ref<ExecResult>()

const howAboutToExecute = async (
account: string,
cb: (...params: any[]) => Extrinsic,
Expand Down
2 changes: 1 addition & 1 deletion utils/gallery/abstractCalls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const bsxParamResolver = (
id: string,
selectedAction: string,
meta: string | number,
currentOwner: string,
currentOwner?: string,
expiration?: number
): any[] => {
const [collectionId, tokenId] = id.split('-')
Expand Down