diff --git a/.vscode/settings.json b/.vscode/settings.json index 196450acf..1e261a9f3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,6 +2,11 @@ "typescript.tsdk": "node_modules/typescript/lib", "npm.packageManager": "pnpm", "eslint.useESLintClass": true, + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "always" + }, "eslint.workingDirectories": [ "./packages/colors", "./packages/icons", diff --git a/apps/portfolio/src/app/[address]/@list/collectibles/_components/collectibles-table.tsx b/apps/portfolio/src/app/[address]/@list/collectibles/_components/collectibles-table.tsx index a678c23a2..5acad61bb 100644 --- a/apps/portfolio/src/app/[address]/@list/collectibles/_components/collectibles-table.tsx +++ b/apps/portfolio/src/app/[address]/@list/collectibles/_components/collectibles-table.tsx @@ -1,18 +1,13 @@ -/* eslint-disable @next/next/no-img-element */ 'use client' import { useMemo } from 'react' -import { Button, Skeleton } from '@status-im/components' -import { SadIcon } from '@status-im/icons/20' +import { CollectiblesGrid as CollectiblesList } from '@status-im/wallet/components' import { useInfiniteQuery } from '@tanstack/react-query' -import { cx } from 'class-variance-authority' import { usePathname, useSearchParams } from 'next/navigation' -import { ErrorBoundary } from 'react-error-boundary' +import { Link } from 'src/app/_components/link' -import { Link } from '../../../../_components/link' -import { DEFAULT_SORT, GRADIENTS } from '../../../../_constants' -import { useInfiniteLoading } from '../../../../_hooks/use-infinite-loading' +import { DEFAULT_SORT } from '../../../../_constants' import { useSearchAndSort } from '../../../../_hooks/use-search-and-sort' import type { GetCollectiblesProps, GetCollectiblesResponse } from '../_actions' @@ -28,29 +23,6 @@ type Props = { hasMore?: boolean } -const FallbackImage = () => { - return ( -
-
- - No image available -
-
- ) -} - -const CollectibleImage = ({ url, name }: { url: string; name: string }) => { - return ( -
- {name} -
- ) -} - const CollectiblesGrid = ({ initialCollectibles, address, @@ -117,125 +89,26 @@ const CollectiblesGrid = ({ staleTime: 0, }) - const { endOfPageRef, isLoading } = useInfiniteLoading({ - rootMargin: '-100px', - fetchNextPage, - isFetchingNextPage, - hasNextPage: hasNextPage ?? false, - }) - const collectibles = useMemo(() => { return data.pages.flatMap(page => page.collectibles ?? []) }, [data.pages]) return ( - <> -
- {collectibles.map(collectible => { - const href = `/${address}/collectibles/${collectible.network}/${collectible.contract}/${collectible.id}` - const search = searchParams.toString() - const query = search ? `?${search}` : '' - // Checking if the collectible.id is in the pathname by splitting the pathname and checking if the last element is the collectible.id and if it is same contract - const isActive = - pathname.split('/').pop() === collectible.id && - pathname.includes(collectible.contract) - - const imageUrl = collectible.thumbnail ?? collectible.image - - return ( - - {imageUrl ? ( - }> - - - ) : ( - - )} -
- {/* {collectible.collection.image && ( - {collectible.collection.name} - )} */} -
- {collectible.name} -
-
- - ) - })} - {collectibles.length === 0 && !!search && ( -
-

- No collectibles found -

-

- We didn't find any collectibles that match your search -

- -
- )} -
- {isLoading && hasNextPage && ( -
- {GRADIENTS.slice(0, 4).map((gradient, index) => { - return ( -
-
-
-
- - -
-
- ) - })} -
- )} - {hasNextPage &&
} - + { + // Handle select action if needed + }} + /> ) } diff --git a/apps/portfolio/src/app/_components/action-buttons.tsx b/apps/portfolio/src/app/_components/action-buttons.tsx index ebc8310ef..5364b3743 100644 --- a/apps/portfolio/src/app/_components/action-buttons.tsx +++ b/apps/portfolio/src/app/_components/action-buttons.tsx @@ -2,12 +2,13 @@ import { Input } from '@status-im/components' import { SearchIcon } from '@status-im/icons/20' +import { TabLink } from '@status-im/wallet/components' +import NextLink from 'next/link' import { useParams, usePathname } from 'next/navigation' import { match, P } from 'ts-pattern' import { useSearchAndSort } from '../_hooks/use-search-and-sort' import { AdminDropdownSort } from './dropdown-sort' -import { TabLink } from './tab-link' const checkPathnameAndReturnTabValue = ( pathname: string @@ -41,8 +42,20 @@ const ActionButtons = () => { return (
- Assets - Collectibles + + Assets + + + Collectibles +
{ .with( P.when(p => { const segments = p.split('/').filter(Boolean) - return p.includes('/assets/') && segments.length >= 4 + return p.includes('/assets/') && segments.length === 3 }), () => true ) diff --git a/apps/wallet/src/components/link.tsx b/apps/wallet/src/components/link.tsx new file mode 100644 index 000000000..b08cb4973 --- /dev/null +++ b/apps/wallet/src/components/link.tsx @@ -0,0 +1,18 @@ +import { Link as LinkBase } from '@tanstack/react-router' + +type LinkProps = { + href: string + className?: string + children: React.ReactNode +} + +const Link = (props: LinkProps) => { + const { href, className, children } = props + return ( + + {children} + + ) +} + +export { Link } diff --git a/apps/wallet/src/data/api.ts b/apps/wallet/src/data/api.ts index 2ff7d8543..3a45620ce 100644 --- a/apps/wallet/src/data/api.ts +++ b/apps/wallet/src/data/api.ts @@ -573,7 +573,7 @@ const apiRouter = router({ // ) const { id } = await keyStore.importKey( - Buffer.from(input.privateKey), + Uint8Array.from(Buffer.from(input.privateKey)), input.name, input.password, walletCore.CoinType.ethereum, diff --git a/apps/wallet/src/router.gen.ts b/apps/wallet/src/router.gen.ts index 3c7800a8a..f8e657ae7 100644 --- a/apps/wallet/src/router.gen.ts +++ b/apps/wallet/src/router.gen.ts @@ -13,10 +13,11 @@ import { Route as rootRoute } from './routes/__root' import { Route as OnboardingLayoutImport } from './routes/onboarding/_layout' import { Route as IndexImport } from './routes/index' -import { Route as PortfolioIndexImport } from './routes/portfolio/index' import { Route as OnboardingIndexImport } from './routes/onboarding/index' import { Route as OnboardingNewImport } from './routes/onboarding/new' import { Route as OnboardingImportImport } from './routes/onboarding/import' +import { Route as PortfolioCollectiblesIndexImport } from './routes/portfolio/collectibles/index' +import { Route as PortfolioAssetsIndexImport } from './routes/portfolio/assets/index' // Create/Update Routes @@ -32,12 +33,6 @@ const IndexRoute = IndexImport.update({ getParentRoute: () => rootRoute, } as any) -const PortfolioIndexRoute = PortfolioIndexImport.update({ - id: '/portfolio/', - path: '/portfolio/', - getParentRoute: () => rootRoute, -} as any) - const OnboardingIndexRoute = OnboardingIndexImport.update({ id: '/', path: '/', @@ -56,6 +51,20 @@ const OnboardingImportRoute = OnboardingImportImport.update({ getParentRoute: () => OnboardingLayoutRoute, } as any) +const PortfolioCollectiblesIndexRoute = PortfolioCollectiblesIndexImport.update( + { + id: '/portfolio/collectibles/', + path: '/portfolio/collectibles/', + getParentRoute: () => rootRoute, + } as any, +) + +const PortfolioAssetsIndexRoute = PortfolioAssetsIndexImport.update({ + id: '/portfolio/assets/', + path: '/portfolio/assets/', + getParentRoute: () => rootRoute, +} as any) + // Populate the FileRoutesByPath interface declare module '@tanstack/react-router' { @@ -95,11 +104,18 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof OnboardingIndexImport parentRoute: typeof OnboardingLayoutImport } - '/portfolio/': { - id: '/portfolio/' - path: '/portfolio' - fullPath: '/portfolio' - preLoaderRoute: typeof PortfolioIndexImport + '/portfolio/assets/': { + id: '/portfolio/assets/' + path: '/portfolio/assets' + fullPath: '/portfolio/assets' + preLoaderRoute: typeof PortfolioAssetsIndexImport + parentRoute: typeof rootRoute + } + '/portfolio/collectibles/': { + id: '/portfolio/collectibles/' + path: '/portfolio/collectibles' + fullPath: '/portfolio/collectibles' + preLoaderRoute: typeof PortfolioCollectiblesIndexImport parentRoute: typeof rootRoute } } @@ -128,7 +144,8 @@ export interface FileRoutesByFullPath { '/onboarding/import': typeof OnboardingImportRoute '/onboarding/new': typeof OnboardingNewRoute '/onboarding/': typeof OnboardingIndexRoute - '/portfolio': typeof PortfolioIndexRoute + '/portfolio/assets': typeof PortfolioAssetsIndexRoute + '/portfolio/collectibles': typeof PortfolioCollectiblesIndexRoute } export interface FileRoutesByTo { @@ -136,7 +153,8 @@ export interface FileRoutesByTo { '/onboarding/import': typeof OnboardingImportRoute '/onboarding/new': typeof OnboardingNewRoute '/onboarding': typeof OnboardingIndexRoute - '/portfolio': typeof PortfolioIndexRoute + '/portfolio/assets': typeof PortfolioAssetsIndexRoute + '/portfolio/collectibles': typeof PortfolioCollectiblesIndexRoute } export interface FileRoutesById { @@ -146,7 +164,8 @@ export interface FileRoutesById { '/onboarding/import': typeof OnboardingImportRoute '/onboarding/new': typeof OnboardingNewRoute '/onboarding/': typeof OnboardingIndexRoute - '/portfolio/': typeof PortfolioIndexRoute + '/portfolio/assets/': typeof PortfolioAssetsIndexRoute + '/portfolio/collectibles/': typeof PortfolioCollectiblesIndexRoute } export interface FileRouteTypes { @@ -157,14 +176,16 @@ export interface FileRouteTypes { | '/onboarding/import' | '/onboarding/new' | '/onboarding/' - | '/portfolio' + | '/portfolio/assets' + | '/portfolio/collectibles' fileRoutesByTo: FileRoutesByTo to: | '/' | '/onboarding/import' | '/onboarding/new' | '/onboarding' - | '/portfolio' + | '/portfolio/assets' + | '/portfolio/collectibles' id: | '__root__' | '/' @@ -172,20 +193,23 @@ export interface FileRouteTypes { | '/onboarding/import' | '/onboarding/new' | '/onboarding/' - | '/portfolio/' + | '/portfolio/assets/' + | '/portfolio/collectibles/' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute OnboardingLayoutRoute: typeof OnboardingLayoutRouteWithChildren - PortfolioIndexRoute: typeof PortfolioIndexRoute + PortfolioAssetsIndexRoute: typeof PortfolioAssetsIndexRoute + PortfolioCollectiblesIndexRoute: typeof PortfolioCollectiblesIndexRoute } const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, OnboardingLayoutRoute: OnboardingLayoutRouteWithChildren, - PortfolioIndexRoute: PortfolioIndexRoute, + PortfolioAssetsIndexRoute: PortfolioAssetsIndexRoute, + PortfolioCollectiblesIndexRoute: PortfolioCollectiblesIndexRoute, } export const routeTree = rootRoute @@ -200,7 +224,8 @@ export const routeTree = rootRoute "children": [ "/", "/onboarding", - "/portfolio/" + "/portfolio/assets/", + "/portfolio/collectibles/" ] }, "/": { @@ -226,8 +251,11 @@ export const routeTree = rootRoute "filePath": "onboarding/index.tsx", "parent": "/onboarding" }, - "/portfolio/": { - "filePath": "portfolio/index.tsx" + "/portfolio/assets/": { + "filePath": "portfolio/assets/index.tsx" + }, + "/portfolio/collectibles/": { + "filePath": "portfolio/collectibles/index.tsx" } } } diff --git a/apps/wallet/src/routes/index.tsx b/apps/wallet/src/routes/index.tsx index 04f8acf2d..e6f07a1ed 100644 --- a/apps/wallet/src/routes/index.tsx +++ b/apps/wallet/src/routes/index.tsx @@ -1,7 +1,13 @@ -import { createFileRoute } from '@tanstack/react-router' +import { createFileRoute, redirect } from '@tanstack/react-router' export const Route = createFileRoute('/')({ component: RouteComponent, + loader: () => { + redirect({ + to: '/portfolio/assets', + throw: true, + }) + }, head: () => ({ meta: [ { diff --git a/apps/wallet/src/routes/portfolio/index.tsx b/apps/wallet/src/routes/portfolio/assets/index.tsx similarity index 63% rename from apps/wallet/src/routes/portfolio/index.tsx rename to apps/wallet/src/routes/portfolio/assets/index.tsx index ff0dbbee4..b9ba50b8d 100644 --- a/apps/wallet/src/routes/portfolio/index.tsx +++ b/apps/wallet/src/routes/portfolio/assets/index.tsx @@ -1,13 +1,15 @@ // import { Suspense } from 'react' -import { AssetsList } from '@status-im/wallet/components' +import { AssetsList, TabLink } from '@status-im/wallet/components' import { useQuery } from '@tanstack/react-query' -import { createFileRoute } from '@tanstack/react-router' +import { createFileRoute, useRouterState } from '@tanstack/react-router' + +import { Link } from '@/components/link' // import { DetailDrawer } from '../../../../portfolio/src/app/[address]/@detail/_drawer' // import { Loading as LoadingNav } from '../../../../portfolio/src/app/[address]/@nav/loading' -export const Route = createFileRoute('/portfolio/')({ +export const Route = createFileRoute('/portfolio/assets/')({ component: RouteComponent, head: () => ({ meta: [ @@ -19,6 +21,9 @@ export const Route = createFileRoute('/portfolio/')({ }) function RouteComponent() { + const { location } = useRouterState() + const pathname = location.pathname + const handleSelect = (url: string, options?: { scroll?: boolean }) => { // Handle the selection of an asset console.log('Selected asset URL:', url) @@ -76,12 +81,32 @@ function RouteComponent() { }) return ( -
-
-
-
+
+
+ {/* {nav} */} + nav +
+ +
+ {/* {list} */} +
+
+ + Assets + + + Collectibles + +
+
{isLoading ? ( -
Loading...
+
+
+
) : ( )}
+
-
Detail
+
+ {/* {detail} */}
diff --git a/apps/wallet/src/routes/portfolio/collectibles/index.tsx b/apps/wallet/src/routes/portfolio/collectibles/index.tsx new file mode 100644 index 000000000..9d2d77a45 --- /dev/null +++ b/apps/wallet/src/routes/portfolio/collectibles/index.tsx @@ -0,0 +1,199 @@ +import { + CollectiblesGrid as CollectiblesList, + TabLink, +} from '@status-im/wallet/components' +import { useInfiniteQuery } from '@tanstack/react-query' +import { createFileRoute, useRouterState } from '@tanstack/react-router' + +import { Link } from '@/components/link' + +import type { NetworkType } from '@status-im/wallet/data' + +const DEFAULT_SORT = { + assets: { column: 'name', direction: 'asc' as const }, + collectibles: { column: 'name', direction: 'asc' as const }, +} as const + +export const SORT_OPTIONS = { + assets: { + name: 'Name', + balance: 'Balance', + '24h': '24H%', + value: 'Value', + price: 'Price', + }, + collectibles: { + name: 'Name', + collection: 'Collection', + }, +} as const + +export const Route = createFileRoute('/portfolio/collectibles/')({ + component: RouteComponent, + head: () => ({ + meta: [ + { + title: 'Extension | Wallet | Portfolio', + }, + ], + }), +}) + +const getCollectibles = async ( + address: string, + networks: NetworkType[], + search?: string, + sort?: { + column: 'name' | 'collection' + direction: 'asc' | 'desc' + }, +) => { + const url = new URL('http://localhost:3030/api/trpc/collectibles.page') + + url.searchParams.set( + 'input', + JSON.stringify({ + json: { + address, + networks, + limit: 20, + offset: 0, + search, + sort, + }, + }), + ) + + const response = await fetch(url, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }) + + if (!response.ok) { + throw new Error('Failed to fetch.') + } + + const body = await response.json() + return body.result.data.json.collectibles +} + +function RouteComponent() { + const { location } = useRouterState() + const pathname = location.pathname + + const handleSelect = (url: string, options?: { scroll?: boolean }) => { + // Handle the selection of an asset + console.log('Selected asset URL:', url) + console.log('Scroll option:', options?.scroll) + } + + // todo: export trpc client with api router and used instead + // todo: cache + const searchParams = new URLSearchParams(window.location.search) + const search = searchParams.get('search') ?? undefined + const sortParam = searchParams.get('sort') + + const address = '0xd8da6bf26964af9d7eed9e03e53415d37aa96045' + const sort = { + column: + (sortParam?.split(',')[0] as 'name' | 'collection') || + DEFAULT_SORT.collectibles.column, + direction: + (sortParam?.split(',')[1] as 'asc' | 'desc') || + DEFAULT_SORT.collectibles.direction, + } + + const networks = searchParams.get('networks')?.split(',') ?? [ + 'ethereum', + 'optimism', + 'arbitrum', + 'base', + 'polygon', + 'bsc', + ] + + const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading } = + useInfiniteQuery({ + queryKey: ['collectibles', address, networks, search, sort], + queryFn: async ({ pageParam = 0 }) => { + const collectibles = await getCollectibles( + address, + networks as NetworkType[], + search, + sort, + ) + return { + collectibles, + nextPage: pageParam + 1, + } + }, + getNextPageParam: lastPage => lastPage.nextPage, + initialPageParam: 0, + staleTime: 60 * 60 * 1000, + gcTime: 60 * 60 * 1000, + refetchOnMount: false, + refetchOnWindowFocus: false, + refetchOnReconnect: false, + }) + + const collectibles = useMemo(() => { + return data?.pages.flatMap(page => page.collectibles ?? []) ?? [] + }, [data?.pages]) + + return ( +
+
+ {/* {nav} */} + nav +
+ +
+ {/* {list} */} +
+
+ + Assets + + + Collectibles + +
+
+ {isLoading ? ( +
+
+
+ ) : ( + { + // Clear the search input + console.log('Search cleared') + }} + hasNextPage={hasNextPage} + onSelect={handleSelect} + /> + )} +
+
+ +
+ {/* {detail} */} +
+
+
+ ) +} diff --git a/packages/wallet/package.json b/packages/wallet/package.json index 10f9b0b54..3037a5857 100644 --- a/packages/wallet/package.json +++ b/packages/wallet/package.json @@ -25,6 +25,11 @@ "types": "./dist/src/components/index.d.ts", "import": "./dist/components/index.es.js", "require": "./dist/components/index.cjs.js" + }, + "./hooks": { + "types": "./dist/src/hooks/index.d.ts", + "import": "./dist/hooks/index.es.js", + "require": "./dist/hooks/index.cjs.js" } }, "files": [ diff --git a/packages/wallet/src/components/assets-list/index.tsx b/packages/wallet/src/components/assets-list/index.tsx index 015f1f4e4..c5b535faf 100644 --- a/packages/wallet/src/components/assets-list/index.tsx +++ b/packages/wallet/src/components/assets-list/index.tsx @@ -73,8 +73,8 @@ const AssetsList = (props: Props) => { }, [assets, searchParamValue, orderByColumn, ascending]) return ( - <> -
+
+
{filteredAssets.length !== 0 && ( @@ -158,7 +158,7 @@ const AssetsList = (props: Props) => {
)}
-
+
{filteredAssets.map(asset => { const href = `${pathname?.replace( @@ -183,7 +183,7 @@ const AssetsList = (props: Props) => { src={asset.icon} />
- + {asset.name} @@ -215,7 +215,7 @@ const AssetsList = (props: Props) => { })}
- +
) } diff --git a/packages/wallet/src/components/collectibles-grid/index.tsx b/packages/wallet/src/components/collectibles-grid/index.tsx new file mode 100644 index 000000000..38c07e1be --- /dev/null +++ b/packages/wallet/src/components/collectibles-grid/index.tsx @@ -0,0 +1,197 @@ +import { Button, Skeleton } from '@status-im/components' +import { SadIcon } from '@status-im/icons/20' +import { cx } from 'class-variance-authority' + +import { useInfiniteLoading } from '../../hooks/use-infinite-loading' + +import type { Collectible } from '@status-im/wallet/data' +import type { ComponentType, ReactNode } from 'react' + +export const GRADIENTS = [ + 'linear-gradient(120deg, #F6B03C, #1992D7, #7140FD)', + 'linear-gradient(190deg, #FF7D46, #7140FD, #2A4AF5)', + 'linear-gradient(145deg, #2A4AF5, #EC266C, #F6B03C)', + 'linear-gradient(195deg, #216266, #FF7D46, #2A4AF5)', + 'linear-gradient(45deg, #7140FD, #216266, #F6B03C)', + 'linear-gradient(145deg, #F6B03C, #1992D7, #7140FD)', + 'linear-gradient(45deg, #F6B03C, #1992D7, #7140FD)', + 'linear-gradient(145deg, #FF7D46, #7140FD, #2A4AF5)', + 'linear-gradient(45deg, #2A4AF5, #EC266C, #F6B03C)', + 'linear-gradient(125deg, #216266, #FF7D46, #2A4AF5)', + 'linear-gradient(145deg, #F6B03C, #1992D7, #7140FD)', + 'linear-gradient(145deg, #F6B03C, #1992D7, #7140FD)', +] +type LinkComponentProps = { + href: string + className?: string + children: ReactNode +} + +type Props = { + collectibles: Collectible[] + address: string + pathname: string + onSelect: (url: string, options?: { scroll?: boolean }) => void + search?: string + hasNextPage?: boolean + fetchNextPage: () => void + isFetchingNextPage: boolean + searchParams: URLSearchParams + clearSearch: () => void + LinkComponent: ComponentType +} + +const FallbackImage = () => { + return ( +
+
+ + No image available +
+
+ ) +} + +const CollectibleImage = ({ url, name }: { url: string; name: string }) => { + return ( +
+ {name} +
+ ) +} + +const CollectiblesGrid = (props: Props) => { + const { + collectibles, + address, + fetchNextPage, + isFetchingNextPage, + pathname, + search, + searchParams, + clearSearch, + hasNextPage, + LinkComponent, + } = props + + const { endOfPageRef, isLoading } = useInfiniteLoading({ + rootMargin: '200px', + fetchNextPage, + isFetchingNextPage, + hasNextPage: hasNextPage ?? false, + }) + + return ( + <> +
+ {collectibles.map(collectible => { + const href = `/${address}/collectibles/${collectible.network}/${collectible.contract}/${collectible.id}` + const search = searchParams.toString() + const query = search ? `?${search}` : '' + // Checking if the collectible.id is in the pathname by splitting the pathname and checking if the last element is the collectible.id and if it is same contract + const isActive = + pathname.split('/').pop() === collectible.id && + pathname.includes(collectible.contract) + + const imageUrl = collectible.thumbnail ?? collectible.image + + return ( + + {imageUrl ? ( + + ) : ( + + )} +
+ {/* {collectible.collection.image && ( + {collectible.collection.name} + )} */} +
+ {collectible.name} +
+
+
+ ) + })} + {collectibles.length === 0 && !!search && ( +
+

+ No collectibles found +

+

+ We didn't find any collectibles that match your search +

+ +
+ )} +
+ {isLoading && hasNextPage && ( +
+ {GRADIENTS.slice(0, 4).map((gradient, index) => { + return ( +
+
+
+
+ + +
+
+ ) + })} +
+ )} + {hasNextPage &&
} + + ) +} + +export { CollectiblesGrid } diff --git a/packages/wallet/src/components/index.tsx b/packages/wallet/src/components/index.tsx index 9e84d1bd7..b0c75fd13 100644 --- a/packages/wallet/src/components/index.tsx +++ b/packages/wallet/src/components/index.tsx @@ -3,6 +3,7 @@ export * from '../utils/variants' export { AccountMenu } from './account-menu' export { type Account, Address, type AddressProps } from './address' export { AssetsList } from './assets-list' +export { CollectiblesGrid } from './collectibles-grid' export { CurrencyAmount } from './currency-amount' export { DeleteAddressAlert } from './delete-address-alert' export { Image, type ImageProps } from './image' @@ -19,4 +20,5 @@ export { } from './shorten-address' export { Slider, type SliderProps } from './slider' export { StickyHeaderContainer } from './sticky-header-container' +export { TabLink } from './tab-link' export { Tooltip } from './tooltip' diff --git a/packages/wallet/src/components/tab-link/index.tsx b/packages/wallet/src/components/tab-link/index.tsx new file mode 100644 index 000000000..3653dc5a5 --- /dev/null +++ b/packages/wallet/src/components/tab-link/index.tsx @@ -0,0 +1,40 @@ +import { cx } from 'class-variance-authority' + +import type { ComponentType, ReactNode } from 'react' + +type LinkComponentProps = { + href: string + className?: string + children: ReactNode +} + +type Props = { + href: string + children: ReactNode + className?: string + isActive?: boolean + LinkComponent: ComponentType +} + +const TabLink = ({ + href, + children, + className, + LinkComponent, + isActive, +}: Props) => { + return ( + + {children} + + ) +} + +export { TabLink } diff --git a/packages/wallet/src/hooks/index.ts b/packages/wallet/src/hooks/index.ts new file mode 100644 index 000000000..fd15fbbe9 --- /dev/null +++ b/packages/wallet/src/hooks/index.ts @@ -0,0 +1,2 @@ +export * from './use-infinite-loading' +export * from './use-intersection-observer' diff --git a/apps/portfolio/src/app/_hooks/use-infinite-loading.ts b/packages/wallet/src/hooks/use-infinite-loading.ts similarity index 98% rename from apps/portfolio/src/app/_hooks/use-infinite-loading.ts rename to packages/wallet/src/hooks/use-infinite-loading.ts index 42e9b058d..430cd2ea8 100644 --- a/apps/portfolio/src/app/_hooks/use-infinite-loading.ts +++ b/packages/wallet/src/hooks/use-infinite-loading.ts @@ -1,3 +1,5 @@ +'use client' + import { useEffect, useRef } from 'react' import { useIntersectionObserver } from './use-intersection-observer' diff --git a/apps/portfolio/src/app/_hooks/use-intersection-observer.ts b/packages/wallet/src/hooks/use-intersection-observer.ts similarity index 99% rename from apps/portfolio/src/app/_hooks/use-intersection-observer.ts rename to packages/wallet/src/hooks/use-intersection-observer.ts index 1d548866c..1f9119743 100644 --- a/apps/portfolio/src/app/_hooks/use-intersection-observer.ts +++ b/packages/wallet/src/hooks/use-intersection-observer.ts @@ -13,7 +13,7 @@ export function useIntersectionObserver( root = null, rootMargin = '0%', freezeOnceVisible = false, - }: Args + }: Args, ): IntersectionObserverEntry | undefined { const [entry, setEntry] = useState() diff --git a/packages/wallet/tailwind.config.ts b/packages/wallet/tailwind.config.ts index 59658440b..877ee772e 100644 --- a/packages/wallet/tailwind.config.ts +++ b/packages/wallet/tailwind.config.ts @@ -1,12 +1,12 @@ -import statusComponentsConfig from '@status-im/components/config' +import config from '@status-im/components/config' import { scrollbarGutter, scrollbarWidth } from 'tailwind-scrollbar-utilities' import plugin from 'tailwindcss/plugin' -import animatePlugin from 'tailwindcss-animate' +import * as animatePlugin from 'tailwindcss-animate' import type { Config } from 'tailwindcss' -const config: Config = { - presets: [statusComponentsConfig], +const tailwindConfig: Config = { + presets: [config], future: { hoverOnlyWhenSupported: true, @@ -255,8 +255,6 @@ const config: Config = { }, }, plugins: [ - animatePlugin, - // add scrollbar utilities before lands in tailwindcss // @see https://github.com/tailwindlabs/tailwindcss/pull/5732 scrollbarWidth(), @@ -286,8 +284,9 @@ const config: Config = { // ) }), + animatePlugin, // reactAriaComponentsPlugin, ], } -export default config +export default tailwindConfig diff --git a/packages/wallet/tsconfig.json b/packages/wallet/tsconfig.json index 1b48b8ce5..d433b64a7 100644 --- a/packages/wallet/tsconfig.json +++ b/packages/wallet/tsconfig.json @@ -8,6 +8,8 @@ "**/*.json" ], "compilerOptions": { + "rootDir": ".", + // "composite": true, "jsx": "preserve", "outDir": "./dist", "allowJs": true, diff --git a/packages/wallet/vite.config.ts b/packages/wallet/vite.config.ts index 986f5882f..7b4835086 100644 --- a/packages/wallet/vite.config.ts +++ b/packages/wallet/vite.config.ts @@ -19,6 +19,7 @@ export default defineConfig(({ mode }) => { 'components/index': './src/components/index.tsx', 'tailwind.config': './tailwind.config.ts', 'data/index': './src/data/index.ts', + 'hooks/index': './src/hooks/index.ts', }, formats: ['es', 'cjs'], fileName: format => {