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

Open UserHub on pending button click #2867

Merged
merged 2 commits into from
Aug 8, 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
30 changes: 16 additions & 14 deletions src/components/common/Extensions/UserHub/UserHub.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,17 @@ import CryptoToFiatTab from './partials/CryptoToFiatTab/CryptoToFiatTab.tsx';
import ReputationTab from './partials/ReputationTab/index.ts';
import StakesTab from './partials/StakesTab/index.ts';
import TransactionsTab from './partials/TransactionsTab/index.ts';
import { type UserHubProps, UserHubTabs } from './types.ts';
import { UserHubTab } from './types.ts';

// @BETA: Disabled for now
// import { COLONY_HOME_ROUTE } from '~routes';
// @BETA: Disabled for now
// import ButtonLink from '~v5/shared/Button/ButtonLink';

interface Props {
initialOpenTab?: UserHubTab;
}

const displayName = 'common.Extensions.UserHub.partials.UserHub';

const MSG = defineMessages({
Expand All @@ -33,12 +37,10 @@ const MSG = defineMessages({
},
});

const UserHub: FC<UserHubProps> = ({
defaultOpenedTab = UserHubTabs.Balance,
}) => {
const UserHub: FC<Props> = ({ initialOpenTab = UserHubTab.Balance }) => {
const isMobile = useMobile();
const featureFlags = useContext(FeatureFlagsContext);
const [selectedTab, setSelectedTab] = useState(defaultOpenedTab);
const [selectedTab, setSelectedTab] = useState(initialOpenTab);

const filteredTabList = tabList.filter(
(tabItem) =>
Expand All @@ -47,7 +49,7 @@ const UserHub: FC<UserHubProps> = ({
featureFlags[tabItem.featureFlag]?.isEnabled),
);

const handleTabChange = (newTab: UserHubTabs) => {
const handleTabChange = (newTab: UserHubTab) => {
setSelectedTab(newTab);
};

Expand All @@ -62,9 +64,9 @@ const UserHub: FC<UserHubProps> = ({
<div
className={clsx('flex h-full flex-col sm:w-[42.625rem] sm:flex-row', {
'sm:h-[27.75rem]':
selectedTab !== UserHubTabs.Balance &&
selectedTab !== UserHubTabs.CryptoToFiat,
'sm:min-h-[27.75rem]': selectedTab === UserHubTabs.Balance,
selectedTab !== UserHubTab.Balance &&
selectedTab !== UserHubTab.CryptoToFiat,
'sm:min-h-[27.75rem]': selectedTab === UserHubTab.Balance,
})}
>
<div className="sticky left-0 right-0 top-0 flex shrink-0 flex-col justify-between border-b border-b-gray-200 bg-base-white px-6 pb-6 pt-4 sm:static sm:left-auto sm:right-auto sm:top-auto sm:w-[13.85rem] sm:border-b-0 sm:border-r sm:border-gray-100 sm:bg-transparent sm:p-6 sm:px-6">
Expand All @@ -73,7 +75,7 @@ const UserHub: FC<UserHubProps> = ({
options={filteredTabList}
defaultValue={selectedTab}
value={selectedTab}
onChange={(value) => handleTabChange(value?.value as UserHubTabs)}
onChange={(value) => handleTabChange(value?.value as UserHubTab)}
className="w-full"
hideSelectedOptions
/>
Expand Down Expand Up @@ -124,14 +126,14 @@ const UserHub: FC<UserHubProps> = ({
)}
</div>
<div className="relative h-full w-full min-w-0">
{selectedTab === UserHubTabs.Balance && (
{selectedTab === UserHubTab.Balance && (
<ReputationTab onTabChange={handleTabChange} />
)}
{selectedTab === UserHubTabs.Stakes && <StakesTab />}
{selectedTab === UserHubTabs.Transactions && (
{selectedTab === UserHubTab.Stakes && <StakesTab />}
{selectedTab === UserHubTab.Transactions && (
<TransactionsTab appearance={{ interactive: true }} />
)}
{selectedTab === UserHubTabs.CryptoToFiat && <CryptoToFiatTab />}
{selectedTab === UserHubTab.CryptoToFiat && <CryptoToFiatTab />}
</div>
{/* @BETA: Disabled for now */}
{/* {isMobile && ( */}
Expand Down
18 changes: 9 additions & 9 deletions src/components/common/Extensions/UserHub/consts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { defineMessages } from 'react-intl';
import { FeatureFlag } from '~context/FeatureFlagsContext/types.ts';
import { formatText } from '~utils/intl.ts';

import { type UserHubTabList, UserHubTabs } from './types.ts';
import { type UserHubTabList, UserHubTab } from './types.ts';

export const menuMessages = defineMessages({
balance: {
Expand All @@ -32,27 +32,27 @@ export const menuMessages = defineMessages({

export const tabList: UserHubTabList = [
{
id: UserHubTabs.Balance,
id: UserHubTab.Balance,
label: formatText(menuMessages.balance),
value: UserHubTabs.Balance,
value: UserHubTab.Balance,
icon: Invoice,
},
{
id: UserHubTabs.Stakes,
id: UserHubTab.Stakes,
label: formatText(menuMessages.stakes),
value: UserHubTabs.Stakes,
value: UserHubTab.Stakes,
icon: CoinVertical,
},
{
id: UserHubTabs.Transactions,
id: UserHubTab.Transactions,
label: formatText(menuMessages.transactions),
value: UserHubTabs.Transactions,
value: UserHubTab.Transactions,
icon: Receipt,
},
{
id: UserHubTabs.CryptoToFiat,
id: UserHubTab.CryptoToFiat,
label: formatText(menuMessages.cryptoToFiat),
value: UserHubTabs.CryptoToFiat,
value: UserHubTab.CryptoToFiat,
icon: CreditCard,
featureFlag: FeatureFlag.CRYPTO_TO_FIAT_WITHDRAWALS,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
import React, { type FC, useMemo } from 'react';
import { defineMessages, useIntl } from 'react-intl';

import { UserHubTabs } from '~common/Extensions/UserHub/types.ts';
import { UserHubTab } from '~common/Extensions/UserHub/types.ts';
import { useColonyContext } from '~context/ColonyContext/ColonyContext.ts';
import { useTokensModalContext } from '~context/TokensModalContext/TokensModalContext.ts';
import { useGetUserTokenBalanceQuery } from '~gql';
Expand Down Expand Up @@ -170,7 +170,7 @@ const Balance: FC<BalanceProps> = ({ nativeToken, wallet, onTabChange }) => {
</span>
{!isMobile && (
<ViewStakedButton
onClick={() => onTabChange(UserHubTabs.Stakes)}
onClick={() => onTabChange(UserHubTab.Stakes)}
/>
)}
</div>
Expand All @@ -185,7 +185,7 @@ const Balance: FC<BalanceProps> = ({ nativeToken, wallet, onTabChange }) => {
{isMobile && (
<div className="mt-3">
<ViewStakedButton
onClick={() => onTabChange(UserHubTabs.Stakes)}
onClick={() => onTabChange(UserHubTab.Stakes)}
isFullSize
/>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { type Token } from '~types/graphql.ts';
import { type ColonyWallet } from '~types/wallet.ts';

import { type UserHubTabs } from '../../types.ts';
import { type UserHubTab } from '../../types.ts';

export interface ReputationTabProps {
onTabChange: (newTab: UserHubTabs) => void;
onTabChange: (newTab: UserHubTab) => void;
}

export interface BalanceProps extends Pick<ReputationTabProps, 'onTabChange'> {
Expand Down
10 changes: 3 additions & 7 deletions src/components/common/Extensions/UserHub/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,16 @@ import { type Icon } from '@phosphor-icons/react';
import { type FeatureFlag } from '~context/FeatureFlagsContext/types.ts';

export type UserHubTabList = {
id: UserHubTabs;
id: UserHubTab;
label: string;
value: UserHubTabs;
value: UserHubTab;
icon: Icon;
featureFlag?: FeatureFlag;
}[];

export enum UserHubTabs {
export enum UserHubTab {
Balance = 0,
Stakes = 1,
Transactions = 2,
CryptoToFiat = 3,
}

export interface UserHubProps {
defaultOpenedTab?: UserHubTabs;
}
98 changes: 52 additions & 46 deletions src/components/common/Extensions/UserHubButton/UserHubButton.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import clsx from 'clsx';
import React, { type FC, useState, useEffect } from 'react';
import React, { type FC, useState, useEffect, useCallback } from 'react';
import { usePopperTooltip } from 'react-popper-tooltip';
import { useSearchParams } from 'react-router-dom';

Expand All @@ -12,7 +12,6 @@ import { useColonyContext } from '~context/ColonyContext/ColonyContext.ts';
import { useTokensModalContext } from '~context/TokensModalContext/TokensModalContext.ts';
import { TransactionStatus } from '~gql';
import { useMobile } from '~hooks/index.ts';
import useDetectClickOutside from '~hooks/useDetectClickOutside.ts';
import useDisableBodyScroll from '~hooks/useDisableBodyScroll/index.ts';
import usePrevious from '~hooks/usePrevious.ts';
import { TX_SEARCH_PARAM } from '~routes';
Expand All @@ -26,19 +25,23 @@ import Button from '~v5/shared/Button/index.ts';
import PopoverBase from '~v5/shared/PopoverBase/index.ts';
import UserAvatar from '~v5/shared/UserAvatar/index.ts';

import { UserHubTabs } from '../UserHub/types.ts';
import { UserHubTab } from '../UserHub/types.ts';

import { OPEN_USER_HUB_EVENT } from './consts.ts';

interface Props {
openTab?: UserHubTab;
onOpen: () => void;
}

const displayName = 'common.Extensions.UserNavigation.partials.UserHubButton';

const UserHubButton: FC = () => {
const UserHubButton: FC<Props> = ({ openTab, onOpen }) => {
const isMobile = useMobile();
const {
colony: { colonyAddress },
} = useColonyContext();
const { wallet, user } = useAppContext();
const [isUserHubOpen, setIsUserHubOpen] = useState(false);
const { transactions } = useGroupedTransactions();
const [prevGroupStatus, setPrevGroupStatus] = useState<
TransactionStatus | undefined
Expand All @@ -52,21 +55,14 @@ const UserHubButton: FC = () => {

const { setOpenItemIndex, mobileMenuToggle } = useNavigationSidebarContext();
const { isTokensModalOpen } = useTokensModalContext();
// Note that this will change once we have a better modal transaction experience
const prevIsTokensModalOpen = usePrevious(isTokensModalOpen);

const [, { toggleOff }] = mobileMenuToggle;

const popperTooltipOffset = isMobile ? [0, 1] : [0, 8];

const ref = useDetectClickOutside({
onTriggered: (e) => {
// This stops the hub closing when clicking the pending button (which is outside)
if (!(e.target as HTMLElement)?.getAttribute('data-openhubifclicked')) {
setIsUserHubOpen(false);
} else {
setIsUserHubOpen(true);
}
},
});
chmanie marked this conversation as resolved.
Show resolved Hide resolved
const [initialOpenTab, setInitialOpenTab] = useState<UserHubTab>();

const { getTooltipProps, setTooltipRef, setTriggerRef, triggerRef, visible } =
usePopperTooltip(
Expand All @@ -76,7 +72,6 @@ const UserHubButton: FC = () => {
placement: isMobile ? 'bottom' : 'bottom-end',
trigger: 'click',
interactive: true,
onVisibleChange: () => {},
closeOnOutsideClick: true,
},
{
Expand All @@ -91,35 +86,50 @@ const UserHubButton: FC = () => {
},
);

// If visible is not true, then clicking buttons within the popover will close it
// So if isUserHubOpen is true, trigger a triggerRef click to ensure visible is true
useEffect(() => {
if (isUserHubOpen && !visible) {
// This is how we should open the userhub now, from this file. It keeps the state in popper
// Important! The user hub can't be closed from outside programmatically!
const openUserHub = useCallback(
(tab?: UserHubTab) => {
if (!visible) {
setInitialOpenTab(tab);
triggerRef?.click();
}
},
[triggerRef, visible],
);

const closeUserHub = useCallback(() => {
if (visible) {
triggerRef?.click();
}
}, [isUserHubOpen, visible, triggerRef]);
}, [visible, triggerRef]);

useEffect(() => {
if (transactionId !== previousTransactionId && (visible || isUserHubOpen)) {
triggerRef?.click();
setIsUserHubOpen(false);
// openTab signals that we want to open the UserHub from outside with a specific tab
// This immediately gets reset (onOpen) once the UserHub is open to prevent recursion
if (openTab) {
openUserHub(openTab);
onOpen();
}
}, [
transactionId,
triggerRef,
previousTransactionId,
visible,
isUserHubOpen,
]);
// once it's visible, we clear out the initial open tab again to open it with the default tab the next time
if (!openTab && visible) {
setInitialOpenTab(undefined);
}
}, [setInitialOpenTab, onOpen, openUserHub, openTab, triggerRef, visible]);

useEffect(() => {
if (isTokensModalOpen) {
triggerRef?.click();
setIsUserHubOpen(false);
if (transactionId !== previousTransactionId) {
closeUserHub();
}
}, [transactionId, previousTransactionId, closeUserHub]);

useEffect(() => {
if (isTokensModalOpen && isTokensModalOpen !== prevIsTokensModalOpen) {
closeUserHub();
}
}, [isTokensModalOpen, triggerRef]);
}, [isTokensModalOpen, prevIsTokensModalOpen, closeUserHub]);

useDisableBodyScroll(visible && isMobile);
useDisableBodyScroll(isMobile && visible);

const handleButtonClick = () => {
trackEvent(OPEN_USER_HUB_EVENT);
Expand All @@ -137,27 +147,27 @@ const UserHubButton: FC = () => {
(prevGroupStatus === TransactionStatus.Pending ||
prevGroupStatus === TransactionStatus.Ready)
) {
setIsUserHubOpen(true);
openUserHub(UserHubTab.Transactions);
}
if (groupStatus !== prevGroupStatus) {
setPrevGroupStatus(groupStatus);
}
}, [transactions, prevGroupStatus]);
}, [transactions, prevGroupStatus, openUserHub]);

const userName =
user?.profile?.displayName ??
splitWalletAddress(walletAddress ?? ADDRESS_ZERO);

return (
<div ref={ref} className="flex-shrink-0">
<div className="flex-shrink-0">
<Button
mode="tertiary"
size="large"
isFullRounded
ref={setTriggerRef}
className={clsx(
{
'!border-blue-400': visible || isUserHubOpen,
'!border-blue-400': visible,
},
'min-w-[3rem] md:hover:!border-blue-400',
)}
Expand Down Expand Up @@ -192,7 +202,7 @@ const UserHubButton: FC = () => {
) : null}
</div>
</Button>
{(visible || isUserHubOpen) && (
{visible && (
<PopoverBase
setTooltipRef={setTooltipRef}
tooltipProps={getTooltipProps}
Expand All @@ -204,11 +214,7 @@ const UserHubButton: FC = () => {
},
)}
>
<UserHub
defaultOpenedTab={
isUserHubOpen ? UserHubTabs.Transactions : undefined
}
/>
<UserHub initialOpenTab={initialOpenTab} />
</PopoverBase>
)}
</div>
Expand Down
Loading
Loading