Commit c2a83cab authored by Jordan Frankfurt's avatar Jordan Frankfurt Committed by GitHub

feat: add loading indicator to activity tab in mini portfolio (#6754)

* feat: add loading indicator to activity tab in mini portfolio

* should not render spinner when activity key is active

* add an unread notification dot that is dismissed when the user visits the activity tab

* pr feedback
parent 7a3c51bc
...@@ -2,12 +2,14 @@ import { Trans } from '@lingui/macro' ...@@ -2,12 +2,14 @@ import { Trans } from '@lingui/macro'
import { Trace, TraceEvent } from '@uniswap/analytics' import { Trace, TraceEvent } from '@uniswap/analytics'
import { BrowserEvent, InterfaceElementName, InterfaceSectionName, SharedEventName } from '@uniswap/analytics-events' import { BrowserEvent, InterfaceElementName, InterfaceSectionName, SharedEventName } from '@uniswap/analytics-events'
import Column from 'components/Column' import Column from 'components/Column'
import { LoaderV2 } from 'components/Icons/LoadingSpinner'
import { AutoRow } from 'components/Row' import { AutoRow } from 'components/Row'
import { useIsNftPage } from 'hooks/useIsNftPage' import { useIsNftPage } from 'hooks/useIsNftPage'
import { useAtomValue } from 'jotai/utils' import { useAtomValue } from 'jotai/utils'
import { useState } from 'react' import { useEffect, useState } from 'react'
import { shouldDisableNFTRoutesAtom } from 'state/application/atoms' import { shouldDisableNFTRoutesAtom } from 'state/application/atoms'
import styled from 'styled-components/macro' import { useHasPendingTransactions } from 'state/transactions/hooks'
import styled, { useTheme } from 'styled-components/macro'
import { ThemedText } from 'theme' import { ThemedText } from 'theme'
import { ActivityTab } from './Activity' import { ActivityTab } from './Activity'
...@@ -35,12 +37,15 @@ const Nav = styled(AutoRow)` ...@@ -35,12 +37,15 @@ const Nav = styled(AutoRow)`
` `
const NavItem = styled(ThemedText.SubHeader)<{ active?: boolean }>` const NavItem = styled(ThemedText.SubHeader)<{ active?: boolean }>`
align-items: center;
color: ${({ theme, active }) => (active ? theme.textPrimary : theme.textTertiary)}; color: ${({ theme, active }) => (active ? theme.textPrimary : theme.textTertiary)};
cursor: pointer;
display: flex;
justify-content: space-between;
transition: ${({ theme }) => `${theme.transition.duration.medium} ${theme.transition.timing.ease} color`}; transition: ${({ theme }) => `${theme.transition.duration.medium} ${theme.transition.timing.ease} color`};
&:hover { &:hover {
${({ theme, active }) => !active && `color: ${theme.textSecondary}`}; ${({ theme, active }) => !active && `color: ${theme.textSecondary}`};
cursor: pointer;
} }
` `
...@@ -88,16 +93,31 @@ const Pages: Array<Page> = [ ...@@ -88,16 +93,31 @@ const Pages: Array<Page> = [
export default function MiniPortfolio({ account }: { account: string }) { export default function MiniPortfolio({ account }: { account: string }) {
const isNftPage = useIsNftPage() const isNftPage = useIsNftPage()
const theme = useTheme()
const [currentPage, setCurrentPage] = useState(isNftPage ? 1 : 0) const [currentPage, setCurrentPage] = useState(isNftPage ? 1 : 0)
const [activityUnread, setActivityUnread] = useState(false)
const shouldDisableNFTRoutes = useAtomValue(shouldDisableNFTRoutesAtom) const shouldDisableNFTRoutes = useAtomValue(shouldDisableNFTRoutesAtom)
const Page = Pages[currentPage].component const { component: Page, key: currentKey } = Pages[currentPage]
const hasPendingTransactions = useHasPendingTransactions()
useEffect(() => {
if (hasPendingTransactions && currentKey !== 'activity') setActivityUnread(true)
}, [currentKey, hasPendingTransactions])
return ( return (
<Trace section={InterfaceSectionName.MINI_PORTFOLIO}> <Trace section={InterfaceSectionName.MINI_PORTFOLIO}>
<Wrapper> <Wrapper>
<Nav data-testid="mini-portfolio-navbar"> <Nav data-testid="mini-portfolio-navbar">
{Pages.map(({ title, loggingElementName, key }, index) => { {Pages.map(({ title, loggingElementName, key }, index) => {
if (shouldDisableNFTRoutes && loggingElementName.includes('nft')) return null if (shouldDisableNFTRoutes && loggingElementName.includes('nft')) return null
const isUnselectedActivity = key === 'activity' && currentKey !== 'activity'
const showActivityIndicator = isUnselectedActivity && (hasPendingTransactions || activityUnread)
const handleNavItemClick = () => {
setCurrentPage(index)
if (key === 'activity') setActivityUnread(false)
}
return ( return (
<TraceEvent <TraceEvent
events={[BrowserEvent.onClick]} events={[BrowserEvent.onClick]}
...@@ -105,8 +125,20 @@ export default function MiniPortfolio({ account }: { account: string }) { ...@@ -105,8 +125,20 @@ export default function MiniPortfolio({ account }: { account: string }) {
element={loggingElementName} element={loggingElementName}
key={index} key={index}
> >
<NavItem onClick={() => setCurrentPage(index)} active={currentPage === index} key={key}> <NavItem onClick={handleNavItemClick} active={currentPage === index} key={key}>
{title} <span>{title}</span>
{showActivityIndicator && (
<>
&nbsp;
{hasPendingTransactions ? (
<LoaderV2 />
) : (
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="4" cy="4" r="4" fill={theme.accentAction} />
</svg>
)}
</>
)}
</NavItem> </NavItem>
</TraceEvent> </TraceEvent>
) )
......
...@@ -125,3 +125,10 @@ export function useHasPendingApproval(token?: Token, spender?: string): boolean ...@@ -125,3 +125,10 @@ export function useHasPendingApproval(token?: Token, spender?: string): boolean
export function useHasPendingRevocation(token?: Token, spender?: string): boolean { export function useHasPendingRevocation(token?: Token, spender?: string): boolean {
return usePendingApprovalAmount(token, spender)?.eq(0) ?? false return usePendingApprovalAmount(token, spender)?.eq(0) ?? false
} }
export function useHasPendingTransactions() {
const allTransactions = useAllTransactions()
return useMemo(() => {
return Object.values(allTransactions).filter((tx) => !tx.receipt).length > 0
}, [allTransactions])
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment