diff --git a/apps/verification-portal/src/lib/features/DOTphin.ts b/apps/verification-portal/src/lib/features/DOTphin.ts index 3fa0dc5c3..47d6342e9 100644 --- a/apps/verification-portal/src/lib/features/DOTphin.ts +++ b/apps/verification-portal/src/lib/features/DOTphin.ts @@ -11,6 +11,7 @@ import { import { fetchDotphinData, submitClaim, + evolveDotphin, claimStatus, burnNFT, } from '$lib/shared/apiServices/DOTphinAPI'; @@ -40,6 +41,10 @@ export async function updateMultipassStateForAddress(address: string) { DID: data.dotphinDID, pending: false, }, + evolution: { + level: data.dotphin ? getLevelAttributeValue(data.dotphin) : 0, + maxLevel: data.dotphinMaxLevel, + }, }); if (data.dotphinDID) { @@ -120,7 +125,13 @@ async function fetchNFTDataByDID(did: string) { console.error('Error fetching NFT data:', error); } } - +// Get Level Attribute Value +function getLevelAttributeValue(asset: DeepAsset): number { + const levelAttribute = asset.attributes?.find( + (attr) => attr.trait_type === 'level' + ); + return levelAttribute ? Number(levelAttribute.value) : 0; +} // Find Proof by Trait Type function findProofByTraitType( proofs: DeepAsset[], @@ -139,16 +150,29 @@ function findProofByTraitType( } // Claim Proof by Trait Type -export async function claimProofByTraitType( +export async function handleProofByTraitType( address: string, - element: 'air' | 'water' | 'earth' + element: 'air' | 'water' | 'earth', + action: 'claim' | 'evolve' ) { const data = get(multipassData); const proofAddress = data.proofs ? findProofByTraitType(data.proofs, element) : undefined; if (proofAddress) { - await claimDOTphinNFT(address, proofAddress); + if (action === 'claim') { + await claimDOTphinNFT(address, proofAddress); + } else if (action === 'evolve') { + const dotphinDID = data.nft.DID; + if (dotphinDID) { + await evolveDOTphinNFT(address, dotphinDID, proofAddress); + } else { + toast.error( + 'Error: No DOTphin found for evolution, please refresh the page and try again' + ); + console.log('No DOTphin DID found for evolution'); + } + } } else { toast.error( 'Error finding a valid proof, please refresh the page and try again' @@ -157,6 +181,39 @@ export async function claimProofByTraitType( } } +// Evolve DOTphin NFT +export async function evolveDOTphinNFT( + address: string, + dotphinDID: string, + proofDID: string +) { + try { + const evolveData = await evolveDotphin(address, dotphinDID, proofDID); + toast.success('Evolution submitted successfully'); + const currentState = get(multipassData); + updateState({ + ...currentState, + proofStats: { + ...currentState.proofStats, + available: { + ...currentState.proofStats.available, + total: currentState.proofStats.available.total - 1, + }, + }, + nft: { DID: dotphinDID, data: evolveData, pending: true }, + evolution: { + ...currentState.evolution, + level: currentState.evolution.level + 1, + }, + }); + setCookie('claimPending', evolveData.id); + checkClaimStatus(evolveData.id, address); + } catch (error) { + toast.error('Error submitting evolve, please try again'); + console.error('Error during evolve submission:', error); + } +} + // Burn DOTphin NFT export async function burnDOTphinNFT() { const currentState = get(multipassData); diff --git a/apps/verification-portal/src/lib/features/MultipassStates.ts b/apps/verification-portal/src/lib/features/MultipassStates.ts index 3a4166794..36817e9d0 100644 --- a/apps/verification-portal/src/lib/features/MultipassStates.ts +++ b/apps/verification-portal/src/lib/features/MultipassStates.ts @@ -7,7 +7,6 @@ const canEvolve = isFeatureEnabled('dotphinEvolution'); import { MAX_EVOLUTION_LEVEL, - EVOLUTION_LIMIT, initialStepConfig, STATUS, } from '$lib/shared/multipassConfig'; @@ -39,6 +38,7 @@ export type MultipassData = { }; evolution: { level: number; + maxLevel: number; }; }; @@ -58,6 +58,7 @@ const initialState: MultipassData = { }, evolution: { level: 0, + maxLevel: MAX_EVOLUTION_LEVEL, // set limit same as max }, }; @@ -112,7 +113,9 @@ export const evolveStepState = derived( return 'INITIAL'; } else if ($multipassData.evolution.level >= MAX_EVOLUTION_LEVEL) { return 'COMPLETE'; - } else if ($multipassData.evolution.level >= EVOLUTION_LIMIT) { + } else if ( + $multipassData.evolution.level >= $multipassData.evolution.maxLevel + ) { return 'LIMITED'; } else { return 'EVOLVING'; @@ -159,7 +162,8 @@ export const multipassStepConfig = derived( : proofsStatus === STATUS.COMPLETE && nftStatus === STATUS.COMPLETE ? $multipassData.evolution.level >= MAX_EVOLUTION_LEVEL ? STATUS.COMPLETE - : $multipassData.evolution.level >= EVOLUTION_LIMIT + : $multipassData.evolution.level >= + $multipassData.evolution.maxLevel ? STATUS.LOCKED : STATUS.ACTIVE : STATUS.LOCKED; diff --git a/apps/verification-portal/src/lib/shared/apiServices/DOTphinAPI.ts b/apps/verification-portal/src/lib/shared/apiServices/DOTphinAPI.ts index 9960c7071..dc3b3ed1d 100644 --- a/apps/verification-portal/src/lib/shared/apiServices/DOTphinAPI.ts +++ b/apps/verification-portal/src/lib/shared/apiServices/DOTphinAPI.ts @@ -31,6 +31,22 @@ export async function claimStatus(claimId: string) { return data.onChain.status === 'success'; } +// Evolve Dotphin +export async function evolveDotphin( + address: string, + dotphinDID: string, + proofDID: string +) { + console.table({ address, dotphinDID, proofDID }); + const response = await fetch(`${BASE_URL}/evolve`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ address, dotphinDID, proofDID }), + }); + if (!response.ok) throw new Error('Failed to evolve DOTphin'); + return await response.json(); +} + // Burn NFT export async function burnNFT(dotphinDID: string, owner: string) { const response = await fetch(`${BASE_URL}/burn`, { diff --git a/apps/verification-portal/src/lib/shared/i18n/en/index.ts b/apps/verification-portal/src/lib/shared/i18n/en/index.ts index f4a5b48e4..3ebb805ca 100644 --- a/apps/verification-portal/src/lib/shared/i18n/en/index.ts +++ b/apps/verification-portal/src/lib/shared/i18n/en/index.ts @@ -163,6 +163,7 @@ const en: BaseTranslation = { nftSubtitle: `One Eco-evolving Avatar`, }, pendingCollect: `Hang tight, we're processing your claim! This could take up to a minute. Feel free to check back soon to meet your DOTphin pal! 🐬🌊`, + pendingEvolve: `Hang tight, we're processing your evolution! This could take up to a minute. Feel free to check back soon and see the evolution! 🐬🌊`, state: { proofStep: { stepTitle: 'Proofs', diff --git a/apps/verification-portal/src/lib/shared/i18n/i18n-types.ts b/apps/verification-portal/src/lib/shared/i18n/i18n-types.ts index ea66d817e..be67a3e3d 100644 --- a/apps/verification-portal/src/lib/shared/i18n/i18n-types.ts +++ b/apps/verification-portal/src/lib/shared/i18n/i18n-types.ts @@ -480,6 +480,10 @@ type RootTranslation = { * H​a​n​g​ ​t​i​g​h​t​,​ ​w​e​'​r​e​ ​p​r​o​c​e​s​s​i​n​g​ ​y​o​u​r​ ​c​l​a​i​m​!​ ​T​h​i​s​ ​c​o​u​l​d​ ​t​a​k​e​ ​u​p​ ​t​o​ ​a​ ​m​i​n​u​t​e​.​ ​F​e​e​l​ ​f​r​e​e​ ​t​o​ ​c​h​e​c​k​ ​b​a​c​k​ ​s​o​o​n​ ​t​o​ ​m​e​e​t​ ​y​o​u​r​ ​D​O​T​p​h​i​n​ ​p​a​l​!​ ​�​�​�​� */ pendingCollect: string + /** + * H​a​n​g​ ​t​i​g​h​t​,​ ​w​e​'​r​e​ ​p​r​o​c​e​s​s​i​n​g​ ​y​o​u​r​ ​e​v​o​l​u​t​i​o​n​!​ ​T​h​i​s​ ​c​o​u​l​d​ ​t​a​k​e​ ​u​p​ ​t​o​ ​a​ ​m​i​n​u​t​e​.​ ​F​e​e​l​ ​f​r​e​e​ ​t​o​ ​c​h​e​c​k​ ​b​a​c​k​ ​s​o​o​n​ ​a​n​d​ ​s​e​e​ ​t​h​e​ ​e​v​o​l​u​t​i​o​n​!​ ​�​�​�​� + */ + pendingEvolve: string state: { proofStep: { /** @@ -1203,6 +1207,10 @@ export type TranslationFunctions = { * Hang tight, we're processing your claim! This could take up to a minute. Feel free to check back soon to meet your DOTphin pal! 🐬🌊 */ pendingCollect: () => LocalizedString + /** + * Hang tight, we're processing your evolution! This could take up to a minute. Feel free to check back soon and see the evolution! 🐬🌊 + */ + pendingEvolve: () => LocalizedString state: { proofStep: { /** diff --git a/apps/verification-portal/src/lib/shared/multipassConfig.ts b/apps/verification-portal/src/lib/shared/multipassConfig.ts index fa2daefcb..afde8c4dd 100644 --- a/apps/verification-portal/src/lib/shared/multipassConfig.ts +++ b/apps/verification-portal/src/lib/shared/multipassConfig.ts @@ -1,5 +1,4 @@ export const MAX_EVOLUTION_LEVEL = 4; -export const EVOLUTION_LIMIT = 3; // Multipass step states, derived from API data. Used for dynamic UI/UX and translation based on API Data points export type ProofStepState = diff --git a/apps/verification-portal/src/lib/widgets/DOTphin/CollectForm/CollectModal.svelte b/apps/verification-portal/src/lib/widgets/DOTphin/CollectForm/CollectModal.svelte index 2ee210d8a..ac07780ae 100644 --- a/apps/verification-portal/src/lib/widgets/DOTphin/CollectForm/CollectModal.svelte +++ b/apps/verification-portal/src/lib/widgets/DOTphin/CollectForm/CollectModal.svelte @@ -4,14 +4,13 @@ closeModal, } from '$lib/widgets/DOTphin/collectModalStore'; import ModalWrapper from '$lib/shared/components/ModalWrapper.svelte'; - import Form from './Form.svelte'; const title = 'Pick an Element'; closeModal()} autoclose={false} @@ -20,11 +19,15 @@

