diff --git a/src/app/(mobile-ui)/history/page.tsx b/src/app/(mobile-ui)/history/page.tsx index f0f6437a..f62ce02c 100644 --- a/src/app/(mobile-ui)/history/page.tsx +++ b/src/app/(mobile-ui)/history/page.tsx @@ -9,14 +9,16 @@ import NavHeader from '@/components/Global/NavHeader' import { PEANUT_API_URL } from '@/constants' import { useWallet } from '@/hooks/wallet/useWallet' import { IDashboardItem } from '@/interfaces' -import { formatAmountWithSignificantDigits, formatDate, printableAddress } from '@/utils' +import { formatAmountWithSignificantDigits, formatDate, getHeaderTitle, printableAddress } from '@/utils' import { useInfiniteQuery } from '@tanstack/react-query' import Link from 'next/link' +import { usePathname } from 'next/navigation' import { useEffect, useRef, useState } from 'react' const ITEMS_PER_PAGE = 10 const HistoryPage = () => { + const pathname = usePathname() const { address } = useWallet() const { composeLinkDataArray, fetchLinkDetailsAsync, removeRequestLinkFromLocalStorage } = useDashboard() const [dashboardData, setDashboardData] = useState([]) @@ -105,15 +107,33 @@ const HistoryPage = () => { return
Error loading history
} + if (!data?.pages.length) { + return ( +
+ + + + } + /> +
+ ) + } + return (
- {!!data?.pages.length ? : null} + {!!data?.pages.length ? : null}
- {!!data?.pages.length ? ( + {!!data?.pages.length && data?.pages.map((page, pageIndex) => ( -
+
{page.items.map((item) => ( -
+
{
))}
- )) - ) : ( -
- - - - } - /> -
- )} + ))}
{isFetchingNextPage &&
Loading more...
} diff --git a/src/app/(mobile-ui)/home/page.tsx b/src/app/(mobile-ui)/home/page.tsx index 8fda8841..83ee10a5 100644 --- a/src/app/(mobile-ui)/home/page.tsx +++ b/src/app/(mobile-ui)/home/page.tsx @@ -126,8 +126,8 @@ export default function Home() { } return ( -
-
+
+
diff --git a/src/app/(mobile-ui)/layout.tsx b/src/app/(mobile-ui)/layout.tsx index c3bfd327..be37250e 100644 --- a/src/app/(mobile-ui)/layout.tsx +++ b/src/app/(mobile-ui)/layout.tsx @@ -3,6 +3,7 @@ import { Button } from '@/components/0_Bruddle' import { useToast } from '@/components/0_Bruddle/Toast' import Modal from '@/components/Global/Modal' +import TopNavbar from '@/components/Global/TopNavbar' import WalletNavigation from '@/components/Global/WalletNavigation' import HomeWaitlist from '@/components/Home/HomeWaitlist' import { ThemeProvider } from '@/config' @@ -15,6 +16,7 @@ import classNames from 'classnames' import Link from 'next/link' import { usePathname } from 'next/navigation' import { useEffect, useMemo, useState } from 'react' +import { twMerge } from 'tailwind-merge' import '../../styles/globals.css' const publicPathRegex = /^\/(request\/pay|claim)/ @@ -24,7 +26,7 @@ const Layout = ({ children }: { children: React.ReactNode }) => { const [isReady, setIsReady] = useState(false) const { signInModal, selectExternalWallet } = useWallet() const web3Modal = useAppKit() - const { user } = useAuth() + const { user, isFetchingUser } = useAuth() const { handleLogin, isLoggingIn } = useZeroDev() const toast = useToast() @@ -33,6 +35,11 @@ const Layout = ({ children }: { children: React.ReactNode }) => { }, []) const isHome = pathName === '/home' + const isHistory = pathName === '/history' + const isWallet = pathName === '/wallet' + const isSupport = pathName === '/support' + const alignStart = isHome || isHistory || isWallet || isSupport + const showFullPeanutWallet = useMemo(() => { const isPublicPath = publicPathRegex.test(pathName) return isPublicPath || (user?.user.hasPwAccess ?? false) || !peanutWalletIsInPreview @@ -40,15 +47,63 @@ const Layout = ({ children }: { children: React.ReactNode }) => { if (!isReady) return null return ( -
-
- {showFullPeanutWallet ? children : } +
+ {/* Wrapper div for desktop layout */} +
+ {/* Sidebar - Fixed on desktop */} + {showFullPeanutWallet && ( +
+
+ +
+
+ )} + + {/* Main content area */} +
+ {/* Fixed top navbar */} + {showFullPeanutWallet && ( +
+ +
+ )} + + {/* Scrollable content area */} +
+ + {showFullPeanutWallet ? ( +
+ {children} +
+ ) : ( + + )} +
+
+ + {/* Mobile navigation */} + {showFullPeanutWallet && ( +
+ +
+ )} +
- {showFullPeanutWallet && } + + {/* Modal */} { @@ -80,7 +135,6 @@ const Layout = ({ children }: { children: React.ReactNode }) => {
+
+ ) +} + +export default TopNavbar diff --git a/src/components/Global/WalletNavigation/index.tsx b/src/components/Global/WalletNavigation/index.tsx index 4f15aff0..c53ba809 100644 --- a/src/components/Global/WalletNavigation/index.tsx +++ b/src/components/Global/WalletNavigation/index.tsx @@ -66,7 +66,7 @@ type MobileNavProps = { } const MobileNav: React.FC = ({ tabs, pathName }) => ( -
+
{tabs.map(({ name, href, icon }) => ( { const { push } = useRouter() const { username, isFetchingUser, user } = useAuth() - const { handleLogin, isLoggingIn } = useZeroDev() useEffect(() => { if (!isFetchingUser && !username) { diff --git a/src/constants/general.consts.ts b/src/constants/general.consts.ts index 55a7a76e..45166ec8 100644 --- a/src/constants/general.consts.ts +++ b/src/constants/general.consts.ts @@ -1,5 +1,5 @@ -import { CHAIN_DETAILS, TOKEN_DETAILS } from '@squirrel-labs/peanut-sdk' import * as interfaces from '@/interfaces' +import { CHAIN_DETAILS, TOKEN_DETAILS } from '@squirrel-labs/peanut-sdk' import { arbitrum, arbitrumSepolia } from 'viem/chains' export const peanutWalletIsInPreview = true @@ -184,3 +184,15 @@ export const nativeCurrencyAddresses: string[] = [ '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', '0x0000000000000000000000000000000000000000', ] + +export const pathTitles: { [key: string]: string } = { + '/home': 'Dashboard', + '/send': 'Send', + '/wallet': 'Wallet asset', + '/request/create': 'Request Payment', + '/request/pay': 'Pay Request', + '/cashout': 'Cashout', + '/history': 'History', + '/support': 'Support', + '/claim': 'Claim Payment', +} diff --git a/src/context/authContext.tsx b/src/context/authContext.tsx index b1037221..a466b68d 100644 --- a/src/context/authContext.tsx +++ b/src/context/authContext.tsx @@ -6,7 +6,8 @@ import { hitUserMetric } from '@/utils/metrics.utils' import { ToastId, useToast } from '@chakra-ui/react' import { useAppKit } from '@reown/appkit/react' import { useQuery } from '@tanstack/react-query' -import { createContext, ReactNode, useContext, useRef } from 'react' +import { useRouter } from 'next/navigation' +import { createContext, ReactNode, useContext, useRef, useState } from 'react' interface AuthContextType { user: interfaces.IUserProfile | null @@ -33,6 +34,7 @@ interface AuthContextType { }) => Promise isFetchingUser: boolean logoutUser: () => Promise + isLoggingOut: boolean } const AuthContext = createContext(undefined) @@ -42,6 +44,7 @@ const AuthContext = createContext(undefined) * adding accounts and logging out. It also provides hooks for child components to access user data and auth-related functions. */ export const AuthProvider = ({ children }: { children: ReactNode }) => { + const router = useRouter() const { open: web3modalOpen } = useAppKit() const isPwa = usePWAStatus() const { @@ -80,6 +83,7 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => { icon: '🥜', }) const toastIdRef = useRef(undefined) + const [isLoggingOut, setIsLoggingOut] = useState(false) const updateUserName = async (username: string) => { if (!user) return @@ -255,6 +259,11 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => { } const logoutUser = async () => { + const LOCAL_STORAGE_WEB_AUTHN_KEY = 'web-authn-key' + + if (isLoggingOut) return + + setIsLoggingOut(true) try { const response = await fetch('/api/peanut/user/logout-user', { method: 'GET', @@ -264,12 +273,34 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => { }) if (response.ok) { - fetchUser() + localStorage.removeItem(LOCAL_STORAGE_WEB_AUTHN_KEY) + await fetchUser() + router.push('/setup') + + toast({ + status: 'success', + title: 'Logged out successfully', + duration: 3000, + }) } else { console.error('Failed to log out user') + toast({ + status: 'error', + title: 'Failed to log out', + description: 'Please try again', + duration: 5000, + }) } } catch (error) { - console.error('Error updating user', error) + console.error('Error logging out user', error) + toast({ + status: 'error', + title: 'Error logging out', + description: 'Please try again', + duration: 5000, + }) + } finally { + setIsLoggingOut(false) } } @@ -289,6 +320,7 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => { addAccount, isFetchingUser, logoutUser, + isLoggingOut, }} > {children} diff --git a/src/utils/general.utils.ts b/src/utils/general.utils.ts index 2ae24f0c..ffea5767 100644 --- a/src/utils/general.utils.ts +++ b/src/utils/general.utils.ts @@ -1070,3 +1070,7 @@ export function getChainName(chainId: string): string | undefined { const chain = Object.entries(wagmiChains).find(([, chain]) => chain.id === Number(chainId))?.[1] return chain?.name ?? undefined } + +export const getHeaderTitle = (pathname: string) => { + return consts.pathTitles[pathname] || 'Peanut Protocol' // default title if path not found +}