Skip to content
Merged

Pirc #19

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
11 changes: 9 additions & 2 deletions app/dashboard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ const formatMintedToken = (token: TokenSummary): TokenSummary => ({
code: token.code,
issuer: token.issuer,
name: token.name,
image: token.image,
totalSupply: token.totalSupply,
circulating: token.circulating,
holders: token.holders,
liquidityPools: token.liquidityPools,
description: token.description,
})
Expand Down Expand Up @@ -90,9 +93,13 @@ export default function DashboardPage() {
mintedTokens.map((token) => ({
code: token.assetCode,
issuer: token.issuer,
name: token.name,
name: token.tomlName || token.name,
image: token.image,
totalSupply: token.totalSupply,
description: token.description,
circulating: token.circulatingSupply,
holders: token.holders,
liquidityPools: token.liquidityPools,
description: token.tomlDescription || token.description,
})),
[mintedTokens]
)
Expand Down
146 changes: 102 additions & 44 deletions app/explore/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
"use client"

import { useState } from "react"
import { useState, useMemo, useCallback } from "react"
import { useRouter } from "next/navigation"
import Link from "next/link"
import { Button } from "@/components/ui/button"
import { Card, CardContent } from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import {
Compass,
ArrowRightLeft,
Expand All @@ -13,23 +14,40 @@ import {
TrendingUp,
Droplets,
Loader2,
Star,
ChevronRight,
Send,
History,
Shield,
Search,
ChevronDown,
} from "lucide-react"
import { useTokenRegistry } from "@/hooks/useTokenRegistry"
import { useNetworkTokens } from "@/hooks/useNetworkTokens"
import { TokenIcon } from "@/components/token-icon"

const PAGE_SIZE = 15

export default function ExplorePage() {
const router = useRouter()
const { tokens, isLoading: tokensLoading, error: tokensError } = useTokenRegistry()
const [searchQuery, setSearchQuery] = useState("")

const {
tokens,
isLoading,
isLoadingMore,
error: tokensError,
hasMore,
loadMore,
refresh,
} = useNetworkTokens({ searchQuery, limit: PAGE_SIZE })

const displayTokens = useMemo(() =>
tokens.filter(token => token.assetCode && token.assetCode.trim() !== ""),
[tokens]
)

const filteredTokens = tokens
.filter(token =>
token.assetCode && token.assetCode.trim() !== ""
)
.slice(0, 12)
const handleSearch = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
setSearchQuery(e.target.value)
}, [])

