feat: improve create-fi page

This commit is contained in:
0xzio
2024-11-14 20:32:06 +08:00
parent 032be05f90
commit bcf5aaee87
47 changed files with 213 additions and 136 deletions

View File

@@ -5,7 +5,7 @@ export const revalidate = 3600 * 24
export default async function Page() {
return (
<div className="pt-10">
<div className="pt-10 mx-auto md:max-w-3xl">
<Wallet />
</div>
)

View File

@@ -3,18 +3,18 @@
import { PropsWithChildren, Suspense } from 'react'
import { useQueryEthBalance } from '@/app/(creator-fi)/hooks/useEthBalance'
import { useQueryEthPrice } from '@/app/(creator-fi)/hooks/useEthPrice'
import { useQuerySpace, useSpace } from '@/app/(creator-fi)/hooks/useSpace'
import { ClientOnly } from '@/components/ClientOnly'
import LoadingCircle from '@/components/icons/loading-circle'
import { Profile } from '@/components/Profile/Profile'
import { SpaceType } from '@/lib/types'
import Link from 'next/link'
import { SpaceBasicInfo } from './Space/SpaceBasicInfo'
import { SpaceNav } from './Space/SpaceNav'
interface HeaderProps {
isLoading: boolean
space: SpaceType
}
function Header({ isLoading }: HeaderProps) {
function Header({ space }: HeaderProps) {
return (
<header className="flex h-16 items-center justify-between px-4">
<div className="flex flex-1 items-center gap-2 ">
@@ -36,10 +36,9 @@ function Header({ isLoading }: HeaderProps) {
></path>
</svg>
</Link>
{isLoading && <div>Loading..</div>}
{!isLoading && <SpaceBasicInfo />}
<SpaceBasicInfo space={space} />
</div>
<SpaceNav></SpaceNav>
<SpaceNav />
<div className="flex flex-1 justify-end">
<Profile />
</div>
@@ -47,26 +46,17 @@ function Header({ isLoading }: HeaderProps) {
)
}
export function CreatorFiLayout({ children }: PropsWithChildren) {
interface Props {
space: SpaceType
}
export function CreatorFiLayout({ children, space }: PropsWithChildren<Props>) {
useQueryEthPrice()
useQueryEthBalance()
useQuerySpace()
const { space } = useSpace()
if (!space?.address) {
return (
<>
<Header isLoading />
<div className="flex h-[80vh] items-center justify-center">
<LoadingCircle />
</div>
</>
)
}
return (
<>
<Header isLoading={false} />
<Header space={space} />
{children}
</>
)

View File

@@ -1,16 +1,16 @@
'use client'
import { useHolders } from '@/app/(creator-fi)/hooks/useHolders'
import { useSpaceContext } from '@/components/SpaceContext'
import { Skeleton } from '@/components/ui/skeleton'
import { UserAvatar } from '@/components/UserAvatar'
import { useHolders } from '@/app/(creator-fi)/hooks/useHolders'
import { useSpace } from '@/app/(creator-fi)/hooks/useSpace'
import { precision } from '@/lib/math'
import { cn, shortenAddress } from '@/lib/utils'
interface Props {}
export function HolderList({}: Props) {
const { space } = useSpace()
const space = useSpaceContext()
const { holders, isLoading } = useHolders()
if (isLoading) {
@@ -38,7 +38,9 @@ export function HolderList({}: Props) {
<div className="text-sm">{shortenAddress(item.account)}</div>
</div>
<div className="flex gap-1">
<span className="font-bold">{precision.toDecimal(item.balance).toFixed(2)}</span>
<span className="font-bold">
{precision.toDecimal(item.balance).toFixed(2)}
</span>
{space.symbolName}
</div>
</div>

View File

@@ -2,12 +2,12 @@
import { Badge } from '@/components/ui/badge'
import { useCopyToClipboard } from '@/app/(creator-fi)/hooks/useCopyToClipboard'
import { useSpace } from '@/app/(creator-fi)/hooks/useSpace'
import { Copy } from 'lucide-react'
import { toast } from 'sonner'
import { useSpaceContext } from '@/components/SpaceContext'
export function SpaceAddress() {
const { space } = useSpace()
const space = useSpaceContext()
const { address = '' } = space
const { copy } = useCopyToClipboard()
return (

View File

@@ -1,33 +1,27 @@
'use client'
import { useAddress } from '@/app/(creator-fi)/hooks/useAddress'
import { useSpace } from '@/app/(creator-fi)/hooks/useSpace'
import { Button } from '@/components/ui/button'
import { getSpaceId } from '@/lib/getSpaceId'
import { SpaceType } from '@/lib/types'
import { getUrl } from '@/lib/utils'
interface Props {}
export function SpaceBasicInfo({}: Props) {
const address = useAddress()
const { space } = useSpace()
const spaceId = getSpaceId()
interface Props {
space: SpaceType
}
export function SpaceBasicInfo({ space }: Props) {
return (
<div className="flex items-center gap-2">
<img
alt={space.name || ''}
className="h-9 w-9 rounded-lg bg-foreground shadow-sm"
src={
space.logo ||
'https://public.blob.vercel-storage.com/eEZHAoPTOBSYGBE3/JRajRyC-PhBHEinQkupt02jqfKacBVHLWJq7Iy.png'
}
src={getUrl(space.logo)}
/>
<div className="text-lg font-bold">{space.name}</div>
{/* <SpaceAddress /> */}
<Button size="sm" variant="secondary" className="rounded-full">
<a
href={`https://www.respace.one/space/${spaceId}`}
href={`https://www.respace.one/space/${space.address}`}
className="flex items-center gap-1"
target="_blank"
>

View File

@@ -1,6 +1,5 @@
'use client'
import { useSpace } from '@/app/(creator-fi)/hooks/useSpace'
import { cn } from '@/lib/utils'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
@@ -9,9 +8,6 @@ interface Props {}
export function SpaceNav({}: Props) {
const pathname = usePathname()
const { space } = useSpace()
if (!space) return null
const Paths = {
about: `/creator-fi`,
@@ -27,7 +23,7 @@ export function SpaceNav({}: Props) {
const linkClassName = (path: string) =>
cn(
'inline-flex item-center justify-center py-1.5 border-b-2 px-3 -mb-[1px] border-transparent',
path === pathname && 'border-black border-zinc-400'
path === pathname && 'border-black border-zinc-400',
)
return (

View File

@@ -3,14 +3,14 @@
import { ReactNode } from 'react'
import { Skeleton } from '@/components/ui/skeleton'
import { useEthPrice } from '@/app/(creator-fi)/hooks/useEthPrice'
import { useSpace } from '@/app/(creator-fi)/hooks/useSpace'
import { precision } from '@/lib/math'
import { useBalance } from 'wagmi'
import { useSpaceContext } from '@/components/SpaceContext'
interface Props {}
export function SpaceStats({}: Props) {
const { space } = useSpace()
const space = useSpaceContext()
const { ethPrice } = useEthPrice()
return (
@@ -49,7 +49,7 @@ function StatsItem({ title, value }: StatsItemProps) {
}
function MarketCap() {
const { space } = useSpace()
const space = useSpaceContext()
const { data, isLoading } = useBalance({ address: space.address })
const { ethPrice } = useEthPrice()
if (isLoading || !data) return <Skeleton className="h-8"></Skeleton>

View File

@@ -2,16 +2,16 @@
import { Skeleton } from '@/components/ui/skeleton'
import { UserAvatar } from '@/components/UserAvatar'
import { useSpace } from '@/app/(creator-fi)/hooks/useSpace'
import { useTrades } from '@/app/(creator-fi)/hooks/useTrades'
import { precision } from '@/lib/math'
import { cn, shortenAddress } from '@/lib/utils'
import { TradeType } from '../constants'
import { useSpaceContext } from '@/components/SpaceContext'
interface Props {}
export function TradeList({}: Props) {
const { space } = useSpace()
const space = useSpaceContext()
const { trades, isLoading } = useTrades()
if (isLoading) {

View File

@@ -18,7 +18,7 @@ export function Transaction() {
}
return (
<div className="rounded-2xl bg-white p-4 shadow-sm dark:bg-zinc-950/50">
<div className="rounded-2xl bg-background p-4 shadow-sm">
<div className="rounded-lg">
<div className="mb-3 flex text-foreground/80">
<button

View File

@@ -1,8 +1,8 @@
import { useState } from 'react'
import { useQueryEthBalance } from '@/app/(creator-fi)/hooks/useEthBalance'
import { useSpace } from '@/app/(creator-fi)/hooks/useSpace'
import { useTrades } from '@/app/(creator-fi)/hooks/useTrades'
import LoadingDots from '@/components/icons/loading-dots'
import { useSpaceContext } from '@/components/SpaceContext'
import { Button } from '@/components/ui/button'
import { WalletConnectButton } from '@/components/WalletConnectButton'
import { useAddress } from '@/hooks/useAddress'
@@ -37,7 +37,7 @@ export const BuyBtn = ({
const balance = useSpaceTokenBalance()
const { refetch: refetchEth } = useQueryEthBalance()
const address = useAddress()
const { space } = useSpace()
const space = useSpaceContext()
const trade = useTrades()
const wagmiConfig = useWagmiConfig()
const checkChain = useCheckChain()
@@ -80,12 +80,13 @@ export const BuyBtn = ({
)
return (
<Button
variant="brand"
className="h-[50px] w-full rounded-xl"
disabled={!isAmountValid || isInsufficientBalance || loading}
onClick={() => onBuy()}
>
{loading ? (
<LoadingDots />
<LoadingDots />
) : isInsufficientBalance ? (
'Insufficient ETH balance'
) : isAmountValid ? (

View File

@@ -1,6 +1,6 @@
import { useState } from 'react'
import { useEthBalance } from '@/app/(creator-fi)/hooks/useEthBalance'
import { useSpace } from '@/app/(creator-fi)/hooks/useSpace'
import { useSpaceContext } from '@/components/SpaceContext'
import { Button } from '@/components/ui/button'
import { precision } from '@/lib/math'
import { toFloorFixed } from '@/lib/utils'
@@ -13,7 +13,7 @@ export const BuyPanel = () => {
const [ethAmount, setEthAmount] = useState<string>('')
const [tokenAmount, setTokenAmount] = useState<string>('')
const { ethBalance } = useEthBalance()
const { space } = useSpace()
const space = useSpaceContext()
const isAmountValid = parseFloat(ethAmount) > 0 && parseFloat(tokenAmount) > 0
@@ -66,7 +66,7 @@ export const BuyPanel = () => {
</div>
</div>
<div className="mb-4 rounded-xl bg-gray-100 p-4 dark:bg-zinc-900">
<div className="mb-4 rounded-xl bg-foreground/5 p-4">
<div className="text-sm">Buy</div>
<AmountInput
symbolName={space.symbolName}

View File

@@ -81,12 +81,13 @@ export const SellBtn = ({
<>
{address ? (
<Button
variant="brand"
className="h-[50px] w-full rounded-xl"
disabled={!isAmountValid || isInsufficientBalance || loading}
onClick={() => onSell()}
>
{loading ? (
<LoadingDots />
<LoadingDots />
) : isInsufficientBalance ? (
`Insufficient ${space.symbolName} balance`
) : isAmountValid ? (

View File

@@ -1,5 +1,5 @@
import { useState } from 'react'
import { useSpace } from '@/app/(creator-fi)/hooks/useSpace'
import { useSpaceContext } from '@/components/SpaceContext'
import { Button } from '@/components/ui/button'
import { precision } from '@/lib/math'
import { toFloorFixed } from '@/lib/utils'
@@ -12,7 +12,7 @@ import { SpaceTokenBalance } from './SpaceTokenBalance'
export const SellPanel = () => {
const [ethAmount, setEthAmount] = useState<string>('')
const [tokenAmount, setTokenAmount] = useState<string>('')
const { space } = useSpace()
const space = useSpaceContext()
const { data: tokenBalance } = useSpaceTokenBalance()
const isAmountValid = parseFloat(tokenAmount) > 0
@@ -50,7 +50,7 @@ export const SellPanel = () => {
return (
<>
<div className="mb-2 rounded-xl bg-gray-100 p-4 dark:bg-zinc-900">
<div className="mb-2 rounded-xl bg-foreground/5 p-4">
<div className="text-sm">Sell</div>
<AmountInput
@@ -83,7 +83,7 @@ export const SellPanel = () => {
</div>
</div>
<div className="mb-4 rounded-xl bg-gray-100 p-4 dark:bg-zinc-900">
<div className="mb-4 rounded-xl bg-foreground/5 p-4">
<div className="text-sm">Buy</div>
<AmountInput

View File

@@ -1,5 +1,5 @@
import { useAddress } from '@/app/(creator-fi)/hooks/useAddress'
import { useSpace } from '@/app/(creator-fi)/hooks/useSpace'
import { useSpaceContext } from '@/components/SpaceContext'
import { spaceAbi } from '@/lib/abi'
import { Address } from 'viem'
import { useReadContract } from 'wagmi'
@@ -11,7 +11,7 @@ export const formatAmount = (value: string): string => {
export function useSpaceTokenBalance() {
const address = useAddress()
const { space } = useSpace()
const space = useSpaceContext()
return useReadContract({
address: space.address as Address,
abi: spaceAbi,

View File

@@ -1,5 +1,7 @@
'use client'
import { useQueryEthBalance } from '@/app/(creator-fi)/hooks/useEthBalance'
import { useSpaceContext } from '@/components/SpaceContext'
import {
Dialog,
DialogContent,
@@ -7,8 +9,6 @@ import {
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog'
import { useQueryEthBalance } from '@/app/(creator-fi)/hooks/useEthBalance'
import { useSpace } from '@/app/(creator-fi)/hooks/useSpace'
import { MemberForm } from './MemberForm'
import { useMemberDialog } from './useMemberDialog'
@@ -16,7 +16,7 @@ interface Props {}
export function MemberDialog({}: Props) {
const { isOpen, setIsOpen } = useMemberDialog()
const { space } = useSpace()
const space = useSpaceContext()
useQueryEthBalance()
return (
@@ -26,8 +26,8 @@ export function MemberDialog({}: Props) {
<DialogHeader>
<DialogTitle>Subscription</DialogTitle>
<div className="text-sm text-neutral-600">
Subscribe to become a member of the <span className="font-bold">{space.name}</span>{' '}
space.
Subscribe to become a member of the{' '}
<span className="font-bold">{space.name}</span> space.
</div>
</DialogHeader>
<MemberForm space={space} />

View File

@@ -1,10 +1,10 @@
'use client'
import { forwardRef } from 'react'
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'
import { useEthBalance } from '@/app/(creator-fi)/hooks/useEthBalance'
import { useSpace } from '@/app/(creator-fi)/hooks/useSpace'
import { useTokenBalance } from '@/app/(creator-fi)/hooks/useTokenBalance'
import { useSpaceContext } from '@/components/SpaceContext'
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'
import { precision } from '@/lib/math'
interface Props {
@@ -14,7 +14,7 @@ interface Props {
export const TokenSelect = forwardRef<HTMLDivElement, Props>(
function TokenSelect({ value, onChange }, ref) {
const { space } = useSpace()
const space = useSpaceContext()
const { ethBalance } = useEthBalance()
const { data } = useTokenBalance()
const isEth = value === 'ETH'

View File

@@ -1,6 +1,6 @@
import React, { forwardRef } from 'react'
import { matchNumber } from '@/lib/utils'
import { Input } from '../../../components/ui/input'
import { Input } from '@/components/ui/input'
export interface NumberInputProps
extends Omit<

View File

@@ -1,8 +1,8 @@
'use client'
import { ReactNode, useState } from 'react'
import { useSpace } from '@/app/(creator-fi)/hooks/useSpace'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { useSpaceContext } from '../../../components/SpaceContext'
enum TabTypes {
Holders = 'Holders',
@@ -12,11 +12,8 @@ enum TabTypes {
export const dynamic = 'force-static'
export default function Layout({ children }: { children: ReactNode }) {
const { space } = useSpace()
const [type, setType] = useState(TabTypes.Trades)
if (!space) return null
return (
<div>
<div className="mx-auto mt-4 flex w-full flex-col gap-12 p-3 sm:w-full">

View File

@@ -1,14 +1,12 @@
'use client'
import { useSpace } from '@/app/(creator-fi)/hooks/useSpace'
import { useSpaceContext } from '../../../../components/SpaceContext'
import { MemberList } from '../../Space/MemberList'
export const dynamic = 'force-static'
// export const revalidate = 3600 * 24
export default function Page() {
const { space } = useSpace()
if (!space) return null
const space = useSpaceContext()
return <MemberList space={space}></MemberList>
}

View File

@@ -1,12 +1,12 @@
'use client'
import { useSpace } from '@/app/(creator-fi)/hooks/useSpace'
import { PlateEditor } from '@/components/editor/plate-editor'
import { useSpaceContext } from '@/components/SpaceContext'
export const dynamic = 'force-static'
export default function Page() {
const { space } = useSpace()
const space = useSpaceContext()
return <PlateEditor value={space.aboutJson} readonly />
}

View File

@@ -5,8 +5,8 @@ import { useForm } from 'react-hook-form'
import { NumberInput } from '@/app/(creator-fi)/components/NumberInput'
import { editorDefaultValue } from '@/app/(creator-fi)/constants'
import { useEthPrice } from '@/app/(creator-fi)/hooks/useEthPrice'
import { useSpace } from '@/app/(creator-fi)/hooks/useSpace'
import LoadingDots from '@/components/icons/loading-dots'
import { useSpaceContext } from '@/components/SpaceContext'
import { Button } from '@/components/ui/button'
import {
Form,
@@ -38,7 +38,7 @@ const FormSchema = z.object({
export function AddPlanForm() {
const [isLoading, setLoading] = useState(false)
const { setIsOpen } = useAddPlanDialog()
const { space } = useSpace()
const space = useSpaceContext()
const { ethPrice } = useEthPrice()
const wagmiConfig = useWagmiConfig()
const checkChain = useCheckChain()

View File

@@ -5,9 +5,9 @@ import { Plan } from '@/app/(creator-fi)/domains/Plan'
import { useAddress } from '@/app/(creator-fi)/hooks/useAddress'
import { useEthPrice } from '@/app/(creator-fi)/hooks/useEthPrice'
import { useMembers } from '@/app/(creator-fi)/hooks/useMembers'
import { useSpace } from '@/app/(creator-fi)/hooks/useSpace'
import { useSubscriptions } from '@/app/(creator-fi)/hooks/useSubscriptions'
import { PlateEditor } from '@/components/editor/plate-editor'
import { useSpaceContext } from '@/components/SpaceContext'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'
import { useConnectModal } from '@rainbow-me/rainbowkit'
@@ -23,7 +23,7 @@ export function PlanItem({ plan }: Props) {
const address = useAddress()
const { ethPrice } = useEthPrice()
const { setState } = useUpdatePlanDialog()
const { space } = useSpace()
const space = useSpaceContext()
const { isConnected } = useAccount()
const { openConnectModal } = useConnectModal()
const { subscriptions } = useSubscriptions()

View File

@@ -3,7 +3,7 @@
import { MemberDialog } from '@/app/(creator-fi)/components/MemberDialog/MemberDialog'
import { useAddress } from '@/app/(creator-fi)/hooks/useAddress'
import { usePlans } from '@/app/(creator-fi)/hooks/usePlans'
import { useSpace } from '@/app/(creator-fi)/hooks/useSpace'
import { useSpaceContext } from '@/components/SpaceContext'
import { AddPlanDialog } from './AddPlanDialog/AddPlanDialog'
import { PlanItem } from './PlanItem'
import { UpdatePlanDialog } from './UpdatePlanDialog/UpdatePlanDialog'
@@ -12,7 +12,7 @@ interface Props {}
export function PlanList({}: Props) {
const { plans, isLoading } = usePlans()
const { space } = useSpace()
const space = useSpaceContext()
const address = useAddress()
if (isLoading) return <div className="text-foreground/60">Loading...</div>

View File

@@ -6,9 +6,9 @@ import { NumberInput } from '@/app/(creator-fi)/components/NumberInput'
import { PlanStatus } from '@/app/(creator-fi)/domains/Plan'
import { useEthPrice } from '@/app/(creator-fi)/hooks/useEthPrice'
import { usePlans } from '@/app/(creator-fi)/hooks/usePlans'
import { useSpace } from '@/app/(creator-fi)/hooks/useSpace'
import { PlateEditor } from '@/components/editor/plate-editor'
import LoadingDots from '@/components/icons/loading-dots'
import { useSpaceContext } from '@/components/SpaceContext'
import { Button } from '@/components/ui/button'
import {
Form,
@@ -44,7 +44,7 @@ export function UpdatePlanForm() {
const [isLoading, setLoading] = useState(false)
const wagmiConfig = useWagmiConfig()
const { setIsOpen } = useUpdatePlanDialog()
const { space } = useSpace()
const space = useSpaceContext()
const { ethPrice } = useEthPrice()
const { plan } = useUpdatePlanDialog()
const { refetch } = usePlans()

View File

@@ -1,6 +1,6 @@
import { useMemo, useState } from 'react'
import { useSpace } from '@/app/(creator-fi)/hooks/useSpace'
import LoadingDots from '@/components/icons/loading-dots'
import { useSpaceContext } from '@/components/SpaceContext'
import { Button } from '@/components/ui/button'
import { WalletConnectButton } from '@/components/WalletConnectButton'
import { useCheckChain } from '@/hooks/useCheckChain'
@@ -25,7 +25,7 @@ export const StakingPanel = () => {
const { writeContractAsync } = useWriteContract()
const [tokenAmount, setTokenAmount] = useState<string>('')
const { data: tokenBalance } = useSpaceTokenBalance()
const { space } = useSpace()
const space = useSpaceContext()
const { isConnected } = useAccount()
const wagmiConfig = useWagmiConfig()
const checkChain = useCheckChain()

View File

@@ -1,12 +1,11 @@
'use client'
import { useSpace } from '@/app/(creator-fi)/hooks/useSpace'
import { useSpaceContext } from '@/components/SpaceContext'
import { SubscriptionRecordList } from '../../Space/SubscriptionRecordList'
export const dynamic = 'force-static'
export default function Page() {
const { space } = useSpace()
if (!space) return
const space = useSpaceContext()
return <SubscriptionRecordList space={space} />
}

View File

@@ -1,13 +1,20 @@
import { getSite, getSpace } from '@/lib/fetchers'
import { Transaction } from '../../Space/Transaction'
export const dynamic = 'force-static'
export const revalidate = 3600 * 24
export default function Page() {
export default async function Page() {
const site = await getSite()
if (!site?.spaceId) return null
const space = await getSpace(site.spaceId)
return (
<div className="flex h-[80vh] items-center justify-center">
<div className="w-full space-y-8 md:w-[480px]">
<div className="text-center text-4xl font-bold">Trade token</div>
<div className="text-center text-4xl font-bold">
Trade ${space.symbol} token
</div>
<Transaction />
</div>
</div>

View File

@@ -1,10 +1,10 @@
import { useSpaceContext } from '@/components/SpaceContext'
import { spaceAbi } from '@/lib/abi'
import { Address } from 'viem'
import { useReadContract } from 'wagmi'
import { useSpace } from './useSpace'
export function useContributors() {
const { space } = useSpace()
const space = useSpaceContext()
const { data: contributors = [], ...rest } = useReadContract({
address: space.address as Address,
abi: spaceAbi,

View File

@@ -1,11 +1,11 @@
import { useSpaceContext } from '@/components/SpaceContext'
import { RESPACE_SUBGRAPH_URL } from '@/lib/constants'
import { Holder } from '@/lib/types'
import { useQuery } from '@tanstack/react-query'
import { gql, request } from 'graphql-request'
import { useSpace } from './useSpace'
export function useHolders() {
const { space } = useSpace()
const space = useSpaceContext()
const query = gql`
query getHolders($spaceAddress: String!) {

View File

@@ -1,10 +1,10 @@
import { useSpaceContext } from '@/components/SpaceContext'
import { spaceAbi } from '@/lib/abi'
import { Address } from 'viem'
import { useReadContract } from 'wagmi'
import { useSpace } from './useSpace'
export function useMembers() {
const { space } = useSpace()
const space = useSpaceContext()
const { data: members = [], ...rest } = useReadContract({
address: space.address as Address,
abi: spaceAbi,

View File

@@ -1,4 +1,5 @@
import { Plan, PlanInfo } from '@/app/(creator-fi)/domains/Plan'
import { useSpaceContext } from '@/components/SpaceContext'
import { useWagmiConfig } from '@/hooks/useWagmiConfig'
import { spaceAbi } from '@/lib/abi'
import { isIPFSCID } from '@/lib/utils'
@@ -9,7 +10,7 @@ import { IPFS_GATEWAY } from '../constants'
import { useSpace } from './useSpace'
export function usePlans() {
const { space } = useSpace()
const space = useSpaceContext()
const wagmiConfig = useWagmiConfig()
const { data: plans = [], ...rest } = useQuery({
queryKey: ['plans', space.address],

View File

@@ -1,10 +1,10 @@
import { useSpaceContext } from '@/components/SpaceContext'
import { spaceAbi } from '@/lib/abi'
import { Address } from 'viem'
import { useReadContract } from 'wagmi'
import { useSpace } from './useSpace'
export function useShareOrders() {
const { space } = useSpace()
const space = useSpaceContext()
const { data: orders = [], ...rest } = useReadContract({
address: space.address as Address,
abi: spaceAbi,

View File

@@ -20,7 +20,6 @@ export function useSpace() {
export function useQuerySpace() {
const spaceId = getSpaceId()
console.log('=========spaceId:', spaceId)
const { data, ...rest } = useQuery({
queryKey: ['space'],

View File

@@ -1,11 +1,11 @@
import { useSpaceContext } from '@/components/SpaceContext'
import { RESPACE_SUBGRAPH_URL } from '@/lib/constants'
import { SubscriptionRecord, Trade } from '@/lib/types'
import { useQuery } from '@tanstack/react-query'
import { gql, request } from 'graphql-request'
import { useSpace } from './useSpace'
export function useSubscriptionRecords() {
const { space } = useSpace()
const space = useSpaceContext()
const query = gql`
query getSubscriptionRecords($spaceAddress: String!) {

View File

@@ -1,11 +1,12 @@
import { Subscription } from '@/app/(creator-fi)/domains/Subscription'
import { useSpaceContext } from '@/components/SpaceContext'
import { spaceAbi } from '@/lib/abi'
import { Address } from 'viem'
import { useReadContract } from 'wagmi'
import { useSpace } from './useSpace'
export function useSubscriptions() {
const { space } = useSpace()
const space = useSpaceContext()
const { data = [], ...rest } = useReadContract({
address: space.address as Address,
abi: spaceAbi,

View File

@@ -1,15 +1,15 @@
import { EthBalance } from '@/app/(creator-fi)/domains/EthBalance'
import { useSpaceContext } from '@/components/SpaceContext'
import { spaceAbi } from '@/lib/abi'
import { atom } from 'jotai'
import { Address } from 'viem'
import { useReadContract } from 'wagmi'
import { useAddress } from './useAddress'
import { useSpace } from './useSpace'
export const ethBalanceAtom = atom<EthBalance>({} as EthBalance)
export function useTokenBalance() {
const { space } = useSpace()
const space = useSpaceContext()
const address = useAddress()
const res = useReadContract({
address: space.address as Address,

View File

@@ -1,11 +1,11 @@
import { useSpaceContext } from '@/components/SpaceContext'
import { RESPACE_SUBGRAPH_URL } from '@/lib/constants'
import { Trade } from '@/lib/types'
import { useQuery } from '@tanstack/react-query'
import { gql, request } from 'graphql-request'
import { useSpace } from './useSpace'
export function useTrades() {
const { space } = useSpace()
const space = useSpaceContext()
const query = gql`
query getTrades($spaceAddress: String!) {

View File

@@ -1,3 +1,4 @@
import { useSpaceContext } from '@/components/SpaceContext'
import { spaceAbi } from '@/lib/abi'
import { Address } from 'viem'
import { useReadContract } from 'wagmi'
@@ -13,7 +14,7 @@ export type Vesting = {
}
export function useVestings() {
const { space } = useSpace()
const space = useSpaceContext()
const { data: vestings = [], ...rest } = useReadContract({
address: space.address as Address,
abi: spaceAbi,

View File

@@ -1,17 +1,27 @@
'use client'
import { getSite, getSpace } from '@/lib/fetchers'
import { StoreProvider } from '@/store'
import { Toaster } from 'sonner'
import { SpaceProvider } from '../../components/SpaceContext'
import { CreatorFiLayout } from './CreatorFiLayout'
export default function Layout({ children }: { children: React.ReactNode }) {
export default async function Layout({
children,
}: {
children: React.ReactNode
}) {
const site = await getSite()
if (!site?.spaceId) return null
const space = await getSpace(site.spaceId)
return (
<div className="min-h-screen bg-foreground/5">
<StoreProvider>
<Toaster className="dark:hidden" />
<Toaster theme="dark" className="hidden dark:block" />
<CreatorFiLayout>{children}</CreatorFiLayout>
</StoreProvider>
<SpaceProvider space={space}>
<StoreProvider>
<Toaster className="dark:hidden" />
<Toaster theme="dark" className="hidden dark:block" />
<CreatorFiLayout space={space}>{children}</CreatorFiLayout>
</StoreProvider>
</SpaceProvider>
</div>
)
}

View File

@@ -25,8 +25,8 @@ export function PostActions({ post, receivers }: Props) {
<TippersDialog post={post} receivers={receivers} />
</div>
<div className="flex items-center gap-1">
{typeof post.creationId === 'number' && <CollectButton post={post} />}
<TipTokenButton post={post} receivers={receivers} />
{typeof post.creationId === 'number' && <CollectButton post={post} />}
</div>
</div>
)

View File

@@ -11,6 +11,7 @@ import {
DialogTitle,
} from '@/components/ui/dialog'
import { useAllocationCap } from '@/hooks/useAllocationCap'
import { useTipInfo } from '@/hooks/useTipInfo'
import { useTipStats } from '@/hooks/useTipStats'
import { useWagmiConfig } from '@/hooks/useWagmiConfig'
import { tipAbi } from '@/lib/abi'
@@ -22,6 +23,7 @@ import { toFloorFixed } from '@/lib/utils'
import { Post } from '@penxio/types'
import { readContract, waitForTransactionReceipt } from '@wagmi/core'
import { SetStateAction } from 'jotai'
import { useParams } from 'next/navigation'
import pRetry, { AbortError } from 'p-retry'
import { toast } from 'sonner'
import { Address } from 'viem'
@@ -49,6 +51,7 @@ export function TipTokenDialog({
receivers,
}: Props) {
const [amount, setAmount] = useState('1')
const params = useParams()
const { address = '' } = useAccount()
let { data: executionFee } = useReadContract({
address: addressMap.Tip,
@@ -56,7 +59,8 @@ export function TipTokenDialog({
functionName: 'executionFee',
})
const { refetch: refetchTipInfo } = useTipStats(receivers)
const { refetch: refetchTipStats } = useTipStats(receivers)
const { refetch: refetchTipInfo } = useTipInfo(post.id)
const { data: data, isLoading: isLoadingCap, refetch } = useAllocationCap()
const { writeContractAsync } = useWriteContract()
@@ -122,7 +126,11 @@ export function TipTokenDialog({
toast.success('Tip $PEN successfully')
await refetch()
setTimeout(() => {
refetchTipInfo()
if (params?.slug) {
refetchTipInfo()
} else {
refetchTipStats()
}
}, 4000)
} catch (error) {
console.log('====error>>>:', error)

View File

@@ -2,9 +2,12 @@
import { Dispatch, SetStateAction } from 'react'
import { Skeleton } from '@/components/ui/skeleton'
import { useTipInfo } from '@/hooks/useTipInfo'
import { useTipStats } from '@/hooks/useTipStats'
import { precision } from '@/lib/math'
import { Post } from '@penxio/types'
import { DollarSign } from 'lucide-react'
import { useParams } from 'next/navigation'
interface Props {
post: Post
@@ -12,7 +15,31 @@ interface Props {
setIsOpen: Dispatch<SetStateAction<boolean>>
}
export function TippedAmount({ post, receivers, setIsOpen }: Props) {
export function TippedAmount(props: Props) {
const params = useParams()
const slug = params.slug
if (slug) {
return <TippedAmountInPostDetail {...props} />
}
return <TippedAmountInHome {...props} />
}
export function TippedAmountInPostDetail({ post, setIsOpen }: Props) {
const { data, isLoading } = useTipInfo(post.id)
if (isLoading) return <Skeleton className="h-6 w-12" />
return (
<div
className="flex items-center justify-between text-foreground gap-1 cursor-pointer opacity-70 hover:opacity-100"
onClick={() => setIsOpen(true)}
>
<DollarSign size={18} />
<div> {!data ? '0' : precision.toDecimal(data?.totalAmount)}</div>
</div>
)
}
export function TippedAmountInHome({ post, receivers, setIsOpen }: Props) {
const { data = [], isLoading } = useTipStats(receivers)
if (isLoading) return <Skeleton className="h-6 w-12" />
const item = data.find((i) => i.uri === post.id)
@@ -22,7 +49,8 @@ export function TippedAmount({ post, receivers, setIsOpen }: Props) {
className="flex items-center justify-between text-foreground gap-1 cursor-pointer opacity-70 hover:opacity-100"
onClick={() => setIsOpen(true)}
>
<span className="i-[ri--quill-pen-ai-fill]"></span>
{/* <span className="i-[ri--quill-pen-ai-fill]"></span> */}
<DollarSign size={18} />
<div> {!data ? '0' : precision.toDecimal(item?.totalAmount)}</div>
</div>
)

View File

@@ -0,0 +1,26 @@
'use client'
import { createContext, PropsWithChildren, useContext } from 'react'
import { SpaceType } from '@/lib/types'
import { Space } from '../app/(creator-fi)/domains/Space'
export const SpaceContext = createContext({} as Space)
interface Props {
space: SpaceType
}
export const SpaceProvider = ({
space,
children,
}: PropsWithChildren<Props>) => {
return (
<SpaceContext.Provider value={new Space(space)}>
{children}
</SpaceContext.Provider>
)
}
export function useSpaceContext() {
return useContext(SpaceContext)
}

View File

@@ -39,7 +39,7 @@ const DialogContent = React.forwardRef<
<DialogPrimitive.Content
ref={ref}
className={cn(
'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
'fixed left-[50%] top-[50%] z-50 grid max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] rounded-lg w-11/12',
className,
)}
{...props}

View File

@@ -8,7 +8,7 @@ type TipInfo = {
uri: string
}
export function useTipStats(receivers: string[]) {
export function useTipStats(receivers: string[] = []) {
const query = gql`
query geTips($receivers: [Bytes]) {
tips(where: { receiver_in: $receivers }) {
@@ -30,7 +30,7 @@ export function useTipStats(receivers: string[]) {
},
})
},
enabled: !!receivers,
enabled: receivers.length > 0,
})
return {

View File

@@ -1,7 +1,9 @@
import prisma from '@/lib/prisma'
import { getSite as getSiteInfo } from '@/server/lib/getSite'
import ky from 'ky'
import { unstable_cache } from 'next/cache'
import { PostStatus } from './constants'
import { PostStatus, RESPACE_BASE_URI } from './constants'
import { SpaceType } from './types'
import { getUrl } from './utils'
export async function getSite() {
@@ -113,3 +115,19 @@ export async function getTagWithPost(name: string) {
// },
// )()
// }
export async function getSpace(spaceId: string) {
return await unstable_cache(
async () => {
const response = await ky
.get(RESPACE_BASE_URI + `/api/get-space?address=${spaceId}`)
.json<SpaceType>()
return response
},
[`space-${spaceId}`],
{
revalidate: 10,
tags: [`space-${spaceId}`],
},
)()
}

View File

@@ -23,8 +23,8 @@ module.exports = {
'penx-theme-micro',
'penx-theme-card',
'penx-theme-minimal',
'penx-theme-photo',
'penx-theme-garden',
'penx-theme-photo',
],
images: {