-
-
Notifications
You must be signed in to change notification settings - Fork 366
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #8842 from kodadot/issue-8835
feat: Implement paid drop
- Loading branch information
Showing
10 changed files
with
329 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,307 @@ | ||
<template> | ||
<Loader v-model="isLoading" :status="status" /> | ||
|
||
<CollectionDropGenerativeLayout | ||
:collection-id="collectionId" | ||
:description="description" | ||
:drop="drop" | ||
:user-minted-nft-id="userMintedNftId" | ||
:is-wallet-connecting="isWalletConnecting" | ||
:is-image-fetching="isImageFetching" | ||
:is-loading="isLoading" | ||
:minimum-funds="minimumFundsProps" | ||
:max-count="maxCount" | ||
:minted-count="mintedCount" | ||
:mint-count-available="mintCountAvailable" | ||
:mint-button="mintButtonProps" | ||
:handle-select-image="handleSelectImage" | ||
:handle-submit-mint="handleSubmitMint" /> | ||
|
||
<CollectionDropAddFundsModal | ||
v-model="isAddFundModalActive" | ||
:minimum-funds="minimumFunds" | ||
:formatted-minimum-funds="formattedMinimumFunds" | ||
:token="token" | ||
:chain="chainName" | ||
@close="closeAddFundModal" | ||
@confirm="handleDropAddModalConfirm" /> | ||
</template> | ||
|
||
<script setup lang="ts"> | ||
import { createUnlockableMetadata } from '../unlockable/utils' | ||
import { DropItem } from '@/params/types' | ||
import { claimDropItem } from '@/services/waifu' | ||
import { useDropMinimumFunds, useDropStatus } from '@/components/drops/useDrops' | ||
import { fetchNft } from '@/components/items/ItemsGrid/useNftActions' | ||
import unlockableCollectionById from '@/queries/subsquid/general/unlockableCollectionById.graphql' | ||
import Loader from '@/components/shared/Loader.vue' | ||
import useGenerativeDropMint, { | ||
type UnlockableCollectionById, | ||
} from '@/composables/drop/useGenerativeDropMint' | ||
import useGenerativeDropDetails from '@/composables/drop/useGenerativeDropDetails' | ||
import formatBalance from '@/utils/format/balance' | ||
const props = withDefaults( | ||
defineProps<{ | ||
drop: DropItem | ||
}>(), | ||
{ | ||
drop: () => ({}) as DropItem, | ||
}, | ||
) | ||
const { fetchMultipleBalance } = useMultipleBalance() | ||
const { chainSymbol, decimals } = useChain() | ||
const { hasMinimumFunds, formattedMinimumFunds, minimumFunds } = | ||
useDropMinimumFunds(props.drop) | ||
const minimumFundsDescription = computed(() => | ||
$i18n.t('mint.unlockable.minimumFundsDescription', [ | ||
formattedMinimumFunds.value, | ||
chainName.value, | ||
]), | ||
) | ||
const minimumFundsProps = computed(() => ({ | ||
amount: minimumFunds.value, | ||
description: minimumFundsDescription.value, | ||
})) | ||
const isWalletConnecting = ref(false) | ||
const { currentAccountMintedToken, mintedDropCount, fetchDropStatus } = | ||
useDropStatus(props.drop.alias) | ||
const instance = getCurrentInstance() | ||
const mintNftSN = ref('0') | ||
const { doAfterLogin } = useDoAfterlogin(instance) | ||
const { $i18n, $consola } = useNuxtApp() | ||
const { urlPrefix } = usePrefix() | ||
const { toast } = useToast() | ||
const { accountId, isLogIn } = useAuth() | ||
const { client } = usePrefix() | ||
const isLoading = ref(false) | ||
const isImageFetching = ref(false) | ||
const isAddFundModalActive = ref(false) | ||
const { | ||
defaultName, | ||
defaultImage, | ||
defaultMax, | ||
collectionId, | ||
chainName, | ||
disabledByBackend, | ||
token, | ||
price, | ||
} = useGenerativeDropDetails(props.drop) | ||
const { | ||
howAboutToExecute, | ||
isLoading: isTransactionLoading, | ||
initTransactionLoader, | ||
status, | ||
} = useMetaTransaction() | ||
const handleSelectImage = (image: string) => { | ||
selectedImage.value = image | ||
} | ||
const { data: collectionData } = await useAsyncData( | ||
'unlockableCollectionData', | ||
async () => | ||
await useAsyncQuery<UnlockableCollectionById>({ | ||
clientId: client.value, | ||
query: unlockableCollectionById, | ||
variables: { | ||
id: collectionId.value, | ||
search: { currentOwner_eq: accountId.value }, | ||
}, | ||
}).then((res) => res.data.value), | ||
{ | ||
watch: [accountId], | ||
}, | ||
) | ||
const { | ||
maxCount, | ||
mintedNft, | ||
mintedNftWithMetadata, | ||
userMintedNftId, | ||
mintedCount, | ||
mintCountAvailable, | ||
selectedImage, | ||
description, | ||
collectionName, | ||
tryCapture, | ||
subscribeToMintedNft, | ||
} = useGenerativeDropMint({ | ||
collectionData, | ||
defaultMax, | ||
currentAccountMintedToken, | ||
collectionId, | ||
mintedDropCount, | ||
defaultImage, | ||
}) | ||
const mintedAmountForCurrentUser = computed( | ||
() => collectionData.value?.nftEntitiesConnection?.totalCount || 0, // todo: fetch from backend | ||
) | ||
const maxMintLimitForCurrentUser = computed(() => maxCount.value) | ||
const mintButtonLabel = computed(() => { | ||
return isWalletConnecting.value | ||
? $i18n.t('shoppingCart.wallet') | ||
: $i18n.t('mint.unlockable.claimPaidNft', [ | ||
`${formatBalance(price.value || '', decimals.value, '')} ${ | ||
chainSymbol.value | ||
}`, | ||
]) | ||
}) | ||
const mintButtonDisabled = computed( | ||
() => | ||
isLogIn.value && | ||
Boolean( | ||
!mintCountAvailable.value || | ||
!selectedImage.value || | ||
disabledByBackend.value || | ||
maxMintLimitForCurrentUser.value <= mintedAmountForCurrentUser.value, | ||
), | ||
) | ||
const mintButtonProps = computed(() => ({ | ||
disabled: mintButtonDisabled.value, | ||
label: mintButtonLabel.value, | ||
})) | ||
const mintNft = async () => { | ||
try { | ||
isLoading.value = true | ||
const { apiInstance } = useApi() | ||
const api = await apiInstance.value | ||
const collectionRes = ( | ||
await api.query.nfts.collection(collectionId.value) | ||
).toJSON() as { | ||
items: string | ||
} | ||
initTransactionLoader() | ||
const cb = api.tx.nfts.mint | ||
const args = [ | ||
collectionId.value, | ||
collectionRes.items, | ||
accountId.value, | ||
{ | ||
ownedItem: null, | ||
mintPrice: price.value, | ||
}, | ||
] | ||
mintNftSN.value = collectionRes.items | ||
howAboutToExecute(accountId.value, cb, args) | ||
} catch (e) { | ||
showNotification(`[MINT::ERR] ${e}`, notificationTypes.warn) | ||
$consola.error(e) | ||
isTransactionLoading.value = false | ||
} | ||
} | ||
watch(status, (curStatus) => { | ||
if (curStatus === TransactionStatus.Block) { | ||
submitMint(mintNftSN.value) | ||
} | ||
}) | ||
const clearWalletConnecting = () => { | ||
isWalletConnecting.value = false | ||
} | ||
const handleSubmitMint = async () => { | ||
if (!isLogIn.value) { | ||
isWalletConnecting.value = true | ||
doAfterLogin({ | ||
onLoginSuccess: clearWalletConnecting, | ||
onCancel: clearWalletConnecting, | ||
}) | ||
return | ||
} | ||
if (isLoading.value || isImageFetching.value) { | ||
return false | ||
} | ||
if (hasMinimumFunds.value) { | ||
mintNft() | ||
} else { | ||
openAddFundModal() | ||
} | ||
} | ||
const openAddFundModal = () => { | ||
isAddFundModalActive.value = true | ||
} | ||
const closeAddFundModal = () => { | ||
isAddFundModalActive.value = false | ||
} | ||
const submitMint = async (sn: string) => { | ||
try { | ||
isImageFetching.value = true | ||
const imageHash = await tryCapture() | ||
const hash = await createUnlockableMetadata( | ||
imageHash, | ||
description.value, | ||
collectionName.value || defaultName.value, | ||
'text/html', | ||
selectedImage.value, | ||
) | ||
isImageFetching.value = false | ||
const { result } = await claimDropItem( | ||
{ | ||
account: accountId.value, | ||
metadata: hash, | ||
sn, | ||
image: imageHash, | ||
}, | ||
props.drop.id, | ||
) | ||
await fetchDropStatus() | ||
const id = `${collectionId.value}-${result.sn}` | ||
subscribeToMintedNft(id, async () => { | ||
mintedNftWithMetadata.value = await fetchNft(id) | ||
}) | ||
isLoading.value = false | ||
mintedNft.value = { | ||
...result, | ||
id, | ||
name: result.name, | ||
collectionName: collectionName.value, | ||
} | ||
} catch (error) { | ||
toast($i18n.t('drops.mintPerAddress')) | ||
isImageFetching.value = false | ||
throw error | ||
} | ||
} | ||
const handleDropAddModalConfirm = () => { | ||
closeAddFundModal() | ||
fetchMultipleBalance([urlPrefix.value]) | ||
} | ||
</script> | ||
|
||
<style scoped lang="scss"> | ||
.order-1 { | ||
order: 1; | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.