const exploreCategories = [
{
Expand Down Expand Up @@ -123,55 +141,95 @@ export default function ExplorePage() {

{/* Token Discovery */}
<div className="space-y-4">
<div>
<h2 className="text-xl font-bold text-slate-900 dark:text-white">Available Tokens</h2>
<p className="text-sm text-slate-500 dark:text-slate-400">Discover tokens on the network</p>
<div className="flex items-center justify-between flex-wrap gap-3">
<div>
<h2 className="text-xl font-bold text-slate-900 dark:text-white">Available Tokens</h2>
<p className="text-sm text-slate-500 dark:text-slate-400">
{displayTokens.length > 0
? `Showing ${displayTokens.length} token${displayTokens.length !== 1 ? "s" : ""}`
: "Discover tokens on the network"}
</p>
</div>
<div className="relative w-full sm:w-64">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-slate-400" />
<Input
placeholder="Search tokens..."
value={searchQuery}
onChange={handleSearch}
className="pl-9 rounded-xl"
/>
</div>
</div>

{tokensLoading ? (
<div className="flex items-center justify-center py-8">
{isLoading ? (
<div className="flex items-center justify-center py-12">
<Loader2 className="h-6 w-6 animate-spin text-slate-400" />
<span className="ml-2 text-sm text-slate-500">Loading tokens...</span>
</div>
) : tokensError ? (
<Card>
<CardContent className="py-8 text-center">
<p className="text-sm text-slate-500">Unable to load tokens right now.</p>
<Button variant="outline" size="sm" className="mt-3" onClick={() => router.refresh()}>Try again</Button>
<Button variant="outline" size="sm" className="mt-3" onClick={() => refresh()}>Try again</Button>
</CardContent>
</Card>
) : (
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-3">
{filteredTokens.length === 0 ? (
<div className="col-span-full text-center py-8">
<p className="text-sm text-slate-500">No tokens available yet.</p>
</div>
) : (
filteredTokens.map((token) => (
<Link
key={`${token.assetCode}-${token.issuer || ""}`}
href={`/token/${encodeURIComponent(token.assetCode)}${token.issuer ? `?issuer=${encodeURIComponent(token.issuer)}` : ''}`}
<>
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-3">
{displayTokens.length === 0 ? (
<div className="col-span-full text-center py-8">
<p className="text-sm text-slate-500">
{searchQuery.trim() ? "No tokens match your search" : "No tokens available yet."}
</p>
</div>
) : (
displayTokens.map((token) => (
<Link
key={`${token.assetCode}-${token.issuer || ""}`}
href={`/token/${encodeURIComponent(token.assetCode)}${token.issuer ? `?issuer=${encodeURIComponent(token.issuer)}` : ''}`}
>
<Card className="h-full hover:shadow-md transition-shadow border border-slate-200 dark:border-slate-700 hover:border-green-200 dark:hover:border-green-800/50 group cursor-pointer">
<CardContent className="p-4 flex items-center gap-3">
<TokenIcon
image={token.image}
code={token.assetCode}
issuer={token.issuer}
size="md"
/>
<div className="min-w-0 flex-1">
<p className="font-semibold text-sm text-slate-900 dark:text-white truncate group-hover:text-green-600 transition-colors">
{token.assetCode}
</p>
{token.name ? (
<p className="text-xs text-slate-500 dark:text-slate-400 truncate">{token.name}</p>
) : null}
</div>
<ChevronRight className="h-4 w-4 text-slate-300 dark:text-slate-600 group-hover:text-green-500 transition-colors shrink-0" />
</CardContent>
</Card>
</Link>
))
)}
</div>

{hasMore && !searchQuery.trim() && (
<div className="flex justify-center pt-4">
<Button
variant="outline"
onClick={loadMore}
disabled={isLoadingMore}
className="gap-2 rounded-xl"
>
<Card className="h-full hover:shadow-md transition-shadow border border-slate-200 dark:border-slate-700 hover:border-green-200 dark:hover:border-green-800/50 group cursor-pointer">
<CardContent className="p-4 flex items-center gap-3">
<div className="w-10 h-10 rounded-full bg-gradient-to-br from-slate-100 to-green-50 dark:from-slate-700 dark:to-slate-800 flex items-center justify-center shrink-0">
<Star className="h-5 w-5 text-green-600 dark:text-green-400" />
</div>
<div className="min-w-0 flex-1">
<p className="font-semibold text-sm text-slate-900 dark:text-white truncate group-hover:text-green-600 transition-colors">
{token.assetCode}
</p>
{token.name && (
<p className="text-xs text-slate-500 dark:text-slate-400 truncate">{token.name}</p>
)}
</div>
<ChevronRight className="h-4 w-4 text-slate-300 dark:text-slate-600 group-hover:text-green-500 transition-colors shrink-0" />
</CardContent>
</Card>
</Link>
))
{isLoadingMore ? (
<Loader2 className="h-4 w-4 animate-spin" />
) : (
<ChevronDown className="h-4 w-4" />
)}
{isLoadingMore ? "Loading..." : "Load more tokens"}
</Button>
</div>
)}
</div>
</>
)}
</div>
</div>
Expand Down
3 changes: 3 additions & 0 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Navbar } from "@/components/navbar"
import { Suspense } from "react"
import { PiProvider } from "@/components/providers/pi-provider"
import { BalanceRefreshProvider } from "@/components/providers/balance-refresh-provider"
import { PoolPriceProvider } from "@/components/providers/pool-price-provider"
import { DisclaimerProvider } from "@/components/disclaimer-provider"
import { RefCapture } from "@/components/ref-capture"
import Script from 'next/script'
Expand Down Expand Up @@ -40,6 +41,7 @@ export default function RootLayout({
<Script src="https://sdk.minepi.com/pi-sdk.js" strategy="beforeInteractive" />
<PiProvider>
<BalanceRefreshProvider>
<PoolPriceProvider>
<ThemeProvider attribute="class" defaultTheme="dark" enableSystem>
<Suspense fallback={null}>
<RefCapture />
Expand All @@ -49,6 +51,7 @@ export default function RootLayout({
<DisclaimerProvider />
</Suspense>
</ThemeProvider>
</PoolPriceProvider>
</BalanceRefreshProvider>
</PiProvider>
</body>
Expand Down
6 changes: 5 additions & 1 deletion app/my-tokens/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { Badge } from "@/components/ui/badge"
import { Loader2 } from "lucide-react"
import { useTokenRegistry } from "@/hooks/useTokenRegistry"
import { usePi } from "@/components/providers/pi-provider"
import { TokenIcon } from "@/components/token-icon"

const formatSupply = (value?: number) =>
(value ?? 0).toLocaleString(undefined, { maximumFractionDigits: 2 })
Expand Down Expand Up @@ -67,11 +68,14 @@ export default function MyTokensPage() {
<Card key={token.assetCode} className="bg-card border-border">
<CardHeader>
<div className="flex items-center justify-between">
<div>
<div className="flex items-center gap-3">
<TokenIcon image={token.image} code={token.assetCode} issuer={token.issuer} size="lg" />
<div>
<CardTitle className="text-xl">{token.assetCode}</CardTitle>
<CardDescription className="text-xs mt-1">
Issuer: {token.issuer.slice(0, 8)}...{token.issuer.slice(-6)}
</CardDescription>
</div>
</div>
<Badge variant="secondary">{token.name || "Custom Token"}</Badge>
</div>
Expand Down
Loading
Loading