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

Evolve Flow modal #1900

Merged
merged 4 commits into from
Oct 23, 2024
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
65 changes: 61 additions & 4 deletions apps/verification-portal/src/lib/features/DOTphin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
import {
fetchDotphinData,
submitClaim,
evolveDotphin,
claimStatus,
burnNFT,
} from '$lib/shared/apiServices/DOTphinAPI';
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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[],
Expand All @@ -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'
Expand All @@ -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);
Expand Down
10 changes: 7 additions & 3 deletions apps/verification-portal/src/lib/features/MultipassStates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ const canEvolve = isFeatureEnabled('dotphinEvolution');

import {
MAX_EVOLUTION_LEVEL,
EVOLUTION_LIMIT,
initialStepConfig,
STATUS,
} from '$lib/shared/multipassConfig';
Expand Down Expand Up @@ -39,6 +38,7 @@ export type MultipassData = {
};
evolution: {
level: number;
maxLevel: number;
};
};

Expand All @@ -58,6 +58,7 @@ const initialState: MultipassData = {
},
evolution: {
level: 0,
maxLevel: MAX_EVOLUTION_LEVEL, // set limit same as max
},
};

Expand Down Expand Up @@ -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';
Expand Down Expand Up @@ -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;
Expand Down
16 changes: 16 additions & 0 deletions apps/verification-portal/src/lib/shared/apiServices/DOTphinAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`, {
Expand Down
1 change: 1 addition & 0 deletions apps/verification-portal/src/lib/shared/i18n/en/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
8 changes: 8 additions & 0 deletions apps/verification-portal/src/lib/shared/i18n/i18n-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
/**
Expand Down Expand Up @@ -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: {
/**
Expand Down
1 change: 0 additions & 1 deletion apps/verification-portal/src/lib/shared/multipassConfig.ts
Original file line number Diff line number Diff line change
@@ -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 =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@
closeModal,
} from '$lib/widgets/DOTphin/collectModalStore';
import ModalWrapper from '$lib/shared/components/ModalWrapper.svelte';

import Form from './Form.svelte';

Check warning on line 7 in apps/verification-portal/src/lib/widgets/DOTphin/CollectForm/CollectModal.svelte

View workflow job for this annotation

GitHub Actions / code-quality

`./Form.svelte` import should occur before import of `$lib/widgets/DOTphin/collectModalStore`

Check warning on line 7 in apps/verification-portal/src/lib/widgets/DOTphin/CollectForm/CollectModal.svelte

View workflow job for this annotation

GitHub Actions / code-quality

`./Form.svelte` import should occur before import of `$lib/widgets/DOTphin/collectModalStore`

const title = 'Pick an Element';
</script>

<ModalWrapper
open={$formModal}
open={$formModal.open}
size="sm"
on:close={() => closeModal()}
autoclose={false}
Expand All @@ -20,11 +19,15 @@
<div class="text-center px-4 dark:text-gray-200">
<h2 class="text-3xl font-sans">{title}</h2>
<p class="text-sm">
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}
</p>
</div>
<div class="pt-1 pb-16">
<Form />
<Form action={$formModal.action} />
</div>
</svelte:fragment>
</ModalWrapper>
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
<script lang="ts">
import TileIcon from '$lib/widgets/DOTphin/CollectForm/TileIcon.svelte';
import { multipassData } from '$lib/features/MultipassStates';
import { claimProofByTraitType } from '$lib/features/DOTphin';
import { handleProofByTraitType } from '$lib/features/DOTphin';
import Spinner from '$lib/components/icons/Spinner.svelte';

let selectedValue: string | null = null;
import { resetConfetti } from '$lib/widgets/DOTphin/confettiStore';
import { closeModal } from '$lib/widgets/DOTphin/collectModalStore';
let selectedValue: string | null = null;

export let action: 'claim' | 'evolve' = 'claim';

function handleTileClick(value: string) {
selectedValue = value;
handleSubmit();
Expand All @@ -17,11 +20,13 @@
isSubmitting = true;

if ($multipassData.address && selectedValue !== null) {
await claimProofByTraitType(
await handleProofByTraitType(
$multipassData.address,
selectedValue as 'earth' | 'air' | 'water'
selectedValue as 'earth' | 'air' | 'water',
action
).finally(() => {
isSubmitting = false;
resetConfetti(); // so that evolution triggers new state
closeModal();
});
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<script lang="ts">
import { openModal } from '$lib/widgets/DOTphin/collectModalStore';

import {
multipassData,
evolveStepState,
Expand All @@ -11,8 +13,8 @@
import TimelineActionButton from '$lib/widgets/DOTphin/TimelineItem/TimelineActionButton.svelte';
import CardSubtitle from '$lib/widgets/DOTphin/TimelineItem/Typography/Subtitle.svelte';
import CardTitle from '$lib/widgets/DOTphin/TimelineItem/Typography/Title.svelte';

import { LL } from '$lib/shared/i18n/i18n-svelte';

const cardLink =
siteConfigs?.contentLinks?.DOTphin?.evolution ||
siteConfigs?.contentLinks?.DOTphin?.default ||
Expand All @@ -35,6 +37,7 @@
umamiID="evolution"
disabled={$multipassStepConfig.evolution.stepStatus !== 'active'}
title={$LL.multipass.state.evolveStep.INITIAL.cta()}
on:click={() => openModal('evolve')}
/>
{/if}
</svelte:fragment>
Expand All @@ -57,6 +60,7 @@
})}
</CardSubtitle>
{/if}

{#if cardLink}
<a
href={cardLink}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@
import TimelineItem from '$lib/widgets/DOTphin/TimelineItem/TimelineItem.svelte';
import CardSubtitle from '$lib/widgets/DOTphin/TimelineItem/Typography/Subtitle.svelte';
import { LL } from '$lib/shared/i18n/i18n-svelte';
import { Confetti } from 'svelte-confetti';

Check warning on line 13 in apps/verification-portal/src/lib/widgets/DOTphin/Steps/CardNFT.svelte

View workflow job for this annotation

GitHub Actions / code-quality

`svelte-confetti` import should occur before import of `$lib/widgets/DOTphin/collectModalStore`

Check warning on line 13 in apps/verification-portal/src/lib/widgets/DOTphin/Steps/CardNFT.svelte

View workflow job for this annotation

GitHub Actions / code-quality

`svelte-confetti` import should occur before import of `$lib/widgets/DOTphin/collectModalStore`
import TimelineActionButton from '$lib/widgets/DOTphin/TimelineItem/TimelineActionButton.svelte';
import CollectModal from '../CollectForm/CollectModal.svelte';

let showConfetti = false;
import {
showConfetti,
triggerConfetti,
} from '$lib/widgets/DOTphin/confettiStore';

$: if ($multipassData.nft.pending && !showConfetti) {
showConfetti = true;
$: if ($multipassData.nft.pending && !$showConfetti) {
triggerConfetti();
}
</script>

Expand All @@ -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')}
/>
</svelte:fragment>
<svelte:fragment slot="content">
Expand All @@ -42,7 +43,6 @@
/>
</svelte:fragment>
</TimelineItem>
<CollectModal></CollectModal>
{:else}
<TimelineItem
itemState={$multipassStepConfig.nft.stepStatus}
Expand All @@ -54,7 +54,7 @@
<svelte:fragment slot="featured">
<div class="max-h-[200px] mb-12">
<FeaturedCard item={$multipassData.nft.data} />
{#if showConfetti && !$isLoading}
{#if $showConfetti && !$isLoading}
<Confetti delay={[100, 250]} rounded colorRange={[75, 175]} />
{/if}
</div>
Expand Down
12 changes: 11 additions & 1 deletion apps/verification-portal/src/lib/widgets/DOTphin/Timeline.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
import EvolutionCard from '$lib/widgets/DOTphin/Steps/CardEvolution.svelte';
import ProofCard from '$lib/widgets/DOTphin/Steps/CardProof.svelte';
import NFTCard from '$lib/widgets/DOTphin/Steps/CardNFT.svelte';
import { Timeline } from 'flowbite-svelte';

Check warning on line 7 in apps/verification-portal/src/lib/widgets/DOTphin/Timeline.svelte

View workflow job for this annotation

GitHub Actions / code-quality

`flowbite-svelte` import should occur before import of `$lib/widgets/DOTphin/Steps/CardEvolution.svelte`

Check warning on line 7 in apps/verification-portal/src/lib/widgets/DOTphin/Timeline.svelte

View workflow job for this annotation

GitHub Actions / code-quality

`flowbite-svelte` import should occur before import of `$lib/widgets/DOTphin/Steps/CardEvolution.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';
Expand Down Expand Up @@ -53,7 +55,11 @@
>
<Spinner className=" w-5 h-5 text-primary-200 fill-primary-400 "
></Spinner>
<span> {$LL.multipass.pendingCollect()}</span>
{#if $evolveStepState === 'EVOLVING'}
<span> {$LL.multipass.pendingEvolve()}</span>
{:else}
<span> {$LL.multipass.pendingCollect()}</span>
{/if}
</div>
{/if}
<Timeline {order}>
Expand All @@ -67,3 +73,7 @@
<EvolutionCard />
</Timeline>
</div>

{#if $nftStepState === 'UNCLAIMED' || $evolveStepState === 'INITIAL' || $evolveStepState === 'EVOLVING'}
<CollectModal />
{/if}
Loading
Loading