From 2ef323a7ee78abb8f344f29c0ba5dcdf563fa4f7 Mon Sep 17 00:00:00 2001 From: "dev.tronghieu" Date: Mon, 5 Aug 2024 18:17:32 +0700 Subject: [PATCH 01/14] refactor: scaffold new ui, setup react query --- apps/wallet/package.json | 2 + .../Loyalty/ActionCard/CompletedTag.tsx | 38 -- .../features/Loyalty/ActionCard/CountDown.tsx | 39 -- .../features/Loyalty/ActionCard/StreakBar.tsx | 61 --- .../ActionCard/VerificationNeededTag.tsx | 38 -- .../src/features/Loyalty/ActionCard/index.tsx | 349 ------------- .../wallet/src/features/Loyalty/ErrorText.tsx | 22 + apps/wallet/src/features/Loyalty/Header.tsx | 142 ----- .../src/features/Loyalty/History/Item.tsx | 59 --- .../src/features/Loyalty/History/index.tsx | 152 ------ .../src/features/Loyalty/LoadingText.tsx | 21 + .../Loyalty/PartnerTab/PartnerFolder.tsx | 108 ---- .../Loyalty/PartnerTab/PartnerQuest.tsx | 97 ---- .../src/features/Loyalty/PartnerTab/index.tsx | 75 --- .../features/Loyalty/ProfileCard/InfoCard.tsx | 72 +++ .../features/Loyalty/ProfileCard/index.tsx | 144 +++++ .../features/Loyalty/TaskBoard/TabSelect.tsx | 56 ++ .../Loyalty/TaskBoard/TasksByType.tsx | 85 +++ .../src/features/Loyalty/TaskBoard/index.tsx | 72 +++ .../src/features/Loyalty/WallessTab/index.tsx | 45 -- .../Loyalty/components/ModalHeader.tsx | 39 -- .../features/Loyalty/components/PointTag.tsx | 35 -- apps/wallet/src/features/Loyalty/constants.ts | 7 + apps/wallet/src/features/Loyalty/index.tsx | 220 +++----- apps/wallet/src/features/Loyalty/internal.tsx | 227 -------- apps/wallet/src/features/Loyalty/types.ts | 6 + apps/wallet/src/index.tsx | 31 +- apps/wallet/src/utils/constants.ts | 6 + apps/wallet/src/utils/format.ts | 12 + apps/wallet/src/utils/style.ts | 56 +- packages/graphql/gen-gql/fragment-masking.ts | 66 +++ packages/graphql/gen-gql/gql.ts | 47 ++ packages/graphql/gen-gql/graphql.ts | 490 ++++++++++++++++++ packages/graphql/gen-gql/index.ts | 2 + packages/graphql/mutation/loyalty.ts | 21 - packages/graphql/query/loyalty.ts | 88 +--- packages/graphql/types.ts | 391 ++++++-------- schema.graphql | 356 ++++++------- tool/codegen.ts | 12 + yarn.lock | 109 ++++ 40 files changed, 1703 insertions(+), 2195 deletions(-) delete mode 100644 apps/wallet/src/features/Loyalty/ActionCard/CompletedTag.tsx delete mode 100644 apps/wallet/src/features/Loyalty/ActionCard/CountDown.tsx delete mode 100644 apps/wallet/src/features/Loyalty/ActionCard/StreakBar.tsx delete mode 100644 apps/wallet/src/features/Loyalty/ActionCard/VerificationNeededTag.tsx delete mode 100644 apps/wallet/src/features/Loyalty/ActionCard/index.tsx create mode 100644 apps/wallet/src/features/Loyalty/ErrorText.tsx delete mode 100644 apps/wallet/src/features/Loyalty/Header.tsx delete mode 100644 apps/wallet/src/features/Loyalty/History/Item.tsx delete mode 100644 apps/wallet/src/features/Loyalty/History/index.tsx create mode 100644 apps/wallet/src/features/Loyalty/LoadingText.tsx delete mode 100644 apps/wallet/src/features/Loyalty/PartnerTab/PartnerFolder.tsx delete mode 100644 apps/wallet/src/features/Loyalty/PartnerTab/PartnerQuest.tsx delete mode 100644 apps/wallet/src/features/Loyalty/PartnerTab/index.tsx create mode 100644 apps/wallet/src/features/Loyalty/ProfileCard/InfoCard.tsx create mode 100644 apps/wallet/src/features/Loyalty/ProfileCard/index.tsx create mode 100644 apps/wallet/src/features/Loyalty/TaskBoard/TabSelect.tsx create mode 100644 apps/wallet/src/features/Loyalty/TaskBoard/TasksByType.tsx create mode 100644 apps/wallet/src/features/Loyalty/TaskBoard/index.tsx delete mode 100644 apps/wallet/src/features/Loyalty/WallessTab/index.tsx delete mode 100644 apps/wallet/src/features/Loyalty/components/ModalHeader.tsx delete mode 100644 apps/wallet/src/features/Loyalty/components/PointTag.tsx create mode 100644 apps/wallet/src/features/Loyalty/constants.ts delete mode 100644 apps/wallet/src/features/Loyalty/internal.tsx create mode 100644 apps/wallet/src/features/Loyalty/types.ts create mode 100644 packages/graphql/gen-gql/fragment-masking.ts create mode 100644 packages/graphql/gen-gql/gql.ts create mode 100644 packages/graphql/gen-gql/graphql.ts create mode 100644 packages/graphql/gen-gql/index.ts diff --git a/apps/wallet/package.json b/apps/wallet/package.json index dc4eea052..70cb4d9fa 100644 --- a/apps/wallet/package.json +++ b/apps/wallet/package.json @@ -49,6 +49,7 @@ "@react-navigation/stack": "^6.3.16", "@solana/spl-token": "0.3.7", "@solana/web3.js": "1.92.3", + "@tanstack/react-query": "^5.51.21", "@taquito/signer": "^17.5.2", "@taquito/taquito": "^17.5.2", "@taquito/tzip12": "^16.2.0", @@ -111,6 +112,7 @@ "use-debounce": "^10.0.0" }, "devDependencies": { + "@tanstack/eslint-plugin-query": "^5.51.15", "@types/color": "^3.0.6", "@types/jest": "^29.2.1", "@types/pouchdb-adapter-memory": "^6.1.6", diff --git a/apps/wallet/src/features/Loyalty/ActionCard/CompletedTag.tsx b/apps/wallet/src/features/Loyalty/ActionCard/CompletedTag.tsx deleted file mode 100644 index 9f781ab09..000000000 --- a/apps/wallet/src/features/Loyalty/ActionCard/CompletedTag.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import type { FC } from 'react'; -import type { ViewStyle } from 'react-native'; -import { StyleSheet, View } from 'react-native'; -import { Text } from '@walless/gui'; -import { Check } from '@walless/icons'; - -import { sharedStyles } from '../internal'; - -interface Props { - style?: ViewStyle; -} - -const CompletedTag: FC = ({ style }) => { - return ( - - - - - Completed - - ); -}; - -export default CompletedTag; - -const styles = StyleSheet.create({ - container: { - backgroundColor: 'rgba(47, 200, 121, 0.15)', - }, - completedIcon: { - borderWidth: 1, - borderColor: 'rgba(47, 200, 121, 0.3)', - }, - text: { - fontSize: 10, - color: 'white', - }, -}); diff --git a/apps/wallet/src/features/Loyalty/ActionCard/CountDown.tsx b/apps/wallet/src/features/Loyalty/ActionCard/CountDown.tsx deleted file mode 100644 index 29e8275ad..000000000 --- a/apps/wallet/src/features/Loyalty/ActionCard/CountDown.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import type { FC } from 'react'; -import { useEffect, useState } from 'react'; -import { StyleSheet } from 'react-native'; -import { Text } from '@walless/gui'; - -import { formatCountdownTime } from '../internal'; - -interface Props { - initialTimeRemaining: number; -} - -const CountDown: FC = ({ initialTimeRemaining }) => { - const [timeRemaining, setTimeRemaining] = - useState(initialTimeRemaining); - - useEffect(() => { - const interval = setInterval(() => { - if (timeRemaining > 0) { - setTimeRemaining((prev) => prev - 1000); - } - }, 1000); - - return () => clearInterval(interval); - }, [initialTimeRemaining]); - - return ( - {formatCountdownTime(timeRemaining)} - ); -}; - -const styles = StyleSheet.create({ - counterText: { - fontSize: 11, - fontWeight: '500', - color: '#00B1FF', - }, -}); - -export default CountDown; diff --git a/apps/wallet/src/features/Loyalty/ActionCard/StreakBar.tsx b/apps/wallet/src/features/Loyalty/ActionCard/StreakBar.tsx deleted file mode 100644 index 012f26288..000000000 --- a/apps/wallet/src/features/Loyalty/ActionCard/StreakBar.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import type { FC } from 'react'; -import type { ViewStyle } from 'react-native'; -import { StyleSheet, View } from 'react-native'; -import { Text } from '@walless/gui'; - -interface Props { - style?: ViewStyle; - currentStreak: number; - streak: number; - isRecorded: boolean; -} - -const StreakBar: FC = ({ style, currentStreak, streak, isRecorded }) => { - return ( - - - {Array.from({ length: streak }, (_, i) => ( - - ))} - - - - {isRecorded ? 'Recorded streak' : ''} - {`${currentStreak}/${streak}`} - - - ); -}; - -const styles = StyleSheet.create({ - container: { - gap: 4, - }, - streakContainer: { - flexDirection: 'row', - gap: 4, - }, - bar: { - height: 3, - borderRadius: 4, - flex: 1, - backgroundColor: '#515151', - }, - activeBar: { - backgroundColor: '#00B1FF', - }, - textContainer: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - }, - text: { - fontSize: 10, - color: '#A4B3C1', - }, -}); - -export default StreakBar; diff --git a/apps/wallet/src/features/Loyalty/ActionCard/VerificationNeededTag.tsx b/apps/wallet/src/features/Loyalty/ActionCard/VerificationNeededTag.tsx deleted file mode 100644 index 7c585db19..000000000 --- a/apps/wallet/src/features/Loyalty/ActionCard/VerificationNeededTag.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import type { FC } from 'react'; -import type { ViewStyle } from 'react-native'; -import { StyleSheet, View } from 'react-native'; -import { Text } from '@walless/gui'; -import { Exclamation2 } from '@walless/icons'; - -import { sharedStyles } from '../internal'; - -interface Props { - style?: ViewStyle; -} - -const VerificationNeededTag: FC = ({ style }) => { - return ( - - - - - Verification Needed - - ); -}; - -export default VerificationNeededTag; - -const styles = StyleSheet.create({ - container: { - backgroundColor: 'rgba(255, 190, 102, 0.15)', - }, - verificationIcon: { - borderWidth: 1, - borderColor: 'rgba(255, 190, 102, 0.3)', - }, - text: { - fontSize: 10, - color: '#FFBE66', - }, -}); diff --git a/apps/wallet/src/features/Loyalty/ActionCard/index.tsx b/apps/wallet/src/features/Loyalty/ActionCard/index.tsx deleted file mode 100644 index 268348d3b..000000000 --- a/apps/wallet/src/features/Loyalty/ActionCard/index.tsx +++ /dev/null @@ -1,349 +0,0 @@ -import type { FC } from 'react'; -import { useMemo, useState } from 'react'; -import type { ViewStyle } from 'react-native'; -import { - ActivityIndicator, - Image, - Linking, - StyleSheet, - Text, - View, -} from 'react-native'; -import { logger } from '@walless/core'; -import type { - Action, - ActionCount, - ActionMetadata, - ActionRecord, - UserProgress, -} from '@walless/graphql'; -import { - ActionCategory, - mutations, - queries, - VerifyMechanism, -} from '@walless/graphql'; -import { Button } from '@walless/gui'; -import { showNotificationModal } from 'modals/Notification'; -import { loyaltyActions, loyaltyState } from 'state/loyalty'; -import { qlClient } from 'utils/graphql'; -import { useSnapshot } from 'utils/hooks'; - -import PointTag from '../components/PointTag'; -import { - checkIfUserCanDoAction, - extractDataFromMetadata, - getActionLogo, - getCycleEndTime, - navigateInternalByCta, - sharedStyles, -} from '../internal'; - -import CompletedTag from './CompletedTag'; -import CountDown from './CountDown'; -import StreakBar from './StreakBar'; -import VerificationNeededTag from './VerificationNeededTag'; - -interface Props { - style?: ViewStyle; - action: Action; -} - -const ActionCard: FC = ({ style, action }) => { - const { userProgress, typeActionMap } = useSnapshot(loyaltyState); - const { name, desc, icon, ctaText, ctaType, cta } = useMemo(() => { - return extractDataFromMetadata(action.metadata as ActionMetadata[]); - }, [action]); - - const [isDoingAction, setIsDoingAction] = useState(false); - - const canUserDoAction = checkIfUserCanDoAction( - userProgress as UserProgress, - action, - ); - - const initialTimeRemaining = useMemo(() => { - if ( - !userProgress || - canUserDoAction || - action.category !== ActionCategory.Recurring || - !action.cycleInHours - ) { - return null; - } - - const lastRecord = (userProgress.actionRecords as ActionRecord[]).findLast( - (record) => record.actionId === action.id, - ); - if (!lastRecord) { - return 0; - } - - const cycleEndTime = getCycleEndTime( - new Date(lastRecord.timestamp), - action.cycleInHours as number, - ); - - return cycleEndTime.getTime() - Date.now(); - }, [userProgress]); - - const isRecorded = useMemo(() => { - if (!userProgress?.actionRecords) return false; - - const relatedRecurringAction = typeActionMap - .get(action.type!) - ?.find((a) => a.category === ActionCategory.Recurring); - - if (relatedRecurringAction?.cycleInHours) { - const lastRecord = userProgress.actionRecords.findLast( - (record) => record!.actionId === relatedRecurringAction.id, - ); - if (!lastRecord) { - return false; - } - - const cycleEndTime = getCycleEndTime( - new Date(lastRecord.timestamp), - relatedRecurringAction.cycleInHours, - ); - - return new Date() < cycleEndTime; - } - - return false; - }, [action, userProgress, typeActionMap]); - - const handleDoAction = async () => { - if (ctaType === 'internal') { - navigateInternalByCta(cta); - return; - } - - if (ctaType === '' || action.mechanism === VerifyMechanism.No) { - setIsDoingAction(true); - - try { - if (action.category === ActionCategory.Onetime) { - await qlClient.request(mutations.doLoyaltyAction, { - actionId: action.id, - }); - } else if (action.category === ActionCategory.Recurring) { - await qlClient.request( - mutations.doRecurringThenStreakThenMilestoneActionsByType, - { - type: action.type, - }, - ); - } - - const { loyaltyUserProgress } = await qlClient.request<{ - loyaltyUserProgress: UserProgress; - }>(queries.loyaltyUserProgress); - - loyaltyActions.setUserProgress(loyaltyUserProgress); - } catch (error) { - logger.error(error); - - showNotificationModal({ - id: `action-error-${action.id}`, - message: `Failed to perform action ${name}`, - textStyle: { - color: 'red', - }, - timeout: 5000, - }); - } - - setIsDoingAction(false); - } - - if (ctaType === 'external') { - Linking.openURL(cta); - } - }; - - const stat = useMemo(() => { - if (!userProgress) { - return null; - } - - return (userProgress.trackList as ActionCount[]).find( - (track) => track.type === action.type, - ); - }, [userProgress]); - - const currentStreak = useMemo(() => { - if (!stat || !stat.streaks) { - return 0; - } - - const cycleEndTime = getCycleEndTime( - new Date(stat.lastClaim), - stat.cycleInHours as number, - ); - - cycleEndTime.setHours( - cycleEndTime.getHours() + (stat.cycleInHours as number), - ); - - if (new Date() > cycleEndTime) { - return 0; - } - - let currentStreak = 0; - for (const streak of stat.streaks) { - if (streak?.streak === action.streak) { - currentStreak = streak?.currentStreak ?? 0; - } - } - return currentStreak; - }, [stat, action.streak]); - - const isPassthrough = - !canUserDoAction && action.category !== ActionCategory.Streak; - - return ( - - - - - {icon ? ( - - ) : ( - getActionLogo(action) - )} - - - - {name} - {desc !== '' && ( - - {desc} - - )} - - - - - - - {!isPassthrough && action.mechanism === VerifyMechanism.Manual && ( - - )} - - {isPassthrough && } - - {initialTimeRemaining && ( - - - - )} - - {action.category === ActionCategory.Streak && action.streak && ( - - )} - - - - {canUserDoAction && - action.category !== ActionCategory.Milestone && - action.category !== ActionCategory.Streak && - (isDoingAction ? ( - - ) : ( -