Skip to content

Commit

Permalink
make mint flow bottom sheet generic, powered by sanity
Browse files Browse the repository at this point in the history
  • Loading branch information
kaitoo1 committed Apr 29, 2024
1 parent d8c0dc4 commit dc52030
Show file tree
Hide file tree
Showing 9 changed files with 237 additions and 92 deletions.
47 changes: 25 additions & 22 deletions apps/mobile/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import ToastProvider from './contexts/ToastContext';
import { TokenStateManagerProvider } from './contexts/TokenStateManagerContext';
import { magic } from './magic';
import { useCacheIntroVideo } from './screens/Onboarding/useCacheIntroVideo';
import SanityDataProvider from './contexts/SanityDataContext';

SplashScreen.preventAutoHideAsync();

Expand Down Expand Up @@ -157,28 +158,30 @@ export default function App() {
<magic.Relayer />
<SearchProvider>
<NavigationContainer ref={navigationRef}>
<ToastProvider>
<TokenStateManagerProvider>
<PortalProvider>
<BottomSheetModalProvider>
<SyncTokensProvider>
<ManageWalletProvider>
<AnnouncementProvider>
{/* Register the user's push token if one exists (does not prompt the user) */}
<NotificationRegistrar />
<DevMenuItems />
<DeepLinkRegistrar />
<RootStackNavigator
navigationContainerRef={navigationRef}
/>
</AnnouncementProvider>
</ManageWalletProvider>
</SyncTokensProvider>
<PortalHost name="app-context" />
</BottomSheetModalProvider>
</PortalProvider>
</TokenStateManagerProvider>
</ToastProvider>
<SanityDataProvider>
<ToastProvider>
<TokenStateManagerProvider>
<PortalProvider>
<BottomSheetModalProvider>
<SyncTokensProvider>
<ManageWalletProvider>
<AnnouncementProvider>
{/* Register the user's push token if one exists (does not prompt the user) */}
<NotificationRegistrar />
<DevMenuItems />
<DeepLinkRegistrar />
<RootStackNavigator
navigationContainerRef={navigationRef}
/>
</AnnouncementProvider>
</ManageWalletProvider>
</SyncTokensProvider>
<PortalHost name="app-context" />
</BottomSheetModalProvider>
</PortalProvider>
</TokenStateManagerProvider>
</ToastProvider>
</SanityDataProvider>
</NavigationContainer>
</SearchProvider>
</SafeAreaProvider>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,48 @@
import { MCHX_CLAIM_CODE_KEY } from 'src/constants/storageKeys';
import { useMemo } from 'react';
import usePersistedState from 'src/hooks/usePersistedState';

import { useSanityDataContext } from '~/contexts/SanityDataContext';

import MintCampaignPostTransaction from './MintCampaignPostTransaction';
import MintCampaignPreTransaction from './MintCampaignPreTransaction';

export default function MintCampaignBottomSheet({ onClose }: { onClose: () => void }) {
type Props = {
onClose?: () => void;
projectInternalId?: string;
};

export default function MintCampaignBottomSheet({ onClose, projectInternalId }: Props) {
// claimCode is the identifer used to poll for the status of the mint
// Once we kick off the mint, the backend returns a claim code from Highlight that we can use to check the status of the mint

const [claimCode, setClaimCode] = usePersistedState(MCHX_CLAIM_CODE_KEY, '');
const [claimCode, setClaimCode] = usePersistedState(`${projectInternalId}_claim_code`, '');

const { data } = useSanityDataContext();
const projectData = useMemo(() => {
return data?.mintProjects.find((document) => document.internalId === projectInternalId);
}, [data, projectInternalId]);
console.log({ projectData });

if (!projectData) {
// TODO decide best way to handle missing data
return null;
}

if (claimCode) {
return <MintCampaignPostTransaction claimCode={claimCode} onClose={onClose} />;
return (
<MintCampaignPostTransaction
claimCode={claimCode}
onClose={onClose}
projectData={projectData}
/>
);
}

return <MintCampaignPreTransaction setClaimCode={setClaimCode} />;
return (
<MintCampaignPreTransaction
setClaimCode={setClaimCode}
projectInternalId={projectInternalId}
projectData={projectData}
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { SpinnerIcon } from 'src/icons/SpinnerIcon';

import { Button } from '~/components/Button';
import { BaseM, TitleS } from '~/components/Text';
import { MintProject } from '~/contexts/SanityDataContext';
import {
HighlightTxStatus,
MintCampaignPostTransactionMintStatusQuery,
Expand All @@ -26,9 +27,11 @@ import { NftDetailAsset } from '../../../screens/NftDetailScreen/NftDetailAsset/
export default function MintCampaignPostTransaction({
claimCode,
onClose,
projectData,
}: {
claimCode: string;
onClose: () => void;
onClose?: () => void;
projectData: MintProject;
}) {
const [state, setState] = useState<HighlightTxStatus>('TX_PENDING');
// TODO: Fix any - prioritizing merging atm
Expand Down Expand Up @@ -119,7 +122,9 @@ export default function MintCampaignPostTransaction({
});
}

onClose();
if (onClose) {
onClose();
}

// close bottom sheet
}, [navigation, onClose, token?.dbid]);
Expand All @@ -129,15 +134,22 @@ export default function MintCampaignPostTransaction({
if (token?.definition?.community) {
navigateToCommunity(token?.definition?.community);
}
onClose();
if (onClose) {
onClose();
}
}, [navigateToCommunity, onClose, token?.definition?.community]);

if (state === 'TOKEN_SYNCED' && token) {
return (
<View>
<View className="mb-1">
<TitleS>Congratulations!</TitleS>
<TitleS>You collected {`${token?.definition?.name ?? 'Radiance'} by MCHX`}</TitleS>
<TitleS>
You collected{' '}
{`${token?.definition?.name ?? projectData.collectionName} by ${
projectData.artistName
}`}
</TitleS>
</View>
<BaseM>
Thank you for using the Gallery mobile app. Share your unique piece with others below!
Expand Down Expand Up @@ -173,7 +185,7 @@ export default function MintCampaignPostTransaction({
<BaseM>
{state === 'TX_PENDING'
? 'Your new artwork is being minted onchain. This should take less than a minute.'
: 'Revealing your unique artwork. It will be ready to view in a moment!'}
: 'Revealing your artwork. It will be ready to view in a moment!'}
</BaseM>

<View className="my-4 flex justify-center items-center bg-faint dark:bg-black-700 w-full aspect-square ">
Expand All @@ -183,30 +195,30 @@ export default function MintCampaignPostTransaction({
size="s"
colorOverride={{ lightMode: colors.shadow, darkMode: colors.shadow }}
/>
<LoadingStateMessage />
<LoadingStateMessage funFacts={projectData.funFacts} />
</View>
</View>
{error && <BaseM classNameOverride="text-red">{error}</BaseM>}
</View>
);
}

const COPY = [
'Anton Dubrovin, aka MCHX, was born in Kazakhstan and is currently based in Georgia.',
'Anton is a digital artist known for experimenting with colors and form.',
'Anton uses color as a universal channel of emotional connection and self-exploration.',
'For this project, MCHX created over 60 unique color modes and used a circle as the central object due to its universal symbolism of unity and integrity.',
'This work leverages Javascript, GLSL, and Display P3 wide-gamut to explore emotional connection through color.',
"Anton's diverse inspiration comes from 20th-century abstraction, Abstract Expressionism, Color Field artists, nature, music, and the internet.",
'In his free time, Anton enjoys taking walks, reading, and watching Japanese anime and dramas.',
'Anton has been creating art since 2016, but entered the NFT and Web3 space in 2020.',
'Anton believes in the exchange of energy inherent in blockchain interactions and his work carries imprints of his emotional states or needs at the time of creation.',
];
// const COPY = [
// 'Anton Dubrovin, aka MCHX, was born in Kazakhstan and is currently based in Georgia.',
// 'Anton is a digital artist known for experimenting with colors and form.',
// 'Anton uses color as a universal channel of emotional connection and self-exploration.',
// 'For this project, MCHX created over 60 unique color modes and used a circle as the central object due to its universal symbolism of unity and integrity.',
// 'This work leverages Javascript, GLSL, and Display P3 wide-gamut to explore emotional connection through color.',
// "Anton's diverse inspiration comes from 20th-century abstraction, Abstract Expressionism, Color Field artists, nature, music, and the internet.",
// 'In his free time, Anton enjoys taking walks, reading, and watching Japanese anime and dramas.',
// 'Anton has been creating art since 2016, but entered the NFT and Web3 space in 2020.',
// 'Anton believes in the exchange of energy inherent in blockchain interactions and his work carries imprints of his emotional states or needs at the time of creation.',
// ];

const FADE_DURATION = 250;
const TEXT_DURATION = 8000;

function LoadingStateMessage() {
function LoadingStateMessage({ funFacts }: { funFacts: string[] }) {
const [index, setIndex] = useState(0);

const fadeInOpacity = useSharedValue(1);
Expand Down Expand Up @@ -235,18 +247,18 @@ function LoadingStateMessage() {
const updateDisplayedMessage = async () => {
fadeOut();
await new Promise((resolve) => setTimeout(resolve, FADE_DURATION));
setIndex((index) => (index + 1) % COPY.length);
setIndex((index) => (index + 1) % funFacts.length);
fadeIn();
};

const interval = setInterval(updateDisplayedMessage, TEXT_DURATION);

return () => clearInterval(interval);
}, [fadeIn, fadeOut]);
}, [fadeIn, fadeOut, funFacts.length]);
return (
<View className="text-center h-32">
<Animated.View style={[animatedStyle]}>
<BaseM classNameOverride="text-shadow text-center ">{COPY[index]}</BaseM>
<BaseM classNameOverride="text-shadow text-center ">{funFacts[index]}</BaseM>
</Animated.View>
</View>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,25 @@ import { Suspense, useCallback, useEffect, useMemo, useState } from 'react';
import { Image, View } from 'react-native';
import { graphql, useLazyLoadQuery } from 'react-relay';
import { contexts } from 'shared/analytics/constants';
import { MCHX_CLAIM_CODE_KEY } from 'src/constants/storageKeys';
import { useHighlightClaimMint } from 'src/hooks/useHighlightClaimMint';
import usePersistedState from 'src/hooks/usePersistedState';

import { Button } from '~/components/Button';
import { BaseM, BaseS, TitleLItalic, TitleS } from '~/components/Text';
import { MintProject } from '~/contexts/SanityDataContext';
import { MintCampaignPreTransactionQuery } from '~/generated/MintCampaignPreTransactionQuery.graphql';

export const MCHX_MINT_CAMPAIGN_END_DATE = '2024-05-05T10:00:00-04:00';

const MCHX_COLLECTION_ID = '660d4342c6bc04d5dc5598e7';

export default function MintCampaignPreTransaction({
setClaimCode,
projectInternalId,
projectData,
}: {
setClaimCode: (claimCode: string) => void;
projectInternalId: string;
projectData: MintProject;
}) {
const calculateTimeLeftText = useCallback(() => {
const endDate = new Date(MCHX_MINT_CAMPAIGN_END_DATE).getTime();
const endDate = new Date(projectData.endDate).getTime();
const now = new Date().getTime();
const difference = endDate - now;

Expand All @@ -33,13 +33,13 @@ export default function MintCampaignPreTransaction({
}

return 'Campaign ended';
}, []);
}, [projectData.endDate]);

const isMintOver = useMemo(() => {
const endDate = new Date(MCHX_MINT_CAMPAIGN_END_DATE).getTime();
const endDate = new Date(projectData.endDate).getTime();
const now = new Date().getTime();
return now > endDate;
}, []);
}, [projectData.endDate]);

const [timeLeft, setTimeLeft] = useState(calculateTimeLeftText());
const [error, setError] = useState('');
Expand All @@ -54,7 +54,7 @@ export default function MintCampaignPreTransaction({

const { claimMint, isClamingMint } = useHighlightClaimMint();

const [, setClaimCodeLocalStorage] = usePersistedState(MCHX_CLAIM_CODE_KEY, '');
const [, setClaimCodeLocalStorage] = usePersistedState(`${projectInternalId}_claim_code`, '');

const handlePress = useCallback(
async (recipientWalletId: string) => {
Expand All @@ -64,7 +64,7 @@ export default function MintCampaignPreTransaction({

try {
const claimCode = await claimMint({
collectionId: MCHX_COLLECTION_ID,
collectionId: projectData.highlightProjectId,
recipientWalletId,
});

Expand All @@ -84,20 +84,17 @@ export default function MintCampaignPreTransaction({
setError('Something went wrong while minting. Please try again.');
}
},
[claimMint, setClaimCode, setClaimCodeLocalStorage]
[claimMint, projectData.highlightProjectId, setClaimCode, setClaimCodeLocalStorage]
);

return (
<View>
<TitleS>Exclusive free mint</TitleS>
<BaseM classNameOverride="mt-1">
Thank you for downloading the Gallery app. As a token of our gratitude, we invite you to
mint Radiance by MCHX - on us ❤️
</BaseM>
<TitleS>{projectData.title}</TitleS>
<BaseM classNameOverride="mt-1">{projectData.description}</BaseM>
<Image
className="w-full aspect-square my-4"
source={{
uri: 'https://slack-imgs.com/?c=1&o1=ro&url=https%3A%2F%2Fhighlight-creator-assets.highlight.xyz%2Fmain%2Fimage%2Fad73bc52-3e26-45c7-a73c-3666f165e9fa.png%3Fd%3D1000x1000',
uri: projectData.previewImageUrl,
}}
/>
<View className="flex flex-row space-apart justify-between">
Expand All @@ -107,7 +104,7 @@ export default function MintCampaignPreTransaction({
<BaseM>Limit 1 per user</BaseM>
</View>
<View>
<TitleLItalic>Gallery x MCHX</TitleLItalic>
<TitleLItalic>Gallery x {projectData.artistName}</TitleLItalic>
</View>
</View>
<View className="my-3">
Expand All @@ -128,7 +125,7 @@ export default function MintCampaignPreTransaction({
{error && <BaseM classNameOverride="text-red">{error}</BaseM>}
</View>
<BaseS>
Note: Image above is an indicative preview only, final artwork will be uniquely generated.
Note: Image above is an indicative preview only, final artwork will be randomly generated.
Powered by highlight.xyz
</BaseS>
</View>
Expand Down
6 changes: 3 additions & 3 deletions apps/mobile/src/components/Notification/NotificationList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export function NotificationList({ queryRef }: Props) {
queryRef
);

const { announcement, fetchAnnouncement } = useAnnouncementContext();
const { announcement, fetchAnnouncement, hasDismissedAnnouncement } = useAnnouncementContext();

const clearNotifications = useMobileClearNotifications();
const { isRefreshing, handleRefresh } = useRefreshHandle(refetch);
Expand All @@ -83,14 +83,14 @@ export function NotificationList({ queryRef }: Props) {
}
}

if (announcement && announcement.active) {
if (announcement && announcement.active && !hasDismissedAnnouncement) {
notifications.push({ id: 'announcement', kind: 'announcement' });
}

notifications.reverse();

return notifications;
}, [announcement, query]);
}, [announcement, hasDismissedAnnouncement, query]);

const loadMore = useCallback(() => {
if (hasPrevious) {
Expand Down
Loading

0 comments on commit dc52030

Please sign in to comment.