{title}

- The element will determine the type of DOTphin that will hatch + {#if $formModal.action === 'claim'} + The element will determine the type of DOTphin that will hatch + {:else if $formModal.action === 'evolve'} + The element will determine the type of next evolution + {/if}

-
+
diff --git a/apps/verification-portal/src/lib/widgets/DOTphin/CollectForm/Form.svelte b/apps/verification-portal/src/lib/widgets/DOTphin/CollectForm/Form.svelte index 6ecb3083a..ad05a1caf 100644 --- a/apps/verification-portal/src/lib/widgets/DOTphin/CollectForm/Form.svelte +++ b/apps/verification-portal/src/lib/widgets/DOTphin/CollectForm/Form.svelte @@ -1,11 +1,14 @@ @@ -33,7 +34,7 @@ umamiID="collect-orbo" disabled={$multipassStepConfig.nft.stepStatus !== 'active'} title={$LL.multipass.state.nftStep.UNCLAIMED.cta()} - on:click={() => openModal()} + on:click={() => openModal('claim')} /> @@ -42,7 +43,6 @@ /> - {:else}
- {#if showConfetti && !$isLoading} + {#if $showConfetti && !$isLoading} {/if}
diff --git a/apps/verification-portal/src/lib/widgets/DOTphin/Timeline.svelte b/apps/verification-portal/src/lib/widgets/DOTphin/Timeline.svelte index 810c926da..96a33df4a 100644 --- a/apps/verification-portal/src/lib/widgets/DOTphin/Timeline.svelte +++ b/apps/verification-portal/src/lib/widgets/DOTphin/Timeline.svelte @@ -7,10 +7,12 @@ import { Timeline } from 'flowbite-svelte'; import Spinner from '$lib/components/icons/Spinner.svelte'; import { LL } from '$lib/shared/i18n/i18n-svelte'; + import CollectModal from '$lib/widgets/DOTphin/CollectForm/CollectModal.svelte'; import { multipassData, nftStepState, + evolveStepState, resetData, } from '$lib/features/MultipassStates'; import { updateMultipassStateForAddress } from '$lib/features/DOTphin'; @@ -53,7 +55,11 @@ > - {$LL.multipass.pendingCollect()} + {#if $evolveStepState === 'EVOLVING'} + {$LL.multipass.pendingEvolve()} + {:else} + {$LL.multipass.pendingCollect()} + {/if} {/if} @@ -67,3 +73,7 @@ + +{#if $nftStepState === 'UNCLAIMED' || $evolveStepState === 'INITIAL' || $evolveStepState === 'EVOLVING'} + +{/if} diff --git a/apps/verification-portal/src/lib/widgets/DOTphin/collectModalStore.ts b/apps/verification-portal/src/lib/widgets/DOTphin/collectModalStore.ts index 4e24e3c64..02752628f 100644 --- a/apps/verification-portal/src/lib/widgets/DOTphin/collectModalStore.ts +++ b/apps/verification-portal/src/lib/widgets/DOTphin/collectModalStore.ts @@ -1,12 +1,21 @@ -// modalStore.ts +// collectModalStore.ts import { writable } from 'svelte/store'; -export const formModal = writable(false); +// Define the store with an object containing open state and action type +export const formModal = writable<{ + open: boolean; + action: 'claim' | 'evolve'; +}>({ + open: false, + action: 'claim', +}); -export function openModal() { - formModal.set(true); +// Function to open the modal with a specific action +export function openModal(action: 'claim' | 'evolve') { + formModal.set({ open: true, action }); } +// Function to close the modal and reset the action export function closeModal() { - formModal.set(false); + formModal.set({ open: false, action: 'claim' }); } diff --git a/apps/verification-portal/src/lib/widgets/DOTphin/confettiStore.ts b/apps/verification-portal/src/lib/widgets/DOTphin/confettiStore.ts new file mode 100644 index 000000000..d8787cc33 --- /dev/null +++ b/apps/verification-portal/src/lib/widgets/DOTphin/confettiStore.ts @@ -0,0 +1,14 @@ +import { writable } from 'svelte/store'; + +// Create a writable store for showConfetti +export const showConfetti = writable(false); + +// Function to trigger confetti +export function triggerConfetti() { + showConfetti.set(true); +} + +// Function to reset confetti +export function resetConfetti() { + showConfetti.set(false); +}