From 40fd94a2d11ec78acfde7b428220ae7c1eb0fd35 Mon Sep 17 00:00:00 2001 From: Kolade Date: Wed, 10 Jul 2024 07:00:41 +0100 Subject: [PATCH] Update Rewards Dashboard flow (#1699) * update unlock profile * add modal back to activities * update label in unlock modal * update flow * New Discord Flow Added * fixes done * fixed the discord verification page alignment * update useWithAuthButton * add button onclick * add unlock profile listener * fix create flows --------- Co-authored-by: abhishek-01k Co-authored-by: rohitmalhotra1420 --- src/App.tsx | 8 +- src/common/Common.constants.ts | 1 + src/common/index.ts | 1 + .../chat/unlockProfile/UnlockProfile.tsx | 85 +++++++---- .../unlockProfile/UnlockProfileWrapper.tsx | 21 ++- src/config/AppPaths.ts | 3 +- src/contexts/AppContext.tsx | 5 +- src/helpers/w2w/index.ts | 8 +- .../dashboard/components/DashboardHeader.tsx | 8 +- .../DiscordVerification.tsx | 66 +++++++++ src/modules/discordVerification/index.ts | 1 + src/modules/rewards/Rewards.tsx | 59 +++++--- .../rewards/components/ActivityButton.tsx | 70 +++++---- .../components/ActivityStatusButton.tsx | 25 ++-- .../components/ActivityVerificationButton.tsx | 122 ++++++++++++++++ .../rewards/components/DashboardSection.tsx | 7 +- .../rewards/components/ReferralSection.tsx | 39 +++-- .../components/RewardsActivitiesList.tsx | 27 ++-- .../components/RewardsActivitiesListItem.tsx | 29 ++-- .../components/RewardsTabsContainer.tsx | 18 +-- .../rewards/hooks/useCreateRewardsUser.tsx | 78 ++++++++++ .../rewards/hooks/useDiscordSession.ts | 15 +- .../rewards/hooks/useGenerateUserId.tsx | 95 ------------ src/modules/rewards/hooks/useRewardsAuth.tsx | 131 +++++++++++++++++ .../rewards/hooks/useVerifyDiscord.tsx | 134 +++++++++++++++++ .../rewards/hooks/useVerifyTwitter.tsx | 137 ++++++++++++++++++ .../rewards/hooks/useWithAuthButton.tsx | 80 ++++++++++ src/pages/DiscordVerificationPage.tsx | 12 ++ src/primaries/Profile.tsx | 10 +- .../hooks/rewards/useGetUserRewardsDetails.ts | 5 +- src/queries/types/rewards.ts | 1 - src/structure/MasterInterfacePage.tsx | 7 +- src/types/context.ts | 2 + 33 files changed, 1014 insertions(+), 296 deletions(-) create mode 100644 src/common/Common.constants.ts create mode 100644 src/modules/discordVerification/DiscordVerification.tsx create mode 100644 src/modules/discordVerification/index.ts create mode 100644 src/modules/rewards/components/ActivityVerificationButton.tsx create mode 100644 src/modules/rewards/hooks/useCreateRewardsUser.tsx delete mode 100644 src/modules/rewards/hooks/useGenerateUserId.tsx create mode 100644 src/modules/rewards/hooks/useRewardsAuth.tsx create mode 100644 src/modules/rewards/hooks/useVerifyDiscord.tsx create mode 100644 src/modules/rewards/hooks/useVerifyTwitter.tsx create mode 100644 src/modules/rewards/hooks/useWithAuthButton.tsx create mode 100644 src/pages/DiscordVerificationPage.tsx diff --git a/src/App.tsx b/src/App.tsx index a66c95ad82..02bdba361a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -320,8 +320,12 @@ export default function App() { // const { spaceUI } = useSpaceComponents(); const location = useLocation(); - const isHeaderHidden = location?.pathname.includes(APP_PATHS.PointsVault); - const isSidebarHidden = location?.pathname.includes(APP_PATHS.PointsVault) || location?.pathname.includes('/snap'); + const isHeaderHidden = + location?.pathname.includes(APP_PATHS.PointsVault) || location?.pathname.includes(APP_PATHS.DiscordVerification); + const isSidebarHidden = + location?.pathname.includes(APP_PATHS.PointsVault) || + location?.pathname.includes('/snap') || + location?.pathname.includes(APP_PATHS.DiscordVerification); return ( diff --git a/src/common/Common.constants.ts b/src/common/Common.constants.ts new file mode 100644 index 0000000000..cf230b4d60 --- /dev/null +++ b/src/common/Common.constants.ts @@ -0,0 +1 @@ +export const GuestModeWalletAddress = 'eip155:0x0000000000000000000000000000000000000001'; diff --git a/src/common/index.ts b/src/common/index.ts index 9330290d59..2c2603cce5 100644 --- a/src/common/index.ts +++ b/src/common/index.ts @@ -1,2 +1,3 @@ export * from './hooks'; export * from './components'; +export * from './Common.constants'; diff --git a/src/components/chat/unlockProfile/UnlockProfile.tsx b/src/components/chat/unlockProfile/UnlockProfile.tsx index 1382c5ec07..ef8d3dd221 100644 --- a/src/components/chat/unlockProfile/UnlockProfile.tsx +++ b/src/components/chat/unlockProfile/UnlockProfile.tsx @@ -5,7 +5,15 @@ import { useContext, useEffect, useState } from 'react'; import styled, { useTheme } from 'styled-components'; // Internal Compoonents -import { ButtonV2, ImageV2, ItemHV2, ItemVV2, Skeleton, SkeletonLine, SpanV2 } from 'components/reusables/SharedStylingV2'; +import { + ButtonV2, + ImageV2, + ItemHV2, + ItemVV2, + Skeleton, + SkeletonLine, + SpanV2, +} from 'components/reusables/SharedStylingV2'; import { AppContext } from 'contexts/AppContext'; import { useAccount, useDeviceWidthCheck } from 'hooks'; import { retrieveUserPGPKeyFromStorage } from 'helpers/connectWalletHelper'; @@ -17,6 +25,7 @@ import { device, size } from 'config/Globals'; import Tooltip from 'components/reusables/tooltip/Tooltip'; import UnlockLogo from '../../../assets/chat/unlock.svg'; import Wallet from '../../../assets/chat/wallet.svg'; +import { Box, CrossFilled, HoverableSVG } from 'blocks'; // Constants export enum UNLOCK_PROFILE_TYPE { @@ -34,12 +43,14 @@ export enum PROFILESTATE { type UnlockProfileModalProps = { InnerComponentProps: { type: UNLOCK_PROFILE_TYPE | undefined; + description?: string; + closeIcon?: boolean; }; onClose?: () => void; }; const UnlockProfile = ({ InnerComponentProps, onClose }: UnlockProfileModalProps) => { - const { type } = InnerComponentProps; + const { type, description, closeIcon } = InnerComponentProps; const theme = useTheme(); const { handleConnectWallet, initializePushSDK } = useContext(AppContext); @@ -68,7 +79,7 @@ const UnlockProfile = ({ InnerComponentProps, onClose }: UnlockProfileModalProps setActiveStatus({ status: PROFILESTATE.UNLOCK_PROFILE, title: 'Unlock Profile', - body: 'Unlock your profile to read and send messages.', + body: description ? description : 'Unlock your profile to read and send messages.', }); } }, [wallet]); @@ -86,11 +97,30 @@ const UnlockProfile = ({ InnerComponentProps, onClose }: UnlockProfileModalProps initializePushSDK(wallet); } } - - }, [account]) + }, [account]); return ( + {closeIcon && ( + + + } + /> + + )} + {/* Logo and Left Text */} - {!isLoading ? ( <> - )} - @@ -188,7 +215,6 @@ const UnlockProfile = ({ InnerComponentProps, onClose }: UnlockProfileModalProps alignItems="baseline" flexDirection={type === UNLOCK_PROFILE_TYPE.MODAL || isMobile ? 'column' : 'row'} > - {!isLoading ? ( <> - )} @@ -252,7 +277,6 @@ const UnlockProfile = ({ InnerComponentProps, onClose }: UnlockProfileModalProps Remember Me - ) : ( )} - )} @@ -285,21 +308,21 @@ const RenderToolTip = ({ children, type }) => { placementProps={ type === UNLOCK_PROFILE_TYPE.MODAL ? { - background: 'black', - width: '220px', - padding: '8px 12px', - top: '10px', - left: '60px', - borderRadius: '4px 12px 12px 12px', - } + background: 'black', + width: '220px', + padding: '8px 12px', + top: '10px', + left: '60px', + borderRadius: '4px 12px 12px 12px', + } : { - background: 'black', - width: '120px', - padding: '8px 12px', - bottom: '0px', - right: '-30px', - borderRadius: '12px 12px 12px 4px', - } + background: 'black', + width: '120px', + padding: '8px 12px', + bottom: '0px', + right: '-30px', + borderRadius: '12px 12px 12px 4px', + } } tooltipContent={ void; + closeIcon?: boolean; } const DEFAULT_PROPS = { type: UNLOCK_PROFILE_TYPE.MODAL, }; -const UnlockProfileWrapper = ({ type = DEFAULT_PROPS.type, showConnectModal }: IntroContainerProps) => { +const UnlockProfileWrapper = ({ + type = DEFAULT_PROPS.type, + showConnectModal, + description, + onClose, + closeIcon, +}: IntroContainerProps) => { const { isModalOpen: isProfileModalOpen, showModal: showProfileModal, @@ -37,7 +46,10 @@ const UnlockProfileWrapper = ({ type = DEFAULT_PROPS.type, showConnectModal }: I InnerComponent={UnlockProfile} InnerComponentProps={{ type, + description, + closeIcon, }} + closeIcon={closeIcon} modalRadius="24px" modalBorder={false} modalPosition={MODAL_POSITION.ON_PARENT} @@ -46,7 +58,10 @@ const UnlockProfileWrapper = ({ type = DEFAULT_PROPS.type, showConnectModal }: I } else { return ( - + ); } diff --git a/src/config/AppPaths.ts b/src/config/AppPaths.ts index 4588a9d8b9..f65a85010c 100644 --- a/src/config/AppPaths.ts +++ b/src/config/AppPaths.ts @@ -7,6 +7,7 @@ enum APP_PATHS { Spaces = '/spaces', Channels = '/channels', Dashboard = '/dashboard', + DiscordVerification = '/discord/verification', Rewards = '/points', RewardsActivities = '/points/activity', RewardsLeaderboard = '/points/leaderboard', @@ -26,7 +27,7 @@ enum APP_PATHS { Support = '/support', UserSettings = '/user/settings', ChannelSettings = '/channel/settings', - ClaimGalxe = 'claim/galxe', + ClaimGalxe = 'claim/galxe' } export default APP_PATHS; diff --git a/src/contexts/AppContext.tsx b/src/contexts/AppContext.tsx index e38743eb4c..704f098cdb 100644 --- a/src/contexts/AppContext.tsx +++ b/src/contexts/AppContext.tsx @@ -60,6 +60,7 @@ const AppContextProvider = ({ children }) => { }); const [SnapState, setSnapState] = useState(1); + const [isUserProfileUnlocked, setUserProfileUnlocked] = useState(false); const { isModalOpen: isMetamaskPushSnapOpen, showModal: showMetamaskPushSnap, @@ -131,7 +132,7 @@ const AppContextProvider = ({ children }) => { // reset the ref to true shouldInitializeRef.current = true; // Directly modify the ref to disable useEffect execution - + setUserProfileUnlocked(true); return user; }; @@ -544,6 +545,8 @@ const AppContextProvider = ({ children }) => { initializePushSdkReadMode, removePGPKeyForUser, storePGPKeyForUser, + isUserProfileUnlocked, + setUserProfileUnlocked, }} > {children} diff --git a/src/helpers/w2w/index.ts b/src/helpers/w2w/index.ts index b78553d5d8..e67b936c0d 100644 --- a/src/helpers/w2w/index.ts +++ b/src/helpers/w2w/index.ts @@ -101,7 +101,7 @@ export const decryptFeeds = async ({ }); } catch (e) { // console.log(e); - if(e.message == decryptionErrorMsg){ + if (e.message == decryptionErrorMsg) { feed.msg.messageType = 'Text'; feed.msg.messageContent = 'message encrypted before you joined'; } @@ -137,7 +137,9 @@ export const decryptMessages = async ({ const member = getMemberDetails(currentChat, currentChat?.msg?.fromCAIP10); signatureValidationPubliKey = member ? member.publicKey : ''; } else { - const latestUserInfo = inbox.find((x) => x.wallets.split(':')[1]?.toLowerCase() === currentChat?.wallets?.split(':')[1]?.toLowerCase()); + const latestUserInfo = inbox.find( + (x) => x.wallets.split(':')[1]?.toLowerCase() === currentChat?.wallets?.split(':')[1]?.toLowerCase() + ); if (latestUserInfo) { signatureValidationPubliKey = latestUserInfo.publicKey!; @@ -157,7 +159,7 @@ export const decryptMessages = async ({ }); } catch (e) { // console.log(e); - if(e.message == decryptionErrorMsg){ + if (e.message == decryptionErrorMsg) { savedMsg.messageType = 'Text'; savedMsg.messageContent = 'message encrypted before you joined'; } diff --git a/src/modules/dashboard/components/DashboardHeader.tsx b/src/modules/dashboard/components/DashboardHeader.tsx index 3e2c5c1412..9b297ca9d9 100644 --- a/src/modules/dashboard/components/DashboardHeader.tsx +++ b/src/modules/dashboard/components/DashboardHeader.tsx @@ -9,11 +9,7 @@ interface DashboardHeaderProps { showSubHeader: boolean; } - -const DashboardHeader: FC = ({ - setSubHeaderVisibility, - showSubHeader -}) => { +const DashboardHeader: FC = ({ setSubHeaderVisibility, showSubHeader }) => { return ( = ({ ); }; -export default DashboardHeader; \ No newline at end of file +export default DashboardHeader; diff --git a/src/modules/discordVerification/DiscordVerification.tsx b/src/modules/discordVerification/DiscordVerification.tsx new file mode 100644 index 0000000000..dc3b103c3c --- /dev/null +++ b/src/modules/discordVerification/DiscordVerification.tsx @@ -0,0 +1,66 @@ +import { Box, Button, Discord, Text } from 'blocks'; +import { useDiscordSession } from 'modules/rewards/hooks/useDiscordSession'; +import { useGetUserDiscordDetails } from 'queries'; + +const DiscordVerification = () => { + const params = new URLSearchParams(location.hash.substring(1)); + const access_token = params.get('access_token'); + + useDiscordSession(); + + const token = localStorage.getItem('access_token'); + + const { data: userDiscordDetails } = useGetUserDiscordDetails(token as string); + + const handleContinueVerification = () => { + if (userDiscordDetails) { + localStorage.setItem('username', userDiscordDetails.username); + window.close(); + } + }; + + if (!access_token) { + window.close(); + } + + return ( + + + + + Complete Verification + + Continue to complete the verification process. + + + + + + + ); +}; + +export { DiscordVerification }; diff --git a/src/modules/discordVerification/index.ts b/src/modules/discordVerification/index.ts new file mode 100644 index 0000000000..f562812455 --- /dev/null +++ b/src/modules/discordVerification/index.ts @@ -0,0 +1 @@ +export { DiscordVerification } from './DiscordVerification'; diff --git a/src/modules/rewards/Rewards.tsx b/src/modules/rewards/Rewards.tsx index 4183a77f0f..39a0090fbc 100644 --- a/src/modules/rewards/Rewards.tsx +++ b/src/modules/rewards/Rewards.tsx @@ -1,56 +1,72 @@ // React and other libraries -import { FC, useEffect } from 'react'; +import { FC, useEffect, useMemo, useState } from 'react'; // third party libraries -import { css } from 'styled-components'; import { useSelector } from 'react-redux'; +import { css } from 'styled-components'; +import { useSearchParams } from 'react-router-dom'; //Hooks -import { useAccount } from 'hooks'; import { useRewardsTabs } from './hooks/useRewardsTabs'; -import { useGenerateUserId } from './hooks/useGenerateUserId'; import { useDiscordSession } from './hooks/useDiscordSession'; +import { useRewardsAuth } from './hooks/useRewardsAuth'; +import { useCreateRewardsUser } from './hooks/useCreateRewardsUser'; //Types import { UserStoreType } from 'types'; -import { UNLOCK_PROFILE_TYPE } from 'components/chat/unlockProfile/UnlockProfile'; - -//helpers -import { walletToCAIP10 } from 'helpers/w2w'; //Components import { Box, Text } from 'blocks'; import { ReferralSection } from './components/ReferralSection'; import { RewardsTabsContainer } from './components/RewardsTabsContainer'; -import UnlockProfileWrapper from 'components/chat/unlockProfile/UnlockProfileWrapper'; +import UnlockProfileWrapper, { UNLOCK_PROFILE_TYPE } from 'components/chat/unlockProfile/UnlockProfileWrapper'; export type RewardsProps = {}; const Rewards: FC = () => { const { userPushSDKInstance } = useSelector((state: UserStoreType) => state.user); - const { account } = useAccount(); + const [hasError, setHasError] = useState<{} | null>(null); + + //fetch ref from url + const [searchParams] = useSearchParams(); - const caip10WalletAddress = walletToCAIP10({ account }); + const ref = searchParams.get('ref'); + if (ref) sessionStorage.setItem('ref', ref); // Used to set the discord session after discord redirects back to the Dapp. useDiscordSession(); + const userMessage = 'Error decrypting PGP private key ...swiching to Guest mode'; + + // reject unlock profile listener + const errorExists = useMemo( + () => userPushSDKInstance?.errors.some((error) => error.type === 'ERROR' && error.message === userMessage), + [userPushSDKInstance?.errors] + ); + + const isErrorPresent = userPushSDKInstance?.errors; + const { activeTab, handleSetActiveTab } = useRewardsTabs(); - const { showConnectModal, setConnectModalVisibility } = useGenerateUserId(caip10WalletAddress); + const { showConnectModal, setShowConnectModal, status, connectUserWallet } = useRewardsAuth(); - useEffect(() => { - if (activeTab !== 'activity') { - setConnectModalVisibility(false); - } + useCreateRewardsUser(); - if (activeTab === 'activity' && userPushSDKInstance && userPushSDKInstance.readmode()) { - setConnectModalVisibility(true); + const heading = activeTab === 'leaderboard' ? 'Push Reward Points' : 'Introducing Push Reward Points Program'; + + useEffect(() => { + if (isErrorPresent && showConnectModal && status === 'error' && errorExists && activeTab === 'dashboard') { + setHasError(isErrorPresent); + setShowConnectModal(false); } - }, [activeTab, account, userPushSDKInstance]); + }, [isErrorPresent, showConnectModal, errorExists]); - const heading = activeTab === 'leaderboard' ? 'Push Reward Points' : 'Introducing Push Reward Points Program'; + // retry unlock profile + const handleUnlockProfile = () => { + setHasError(null); + connectUserWallet(); + }; return ( = () => { handleSetActiveTab={handleSetActiveTab} /> - {activeTab === 'dashboard' && } + {activeTab === 'dashboard' && } {userPushSDKInstance && userPushSDKInstance?.readmode() && showConnectModal && ( @@ -99,6 +115,7 @@ const Rewards: FC = () => { )} diff --git a/src/modules/rewards/components/ActivityButton.tsx b/src/modules/rewards/components/ActivityButton.tsx index 3946a86874..135b73aa33 100644 --- a/src/modules/rewards/components/ActivityButton.tsx +++ b/src/modules/rewards/components/ActivityButton.tsx @@ -1,14 +1,10 @@ // React and other libraries import { FC } from 'react'; -// Component -import { DiscordActivityButton } from './DiscordActivityButton'; -import { TwitterActivityButton } from './TwitterActivityButton'; -import { DefaultActivityButton } from './DefaultActivityButton'; -import { ActivityStatusButton } from './ActivityStatusButton'; - //Queries import { ActvityType, UsersActivity } from 'queries'; +import { Button } from 'blocks'; +import { ActivityVerificationButton } from './ActivityVerificationButton'; type ActivityButtonProps = { userId: string; @@ -16,7 +12,7 @@ type ActivityButtonProps = { activityType: ActvityType; refetchActivity: () => void; setErrorMessage: (errorMessage: string) => void; - usersSingleActivity: UsersActivity; + usersSingleActivity?: UsersActivity; }; const ActivityButton: FC = ({ @@ -25,38 +21,40 @@ const ActivityButton: FC = ({ refetchActivity, activityType, setErrorMessage, - usersSingleActivity + usersSingleActivity, }) => { + if (usersSingleActivity?.status === 'COMPLETED') { + return ( + + ); + } - switch (usersSingleActivity.status) { - case 'COMPLETED': - return ; - case 'PENDING': - return ; - default: - switch (activityType) { - case 'follow_push_on_discord': - return - case 'follow_push_on_twitter': - return - default: - return - } + if (usersSingleActivity?.status === 'PENDING') { + return ( + + ); } + + return ( + // Verify button + + ); }; export { ActivityButton }; diff --git a/src/modules/rewards/components/ActivityStatusButton.tsx b/src/modules/rewards/components/ActivityStatusButton.tsx index cf01a25264..742777522e 100644 --- a/src/modules/rewards/components/ActivityStatusButton.tsx +++ b/src/modules/rewards/components/ActivityStatusButton.tsx @@ -1,25 +1,30 @@ // React and other libraries import { FC } from 'react'; +// type +import { ButtonSize, ButtonVariant } from 'blocks/button'; + //Components import { Box, Button, Skeleton } from 'blocks'; - type ActivityStatusButtonProps = { label: string; disabledLabel?: string; isLoading?: boolean; onClick?: () => void; disabled: boolean; - -} + variant?: ButtonVariant; + size?: ButtonSize; +}; const ActivityStatusButton: FC = ({ label, isLoading, onClick, disabled, - disabledLabel + disabledLabel, + variant, + size, }) => { return ( = ({ alignItems={{ ml: 'flex-start', initial: 'center' }} flexDirection="column" > - + + ); + } + + return authButton; +}; diff --git a/src/modules/rewards/components/DashboardSection.tsx b/src/modules/rewards/components/DashboardSection.tsx index 11a391e3e5..204bf8cc2b 100644 --- a/src/modules/rewards/components/DashboardSection.tsx +++ b/src/modules/rewards/components/DashboardSection.tsx @@ -18,17 +18,16 @@ export type DashboardSectionProps = { }; const DashboardSection: FC = ({ onGetStarted }) => { - const { isWalletConnected, account } = useAccount(); + const { account } = useAccount(); const caip10WalletAddress = walletToCAIP10({ account }); const { data: userDetails, - isSuccess, refetch, isLoading: isUserLoading, isFetching, - } = useGetUserRewardsDetails({ caip10WalletAddress: caip10WalletAddress, enabled: isWalletConnected }); + } = useGetUserRewardsDetails({ caip10WalletAddress: caip10WalletAddress }); - const isLoading = isUserLoading || !isSuccess; + const isLoading = isUserLoading; return ( <> diff --git a/src/modules/rewards/components/ReferralSection.tsx b/src/modules/rewards/components/ReferralSection.tsx index e76593b46e..4897c6d2e2 100644 --- a/src/modules/rewards/components/ReferralSection.tsx +++ b/src/modules/rewards/components/ReferralSection.tsx @@ -7,6 +7,7 @@ import { css } from 'styled-components'; //hooks import { useAccount, useCopy } from 'hooks'; import { useGetUserRewardsDetails } from 'queries'; +import { useRewardsAuth } from '../hooks/useRewardsAuth'; //helpers import { walletToCAIP10 } from 'helpers/w2w'; @@ -15,25 +16,24 @@ import { getPreviewBasePath } from '../../../../basePath'; // components import { Box, Button, Copy, Text, Referral, Skeleton } from 'blocks'; -export type ReferralSectionProps = {}; +export type ReferralSectionProps = { + handleUnlockProfile: () => void; +}; -const ReferralSection: FC = () => { +const ReferralSection: FC = ({ handleUnlockProfile }) => { const previewBasePath = getPreviewBasePath() || ''; const baseUrl = window.location.origin + previewBasePath; const { isWalletConnected, account, connect } = useAccount(); const caip10WalletAddress = walletToCAIP10({ account }); - const { - data: userDetails, - isSuccess, - isLoading: isUserLoading, - } = useGetUserRewardsDetails({ + const { data: userDetails, isLoading: isUserLoading } = useGetUserRewardsDetails({ caip10WalletAddress: caip10WalletAddress, - enabled: isWalletConnected, }); - const isLoading = isUserLoading || !isSuccess; + const { status } = useRewardsAuth(); + + const isLoading = isUserLoading; const { textRef, isCopied, copyToClipboard } = useCopy(); @@ -78,8 +78,8 @@ const ReferralSection: FC = () => { - {isWalletConnected && ( - + + {isWalletConnected && userDetails && ( = () => { {isCopied ? 'Copied' : 'Copy Link'} - - )} + )} + + + + {isWalletConnected && status == 'error' && ( + + + + )} + {!isWalletConnected && ( diff --git a/src/modules/rewards/components/RewardsActivitiesList.tsx b/src/modules/rewards/components/RewardsActivitiesList.tsx index 89514db2da..eb4dd89c7e 100644 --- a/src/modules/rewards/components/RewardsActivitiesList.tsx +++ b/src/modules/rewards/components/RewardsActivitiesList.tsx @@ -1,21 +1,13 @@ -// React and other libraries import { FC } from 'react'; import InfiniteScroll from 'react-infinite-scroller'; -//Hooks -import { Activity } from 'queries'; +import { Box } from 'blocks'; import { useAccount } from 'hooks'; -import { useGetRewardsActivities, useGetUserRewardsDetails } from 'queries/hooks/rewards'; - -//Components -import { RewardsActivitiesListItem } from './RewardsActivitiesListItem'; -import LoaderSpinner, { LOADER_TYPE } from 'components/reusables/loaders/LoaderSpinner'; - -//Helpers import { walletToCAIP10 } from 'helpers/w2w'; +import { Activity, useGetRewardsActivities, useGetUserRewardsDetails } from 'queries'; -//Components -import { Box } from 'blocks'; +import LoaderSpinner, { LOADER_TYPE } from 'components/reusables/loaders/LoaderSpinner'; +import { RewardsActivitiesListItem } from './RewardsActivitiesListItem'; export type RewardActivitiesProps = {}; @@ -27,19 +19,22 @@ const RewardsActivitiesList: FC = () => { isLoading: isLoadingActivities, fetchNextPage, hasNextPage, - isFetchingNextPage + isFetchingNextPage, } = useGetRewardsActivities({ pageSize: 5 }); // Getting user Id by wallet address const caip10WalletAddress = walletToCAIP10({ account }); - const { data: userDetails, isLoading: isLoadingUserDetails } = useGetUserRewardsDetails({ + const { data: userDetails } = useGetUserRewardsDetails({ caip10WalletAddress: caip10WalletAddress, }); - const isLoading = isLoadingUserDetails || isLoadingActivities; + const isLoading = isLoadingActivities; + // const isLoading = isLoadingUserDetails || isLoadingActivities; // If there are activities then render them else render 5 skeletons - const activityList = isLoading ? Array(3).fill(0) : rewardActivitiesResponse?.pages.flatMap((page) => page.activities) || []; + const activityList = isLoading + ? Array(3).fill(0) + : rewardActivitiesResponse?.pages.flatMap((page) => page.activities) || []; const hasMoreData = !isFetchingNextPage && hasNextPage; diff --git a/src/modules/rewards/components/RewardsActivitiesListItem.tsx b/src/modules/rewards/components/RewardsActivitiesListItem.tsx index 296d140817..b237119c4d 100644 --- a/src/modules/rewards/components/RewardsActivitiesListItem.tsx +++ b/src/modules/rewards/components/RewardsActivitiesListItem.tsx @@ -1,16 +1,11 @@ -// React and other libraries import { FC, useState } from 'react'; -//Hooks import { Activity, useGetRewardsActivity } from 'queries'; -//Components import { Box, ErrorFilled, InfoFilled, Lozenge, RewardsBell, Skeleton, Text } from 'blocks'; import { ActivityButton } from './ActivityButton'; import { RewardsActivityIcon } from './RewardsActivityIcon'; import { RewardsActivityTitle } from './RewardsActivityTitle'; -import { useSelector } from 'react-redux'; -import { UserStoreType } from 'types'; export type RewardActivitiesListItemProps = { userId: string; @@ -31,8 +26,6 @@ const RewardsActivitiesListItem: FC = ({ userId, refetch: refetchActivity, } = useGetRewardsActivity({ userId, activityId: activity.id }, { enabled: !!userId }); - const { userPushSDKInstance } = useSelector((state: UserStoreType) => state.user); - const [errorMessage, setErrorMessage] = useState(''); return ( @@ -139,18 +132,16 @@ const RewardsActivitiesListItem: FC = ({ userId, {/* Buttons Logic */} - {usersSingleActivity && userPushSDKInstance && !userPushSDKInstance?.readmode() && ( - - - - )} + + + diff --git a/src/modules/rewards/components/RewardsTabsContainer.tsx b/src/modules/rewards/components/RewardsTabsContainer.tsx index 608ecc2bd7..ce0308712e 100644 --- a/src/modules/rewards/components/RewardsTabsContainer.tsx +++ b/src/modules/rewards/components/RewardsTabsContainer.tsx @@ -1,4 +1,4 @@ -import { FC, useEffect, useState } from 'react'; +import { FC } from 'react'; //Components import { Box } from 'blocks'; @@ -6,27 +6,14 @@ import { RewardsTabs } from './RewardsTabs'; import { DashboardSection } from './DashboardSection'; import { LeaderBoardSection } from './LeaderBoardSection'; import { RewardsActivitiesSection } from './RewardsActivitiesSection'; - -//Types import { RewardsTabs as RewardsTabsType } from '../Rewards.types'; -import { useSelector } from 'react-redux'; -import { UserStoreType } from 'types'; -import UnlockProfileWrapper from 'components/chat/unlockProfile/UnlockProfileWrapper'; -import { UNLOCK_PROFILE_TYPE } from 'components/chat/unlockProfile/UnlockProfile'; -import { css } from 'styled-components'; export type RewardsTabsContainerProps = { activeTab: RewardsTabsType; handleSetActiveTab: (tab: RewardsTabsType) => void; }; -const RewardsTabsContainer: FC = ({ - activeTab, - handleSetActiveTab, -}) => { - - - +const RewardsTabsContainer: FC = ({ activeTab, handleSetActiveTab }) => { return ( = ({ {activeTab === 'dashboard' && handleSetActiveTab('activity')} />} {activeTab === 'activity' && } {activeTab === 'leaderboard' && } - ); diff --git a/src/modules/rewards/hooks/useCreateRewardsUser.tsx b/src/modules/rewards/hooks/useCreateRewardsUser.tsx new file mode 100644 index 0000000000..1e644dce94 --- /dev/null +++ b/src/modules/rewards/hooks/useCreateRewardsUser.tsx @@ -0,0 +1,78 @@ +// React and other libraries +import { useContext, useEffect, useState } from 'react'; + +// third party libraries +import { useSelector } from 'react-redux'; + +// helpers +import { useAccount } from 'hooks'; +import { generateVerificationProof } from '../utils/generateVerificationProof'; +import { walletToCAIP10 } from 'helpers/w2w'; +import { AppContext } from 'contexts/AppContext'; + +// hooks +import { useCreateRewardsUser as useCreateRewardsUserQuery, useGetUserRewardsDetails } from 'queries/hooks'; + +// types +import { UserStoreType } from 'types'; +import { AppContextType } from 'types/context'; + +const useCreateRewardsUser = () => { + const { account } = useAccount(); + + const caip10WalletAddress = walletToCAIP10({ account }); + + const [isSuccess, setIsSuccess] = useState(false); + + // @ts-expect-error + const { isUserProfileUnlocked } = useContext(AppContext); + + const { userPushSDKInstance } = useSelector((state: UserStoreType) => state.user); + + const { status, refetch } = useGetUserRewardsDetails({ + caip10WalletAddress: caip10WalletAddress, + }); + + const { mutate: createUser } = useCreateRewardsUserQuery(); + + const handleCreateUser = async () => { + // get ref, send with user wallet. if ref is null, send only user wallet + const ref = sessionStorage.getItem('ref'); + const data = { + ...(ref && { refPrimary: ref }), + userWallet: caip10WalletAddress, + }; + + //generate verification proof again, after unlocking profile + const verificationProof = await generateVerificationProof(data, userPushSDKInstance); + if (!verificationProof) return; + + createUser( + { + pgpPublicKey: userPushSDKInstance?.pgpPublicKey, + userWallet: caip10WalletAddress, + verificationProof: verificationProof as string, + refPrimary: ref, + }, + { + onSuccess: () => { + refetch(); + setIsSuccess(true); + }, + onError: (err) => { + console.error('Error', err); + }, + } + ); + }; + + useEffect(() => { + if (isUserProfileUnlocked && userPushSDKInstance && status !== 'success') { + handleCreateUser(); + } + }, [isUserProfileUnlocked, userPushSDKInstance, status]); + + return { handleCreateUser, isSuccess, isUserProfileUnlocked }; +}; + +export { useCreateRewardsUser }; diff --git a/src/modules/rewards/hooks/useDiscordSession.ts b/src/modules/rewards/hooks/useDiscordSession.ts index b5b1121cba..4335bf864a 100644 --- a/src/modules/rewards/hooks/useDiscordSession.ts +++ b/src/modules/rewards/hooks/useDiscordSession.ts @@ -1,11 +1,9 @@ import { useEffect } from 'react'; -import { useLocation, useNavigate } from 'react-router-dom'; +import { useLocation } from 'react-router-dom'; export const useDiscordSession = () => { const location = useLocation(); - const navigate = useNavigate(); - useEffect(() => { if (location.hash) { const params = new URLSearchParams(location.hash.substring(1)); @@ -13,16 +11,9 @@ export const useDiscordSession = () => { const expiresIn = params.get('expires_in'); if (token && expiresIn) { - sessionStorage.setItem('access_token', token); - sessionStorage.setItem('expires_in', expiresIn); + localStorage.setItem('access_token', token); + localStorage.setItem('expires_in', expiresIn); } - - // Splitting the url into baseURL+pathname and hashes of the url - const baseUrl = window.location.href.split('#')[0]; - // Generating a new URL - const url = new URL(baseUrl); - //navigating the user to the new URL without reloading the page - navigate(url, { replace: true }); } }, []); }; diff --git a/src/modules/rewards/hooks/useGenerateUserId.tsx b/src/modules/rewards/hooks/useGenerateUserId.tsx deleted file mode 100644 index 345ab950fb..0000000000 --- a/src/modules/rewards/hooks/useGenerateUserId.tsx +++ /dev/null @@ -1,95 +0,0 @@ -// React and other libraries -import { useEffect, useState } from 'react'; - -// third party libraries -import { useSelector } from 'react-redux'; -import { useSearchParams } from 'react-router-dom'; - -//Hooks -import { useGetUserRewardsDetails, useCreateRewardsUser } from 'queries'; - -//Types -import { UserStoreType } from 'types'; -import { AxiosError } from 'axios'; - -//helpers -import { generateVerificationProof } from '../utils/generateVerificationProof'; - -const useGenerateUserId = (caip10WalletAddress: string) => { - const [searchParams] = useSearchParams(); - - const ref = searchParams.get('ref'); - const [showConnectModal, setConnectModalVisibility] = useState(false); - - const { userPushSDKInstance } = useSelector((state: UserStoreType) => state.user); - - const { - data: userDetails, - status, - refetch, - error, - } = useGetUserRewardsDetails({ - caip10WalletAddress: caip10WalletAddress, - }); - - const { mutate: createUser } = useCreateRewardsUser(); - - const errorMessage = 'Failed to retrieve user'; - - useEffect(() => { - if (status === 'error' && error instanceof AxiosError && error?.response?.data?.error === errorMessage) { - // generate userId - generateUserId(ref); - } - // bad request error - }, [userDetails, status]); - - const generateUserId = async (ref: string | null) => { - // userPushSDKInstance null check - if (!userPushSDKInstance) return; - - // if ref is present, add it to the data needed to generate verification proof, if not - send only user wallet - const data = { - ...(ref && { refPrimary: ref }), - userWallet: caip10WalletAddress, - }; - - // generate verification proof - const verificationProof = await generateVerificationProof(data, userPushSDKInstance); - - //if verification proof is null, unlock push profile to get the decrypted pgp pvt key needed for the verification proof - if (verificationProof == null || verificationProof == undefined) { - if (userPushSDKInstance && userPushSDKInstance.readmode()) { - setConnectModalVisibility(true); - } - return; - } - - console.log(verificationProof, ref, errorMessage); - - // mutate action to make the request and on success - call the get user by wallet address fn with the id again - createUser( - { - pgpPublicKey: userPushSDKInstance?.pgpPublicKey, - userWallet: caip10WalletAddress, - verificationProof: verificationProof as string, - refPrimary: ref, - }, - { - onSuccess: () => { - refetch(); - }, - onError: (err) => { - console.error('Error', err); - }, - } - ); - }; - - return { - showConnectModal, - setConnectModalVisibility, - }; -}; - -export { useGenerateUserId }; diff --git a/src/modules/rewards/hooks/useRewardsAuth.tsx b/src/modules/rewards/hooks/useRewardsAuth.tsx new file mode 100644 index 0000000000..50fb21ef80 --- /dev/null +++ b/src/modules/rewards/hooks/useRewardsAuth.tsx @@ -0,0 +1,131 @@ +// react and other libraries +import { useEffect, useState } from 'react'; + +// third party libraries +import { useSelector } from 'react-redux'; + +// helpers +import { walletToCAIP10 } from 'helpers/w2w'; +import { useAccount } from 'hooks'; +import { generateVerificationProof } from '../utils/generateVerificationProof'; +import { useRewardsTabs } from './useRewardsTabs'; + +// hooks +import { useGetUserRewardsDetails } from 'queries/hooks/rewards'; + +// types +import { AxiosError } from 'axios'; +import { UserStoreType } from 'types'; + +const useRewardsAuth = () => { + const { account, isWalletConnected, connect } = useAccount(); + const caip10WalletAddress = walletToCAIP10({ account }); + const { userPushSDKInstance } = useSelector((state: UserStoreType) => state.user); + + const [showConnectModal, setShowConnectModal] = useState(false); + + const [isVerifyClicked, setIsVerifyClicked] = useState(false); + const [isDashClicked, setIsDashClicked] = useState(false); + const [handleVerify, setHandleVerify] = useState(false); + const { activeTab } = useRewardsTabs(); + + const { + data: userDetails, + status, + error, + } = useGetUserRewardsDetails({ + caip10WalletAddress: caip10WalletAddress, + }); + + // error responses + const errorMessage = 'Failed to retrieve user'; + + // rewards activity connect function + const connectWallet = () => { + setHandleVerify(false); + setIsVerifyClicked(true); + + if (!isWalletConnected) { + connect(); + } + }; + + // dashboard referral section unlock profile + const connectUserWallet = () => { + setIsDashClicked(true); + }; + + // unlock profile function + const unlockProfile = async () => { + // get ref, send with user wallet. if ref is null, send only user wallet + const ref = sessionStorage.getItem('ref'); + const data = { + ...(ref && { refPrimary: ref }), + userWallet: caip10WalletAddress, + }; + + // generate verification proof + const verificationProof = await generateVerificationProof(data, userPushSDKInstance); + + //if verification proof is null, unlock push profile update to update userPUSHSDKInstance + if (verificationProof === null || verificationProof === undefined) { + if (userPushSDKInstance && userPushSDKInstance.readmode()) { + setShowConnectModal(true); + } + } + + // if verify is clicked as an existing user, or verificationProof is present, handle verification + if ((isVerifyClicked && userDetails) || verificationProof) { + setHandleVerify(true); + } + setIsVerifyClicked(false); + setIsDashClicked(false); + }; + + useEffect(() => { + // dashboard connect wallet flow + if (status === 'error' && activeTab == 'dashboard' && !isVerifyClicked && !!userPushSDKInstance?.errors) { + if (error instanceof AxiosError && error?.response?.data?.error === errorMessage) { + unlockProfile(); + } + } + + // user disconnects while modal is open + if (status === 'pending' && !isWalletConnected) { + setShowConnectModal(false); + } + + // rewards activity first user + if (isVerifyClicked && status === 'error') { + if (error instanceof AxiosError && error?.response?.data?.error === errorMessage) { + unlockProfile(); + } + } + + // rewards activity existing user + if (isVerifyClicked && userDetails && !handleVerify) { + unlockProfile(); + } + + // referral section click + if (isDashClicked && status == 'error') { + unlockProfile(); + } + }, [status, isVerifyClicked, isDashClicked]); + + return { + caip10WalletAddress, + status, + unlockProfile, + showConnectModal, + setShowConnectModal, + connectWallet, + handleVerify, + userDetails, + isVerifyClicked, + connectUserWallet, + isDashClicked, + }; +}; + +export { useRewardsAuth }; diff --git a/src/modules/rewards/hooks/useVerifyDiscord.tsx b/src/modules/rewards/hooks/useVerifyDiscord.tsx new file mode 100644 index 0000000000..f51dcfaf03 --- /dev/null +++ b/src/modules/rewards/hooks/useVerifyDiscord.tsx @@ -0,0 +1,134 @@ +// React and other libraries +import { useEffect, useState } from 'react'; + +// Third-party libraries +import { PushAPI } from '@pushprotocol/restapi'; +import { useSelector } from 'react-redux'; + +import { appConfig } from 'config'; +import { useClaimRewardsActivity } from 'queries'; + +// helpers +import { generateVerificationProof } from '../utils/generateVerificationProof'; + +// Types +import { UserStoreType } from 'types'; + +//Config +import APP_PATHS from 'config/AppPaths'; + +type UseDiscordActivityVerificationProps = { + activityTypeId: string; + refetchActivity: () => void; + setErrorMessage: (errorMessage: string) => void; +}; + +const useVerifyDiscord = ({ + activityTypeId, + refetchActivity, + setErrorMessage, +}: UseDiscordActivityVerificationProps) => { + const token = localStorage.getItem('access_token'); + + const { userPushSDKInstance } = useSelector((state: UserStoreType) => state.user); + + const [activityStatus, setActivityStatus] = useState(null); + const [verifying, setVerifying] = useState(token ? true : false); + const [updatedId, setUpdatedId] = useState(null); + + useEffect(() => { + setErrorMessage(''); + }, [setErrorMessage]); + + const { mutate: claimRewardsActivity } = useClaimRewardsActivity({ + userId: updatedId as string, + activityTypeId, + }); + + const handleDiscordVerification = (userId: string) => { + setUpdatedId(userId); + setErrorMessage(''); + handleConnect(userId); + }; + + const handleConnect = (userId: string) => { + const clientID = appConfig.discord_client_id; + const baseURL = window.location.origin; + const redirectURI = `${baseURL}${APP_PATHS.DiscordVerification}`; + const scope = 'identify email guilds.members.read'; + + const authURL = `https://discord.com/api/oauth2/authorize?client_id=${clientID}&redirect_uri=${redirectURI}&response_type=token&scope=${scope}`; + + const newWindow = window.open(authURL, '_blank'); + + const checkAuth = setInterval(() => { + if (newWindow?.closed) { + clearInterval(checkAuth); + handleVerify(userPushSDKInstance, userId); + } + }, 1000); + }; + + const handleVerify = async (userPushSDKInstance: PushAPI, userId: string) => { + const token = localStorage.getItem('access_token'); + const username = localStorage.getItem('username'); + + if (username && token) { + setVerifying(true); + const data = { + discord: username, + discord_token: token, + }; + + const verificationProof = await generateVerificationProof(data, userPushSDKInstance); + + if (verificationProof == null || verificationProof == undefined) { + if (userPushSDKInstance && userPushSDKInstance.readmode()) { + setVerifying(false); + setErrorMessage('Please Enable Push profile'); + } + return; + } + + localStorage.removeItem('access_token'); + localStorage.removeItem('username'); + localStorage.removeItem('expires_in'); + + claimRewardsActivity( + { + userId: updatedId || (userId as string), + activityTypeId, + pgpPublicKey: userPushSDKInstance.pgpPublicKey as string, + data: data, + verificationProof: verificationProof as string, + }, + { + onSuccess: (response) => { + if (response.status === 'COMPLETED') { + setActivityStatus('Claimed'); + refetchActivity(); + setVerifying(false); + setErrorMessage(''); + // localStorage.removeItem('discordVerificationTriggered'); + } + }, + onError: (error: any) => { + console.log('Error in creating activity', error); + setVerifying(false); + if (error.name) { + setErrorMessage(error.response.data.error); + } + }, + } + ); + } + }; + + return { + activityStatus, + verifying, + handleDiscordVerification, + }; +}; + +export { useVerifyDiscord }; diff --git a/src/modules/rewards/hooks/useVerifyTwitter.tsx b/src/modules/rewards/hooks/useVerifyTwitter.tsx new file mode 100644 index 0000000000..99b7cabf2d --- /dev/null +++ b/src/modules/rewards/hooks/useVerifyTwitter.tsx @@ -0,0 +1,137 @@ +// react and other libraries +import { useEffect, useState } from 'react'; + +// third party libraries +import { initializeApp } from 'firebase/app'; +import { getAuth, signInWithPopup, TwitterAuthProvider, User } from 'firebase/auth'; +import { useSelector } from 'react-redux'; + +// helpers +import { appConfig } from 'config'; +import { generateVerificationProof } from '../utils/generateVerificationProof'; +import { useClaimRewardsActivity } from 'queries'; + +// types +import { UserStoreType } from 'types'; + +export type UseTwitterVerifyParams = { + activityTypeId: string; + setErrorMessage: (errorMessage: string) => void; + refetchActivity: () => void; +}; + +const useVerifyTwitter = ({ activityTypeId, setErrorMessage, refetchActivity }: UseTwitterVerifyParams) => { + const [verifying, setVerifying] = useState(false); + const [activityStatus, setActivityStatus] = useState(null); + const { userPushSDKInstance } = useSelector((state: UserStoreType) => state.user); + const [updatedId, setUpdatedId] = useState(null); + + useEffect(() => { + setErrorMessage(''); + }, [setErrorMessage]); + + initializeApp(appConfig.firebaseConfig); + + const provider = new TwitterAuthProvider(); + const auth = getAuth(); + + const handleTwitterVerification = (userId: string) => { + setUpdatedId(userId); + handleVerify(userId); + }; + + const handleConnect = (): Promise => { + return signInWithPopup(auth, provider) + .then((result) => { + const credential = TwitterAuthProvider.credentialFromResult(result); + if (credential) { + const user = result.user; + return user; + } else { + return null; + } + }) + .catch((error) => { + const errorCode = error.code; + const errorMessage = error.message; + setErrorMessage(errorMessage); + const credential = TwitterAuthProvider.credentialFromError(error); + console.log('Error in connecting twitter >>>', errorCode, errorMessage, credential); + setVerifying(false); + return null; + }); + }; + + const { mutate: claimRewardsActivity } = useClaimRewardsActivity({ + userId: updatedId as string, + activityTypeId, + }); + + const handleVerify = async (userId: string | null) => { + setErrorMessage(''); + setVerifying(true); + + const userTwitterDetails = await handleConnect(); + + if (userTwitterDetails) { + // @ts-expect-error + const twitterHandle = userTwitterDetails.reloadUserInfo.screenName; + + const verificationProof = await generateVerificationProof( + { + twitter: twitterHandle, + }, + userPushSDKInstance + ); + + if (verificationProof == null || verificationProof == undefined) { + if (userPushSDKInstance && userPushSDKInstance.readmode()) { + setVerifying(false); + setErrorMessage('Please Enable Push profile'); + } + return; + } + + claimRewardsActivity( + { + userId: updatedId || (userId as string), + activityTypeId, + pgpPublicKey: userPushSDKInstance.pgpPublicKey as string, + data: { + twitter: twitterHandle, + }, + verificationProof: verificationProof as string, + }, + { + onSuccess: (response) => { + if (response.status === 'COMPLETED') { + setActivityStatus('Claimed'); + refetchActivity(); + setVerifying(false); + } + if (response.status === 'PENDING') { + setActivityStatus('Pending'); + refetchActivity(); + setVerifying(false); + } + }, + onError: (error: any) => { + console.log('Error in creating activity', error); + setVerifying(false); + if (error.name) { + setErrorMessage(error.response.data.error); + } + }, + } + ); + } + }; + + return { + verifying, + activityStatus, + handleTwitterVerification, + }; +}; + +export { useVerifyTwitter }; diff --git a/src/modules/rewards/hooks/useWithAuthButton.tsx b/src/modules/rewards/hooks/useWithAuthButton.tsx new file mode 100644 index 0000000000..a3d78084d7 --- /dev/null +++ b/src/modules/rewards/hooks/useWithAuthButton.tsx @@ -0,0 +1,80 @@ +// React and other libraries +import { useEffect, useMemo, useState } from 'react'; + +// third party libraries +import { useSelector } from 'react-redux'; + +//Hooks +import { useAccount } from 'hooks'; +import { useRewardsAuth } from './useRewardsAuth'; +import { useCreateRewardsUser } from './useCreateRewardsUser'; +import { UserRewardsDetailResponse } from 'queries'; + +// types +import { UserStoreType } from 'types'; + +// components +import { Button } from 'blocks'; + +export const useAuthWithButton = ({ onSuccess }: { onSuccess: (userDetails: UserRewardsDetailResponse) => void }) => { + const [isWalletConnectedAndProfileUnlocked, setIsWalletConnectedAndProfileUnlocked] = useState(false); + const [showAuth, setShowAuth] = useState(false); // Track button click + + const { isWalletConnected } = useAccount(); + const { userPushSDKInstance } = useSelector((state: UserStoreType) => state.user); + + const { showConnectModal, setShowConnectModal, connectWallet, handleVerify, userDetails } = useRewardsAuth(); + const { isSuccess, isUserProfileUnlocked } = useCreateRewardsUser(); + + const showAuthModal = async () => { + setShowAuth(true); + connectWallet(); + }; + + const isAuthenticated = useMemo(() => { + return ( + showAuth && + (isSuccess || + (userDetails && + isUserProfileUnlocked && + handleVerify && + userPushSDKInstance && + !userPushSDKInstance.readmode())) + ); + }, [showAuth, isSuccess, userDetails, isUserProfileUnlocked, handleVerify, userPushSDKInstance]); + + const handleSuccess = (userDetails: UserRewardsDetailResponse) => { + setIsWalletConnectedAndProfileUnlocked(true); + onSuccess(userDetails); + setShowAuth(false); + }; + + useEffect(() => { + if (isAuthenticated && userDetails) { + handleSuccess(userDetails); + console.log('handle Success'); + } + }, [isAuthenticated, userDetails]); + + const authButton = useMemo( + () => ( + <> + + + ), + [isWalletConnected, showConnectModal] + ); + + return { + authButton, + isAuthenticated: isWalletConnectedAndProfileUnlocked, + showConnectModal: showConnectModal, + setShowConnectModal: setShowConnectModal, + }; +}; diff --git a/src/pages/DiscordVerificationPage.tsx b/src/pages/DiscordVerificationPage.tsx new file mode 100644 index 0000000000..6734c0dacc --- /dev/null +++ b/src/pages/DiscordVerificationPage.tsx @@ -0,0 +1,12 @@ +import { ContentLayout } from 'common'; +import { DiscordVerification } from 'modules/discordVerification'; + +const DiscordVerificationPage = () => { + return ( + + + + ); +}; + +export default DiscordVerificationPage; diff --git a/src/primaries/Profile.tsx b/src/primaries/Profile.tsx index 4f2224c523..674eacb08e 100644 --- a/src/primaries/Profile.tsx +++ b/src/primaries/Profile.tsx @@ -25,7 +25,8 @@ import { getPublicAssetPath } from 'helpers/RoutesHelper.js'; // Create Header const Profile = ({ isDarkMode }: { isDarkMode: boolean }) => { - const { web3NameList, removePGPKeyForUser, initializePushSdkReadMode }: AppContextType = useContext(AppContext); + const { web3NameList, removePGPKeyForUser, initializePushSdkReadMode, setUserProfileUnlocked } = + useContext(AppContext); const { setReadOnlyWallet, setMode }: GlobalContextType = useContext(GlobalContext); const { authError } = useContext(ErrorContext); const toggleArrowRef = useRef(null); @@ -51,21 +52,21 @@ const Profile = ({ isDarkMode }: { isDarkMode: boolean }) => { id: 'walletAddress', value: account, title: account, - function: () => { }, + function: () => {}, invertedIcon: getPublicAssetPath('copy.svg'), }, { id: 'userSettings', value: '', title: 'Settings', - function: () => { }, + function: () => {}, to: APP_PATHS.UserSettings, invertedIcon: getPublicAssetPath('svg/setting.svg'), }, { id: 'prodDapp', value: '', - function: () => { }, + function: () => {}, link: `https://${envUtil.prod}`, title: 'Production dapp', invertedIcon: getPublicAssetPath('prod.svg'), @@ -74,6 +75,7 @@ const Profile = ({ isDarkMode }: { isDarkMode: boolean }) => { id: 'disconnect', value: '', function: async () => { + setUserProfileUnlocked(false); removePGPKeyForUser(userPushSDKInstance.account); await disconnect(wallet); setMode(ReadOnlyWalletMode.GUEST_MODE); diff --git a/src/queries/hooks/rewards/useGetUserRewardsDetails.ts b/src/queries/hooks/rewards/useGetUserRewardsDetails.ts index af6827df7c..b92fdb29be 100644 --- a/src/queries/hooks/rewards/useGetUserRewardsDetails.ts +++ b/src/queries/hooks/rewards/useGetUserRewardsDetails.ts @@ -1,5 +1,5 @@ import { useQuery } from '@tanstack/react-query'; - +import { GuestModeWalletAddress } from 'common'; import { UserRewardsDetailParams } from '../../types'; import { userRewardsDetails } from '../../queryKeys'; import { getUserRewardsDetail } from '../../services'; @@ -8,5 +8,6 @@ export const useGetUserRewardsDetails = (options: UserRewardsDetailParams) => useQuery({ queryKey: [userRewardsDetails, options.caip10WalletAddress], queryFn: () => getUserRewardsDetail(options), - enabled: options.enabled, + retry: false, + enabled: !(options.caip10WalletAddress === GuestModeWalletAddress || !options.caip10WalletAddress), }); diff --git a/src/queries/types/rewards.ts b/src/queries/types/rewards.ts index fccbb6cf08..824235d5d1 100644 --- a/src/queries/types/rewards.ts +++ b/src/queries/types/rewards.ts @@ -84,7 +84,6 @@ export type DiscordDetails = { export type UserRewardsDetailParams = { caip10WalletAddress: string; - enabled?: boolean; }; export type UserRewardsDetailResponse = { diff --git a/src/structure/MasterInterfacePage.tsx b/src/structure/MasterInterfacePage.tsx index d30db9404a..e28e739d65 100644 --- a/src/structure/MasterInterfacePage.tsx +++ b/src/structure/MasterInterfacePage.tsx @@ -42,7 +42,7 @@ const ClaimGalxePage = lazy(() => import('pages/ClaimGalxePage')); const WelcomDashboardPage = lazy(() => import('pages/WelcomeDashboardPage')); const RewardPointsPage = lazy(() => import('pages/RewardPointsPage')); const PointsVaultPage = lazy(() => import('pages/PointsVaultPage')); - +const DiscordVerificationPage = lazy(() => import('pages/DiscordVerificationPage')); // import AirdropPage from 'pages/AirdropPage'; // import ChannelDashboardPage from 'pages/ChannelDashboardPage'; // import ChannelsPage from 'pages/ChannelsPage'; @@ -171,6 +171,11 @@ function MasterInterfacePage() { /> ))} + } + /> + } diff --git a/src/types/context.ts b/src/types/context.ts index 19a0a1ee4d..326ea12bef 100644 --- a/src/types/context.ts +++ b/src/types/context.ts @@ -61,4 +61,6 @@ export interface AppContextType { setDisplayQR: (displayQR: boolean) => void; createUserIfNecessary: () => Promise; initializePushSdkReadMode: () => Promise; + isUserProfileUnlocked: boolean; + setUserProfileUnlocked: (isUserProfileUnlocked: boolean) => void; }