feat: create theme-ui

This commit is contained in:
0xzio
2024-12-17 13:18:38 +08:00
parent 0f03210fa1
commit b87bf034bc
157 changed files with 250 additions and 2000 deletions

View File

@@ -1,5 +1,3 @@
import { ContentRender } from '@/components/ContentRender/ContentRender'
import { PostActions } from '@/components/PostActions/PostActions'
import { getPosts, getSite } from '@/lib/fetchers'
import { loadTheme } from '@/themes/theme-loader'
import { Metadata, ResolvingMetadata } from 'next'
@@ -26,13 +24,5 @@ export default async function HomePage() {
return <div>Theme not found</div>
}
return (
<HomePage
posts={posts}
authors={[]}
site={site}
ContentRender={ContentRender}
PostActions={PostActions}
/>
)
return <HomePage posts={posts} authors={[]} site={site} />
}

View File

@@ -1,7 +1,4 @@
import { ReactNode } from 'react'
import { Airdrop } from '@/components/Airdrop/Airdrop'
import { ModeToggle } from '@/components/ModeToggle'
import { Profile } from '@/components/Profile/Profile'
import { getSite } from '@/lib/fetchers'
import { loadTheme } from '@/themes/theme-loader'
@@ -16,17 +13,5 @@ export default async function RootLayout({
}) {
const site = await getSite()
const { SiteLayout } = loadTheme(site.themeName)
return (
<SiteLayout
site={site}
Logo={null}
ModeToggle={ModeToggle}
MobileNav={null}
ConnectButton={Profile}
Airdrop={Airdrop}
>
{children}
</SiteLayout>
)
return <SiteLayout site={site}>{children}</SiteLayout>
}

View File

@@ -1,7 +1,5 @@
'use client'
import { ContentRender } from '@/components/ContentRender/ContentRender'
import { PostActions } from '@/components/PostActions/PostActions'
import { SubscriptionInSession } from '@/lib/types'
import useSession from '@/lib/useSession'
import { cn } from '@/lib/utils'
@@ -58,8 +56,6 @@ export function PaidContent({ postId, post, next, prev }: Props) {
readable={false}
next={next}
prev={prev}
PostActions={PostActions}
ContentRender={ContentRender}
className="min-h-[auto]"
/>
@@ -83,8 +79,6 @@ export function PaidContent({ postId, post, next, prev }: Props) {
readable={hasMembership}
next={next}
prev={prev}
PostActions={PostActions}
ContentRender={ContentRender}
className={cn(!hasMembership && 'min-h-[auto]')}
/>
{!hasMembership && (

View File

@@ -1,5 +1,3 @@
import { ContentRender } from '@/components/ContentRender/ContentRender'
import { PostActions } from '@/components/PostActions/PostActions'
import { getPost, getPosts, getSite } from '@/lib/fetchers'
import { GateType } from '@/lib/types'
import { Post } from '@/server/db/schema'
@@ -70,8 +68,6 @@ export default async function Page({ params }: { params: { slug: string[] } }) {
readable
next={next}
prev={prev}
PostActions={PostActions}
ContentRender={ContentRender}
/>
</>
)

View File

@@ -1,7 +1,7 @@
import { useState } from 'react'
import { useQueryEthBalance } from '@/app/(creator-fi)/hooks/useEthBalance'
import { useTrades } from '@/app/(creator-fi)/hooks/useTrades'
import LoadingDots from '@/components/icons/loading-dots'
import { LoadingDots } from '@/components/icons/loading-dots'
import { useSpaceContext } from '@/components/SpaceContext'
import { Button } from '@/components/ui/button'
import { WalletConnectButton } from '@/components/WalletConnectButton'

View File

@@ -3,7 +3,7 @@ import { Space } from '@/app/(creator-fi)/domains/Space'
import { useAddress } from '@/app/(creator-fi)/hooks/useAddress'
import { useQueryEthBalance } from '@/app/(creator-fi)/hooks/useEthBalance'
import { useTrades } from '@/app/(creator-fi)/hooks/useTrades'
import LoadingDots from '@/components/icons/loading-dots'
import { LoadingDots } from '@/components/icons/loading-dots'
import { Button } from '@/components/ui/button'
import { WalletConnectButton } from '@/components/WalletConnectButton'
import { useCheckChain } from '@/lib/hooks/useCheckChain'

View File

@@ -6,7 +6,7 @@ import { SubscriptionType } from '@/app/(creator-fi)/constants'
import { Space } from '@/app/(creator-fi)/domains/Space'
import { useQueryEthBalance } from '@/app/(creator-fi)/hooks/useEthBalance'
import { useTokenBalance } from '@/app/(creator-fi)/hooks/useTokenBalance'
import LoadingDots from '@/components/icons/loading-dots'
import { LoadingDots } from '@/components/icons/loading-dots'
import { ProfileAvatar } from '@/components/Profile/ProfileAvatar'
import { Button } from '@/components/ui/button'
import {

View File

@@ -5,7 +5,7 @@ import { useForm } from 'react-hook-form'
import { NumberInput } from '@/app/(creator-fi)/components/NumberInput'
import { editorDefaultValue } from '@/app/(creator-fi)/constants'
import { usePlans } from '@/app/(creator-fi)/hooks/usePlans'
import LoadingDots from '@/components/icons/loading-dots'
import { LoadingDots } from '@/components/icons/loading-dots'
import { useSpaceContext } from '@/components/SpaceContext'
import { Button } from '@/components/ui/button'
import {

View File

@@ -6,7 +6,7 @@ import { NumberInput } from '@/app/(creator-fi)/components/NumberInput'
import { PlanStatus } from '@/app/(creator-fi)/domains/Plan'
import { usePlans } from '@/app/(creator-fi)/hooks/usePlans'
import { PlateEditor } from '@/components/editor/plate-editor'
import LoadingDots from '@/components/icons/loading-dots'
import { LoadingDots } from '@/components/icons/loading-dots'
import { useSpaceContext } from '@/components/SpaceContext'
import { Button } from '@/components/ui/button'
import {

View File

@@ -1,5 +1,5 @@
import { useMemo, useState } from 'react'
import LoadingDots from '@/components/icons/loading-dots'
import { LoadingDots } from '@/components/icons/loading-dots'
import { useSpaceContext } from '@/components/SpaceContext'
import { Button } from '@/components/ui/button'
import { WalletConnectButton } from '@/components/WalletConnectButton'

View File

@@ -2,7 +2,7 @@
import { ReactNode, useEffect, useState } from 'react'
import { CreationDialog } from '@/components/CreationDialog/CreationDialog'
import LoadingDots from '@/components/icons/loading-dots'
import { LoadingDots } from '@/components/icons/loading-dots'
import { useQueryEthBalance } from '@/lib/hooks/useEthBalance'
import { useQueryEthPrice } from '@/lib/hooks/useEthPrice'
import { SIDEBAR_WIDTH } from '@/lib/constants'

View File

@@ -1,7 +1,7 @@
'use client'
import { DeleteConfirmDialog } from '@/components/DeleteConfirmDialog'
import LoadingDots from '@/components/icons/loading-dots'
import { LoadingDots } from '@/components/icons/loading-dots'
import { Separator } from '@/components/ui/separator'
import { extractErrorMessage } from '@/lib/extractErrorMessage'
import { trpc } from '@/lib/trpc'

View File

@@ -1,4 +1,4 @@
import LoadingDots from '@/components/icons/loading-dots'
import { LoadingDots } from '@/components/icons/loading-dots'
import { Avatar, AvatarImage } from '@/components/ui/avatar'
import { Button } from '@/components/ui/button'
import { Separator } from '@/components/ui/separator'

View File

@@ -1,6 +1,6 @@
import { useEffect, useState } from 'react'
import { IconGoogle } from '@/components/icons/IconGoogle'
import LoadingDots from '@/components/icons/loading-dots'
import { LoadingDots } from '@/components/icons/loading-dots'
import { Button } from '@/components/ui/button'
import {
GOOGLE_CLIENT_ID,

View File

@@ -1,7 +1,7 @@
'use client'
import { PropsWithChildren, useEffect } from 'react'
import LoadingDots from '@/components/icons/loading-dots'
import { LoadingDots } from '@/components/icons/loading-dots'
import { loadPost, postAtom, usePost } from '@/lib/hooks/usePost'
import { usePostLoading } from '@/lib/hooks/usePostLoading'
import { store } from '@/lib/store'

View File

@@ -3,7 +3,7 @@
import { useForm } from 'react-hook-form'
import { PlateEditor } from '@/components/editor/plate-editor'
import { FileUpload } from '@/components/FileUpload'
import LoadingDots from '@/components/icons/loading-dots'
import { LoadingDots } from '@/components/icons/loading-dots'
import { Button } from '@/components/ui/button'
import {
Form,

View File

@@ -1,7 +1,7 @@
'use client'
import { useForm } from 'react-hook-form'
import LoadingDots from '@/components/icons/loading-dots'
import { LoadingDots } from '@/components/icons/loading-dots'
import { Button } from '@/components/ui/button'
import {
Form,

View File

@@ -1,6 +1,6 @@
'use client'
import LoadingDots from '@/components/icons/loading-dots'
import { LoadingDots } from '@/components/icons/loading-dots'
import { trpc } from '@/lib/trpc'
import { AppearanceSettingForm } from './AppearanceSettingForm'

View File

@@ -1,5 +1,5 @@
import { useState } from 'react'
import LoadingDots from '@/components/icons/loading-dots'
import { LoadingDots } from '@/components/icons/loading-dots'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { extractErrorMessage } from '@/lib/extractErrorMessage'

View File

@@ -1,6 +1,6 @@
import { useState } from 'react'
import { DeleteConfirmDialog } from '@/components/DeleteConfirmDialog'
import LoadingDots from '@/components/icons/loading-dots'
import { LoadingDots } from '@/components/icons/loading-dots'
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
import {
Select,

View File

@@ -1,7 +1,7 @@
'use client'
import { IconGoogle } from '@/components/icons/IconGoogle'
import LoadingDots from '@/components/icons/loading-dots'
import { LoadingDots } from '@/components/icons/loading-dots'
import { Avatar, AvatarFallback } from '@/components/ui/avatar'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'

View File

@@ -1,7 +1,7 @@
'use client'
import { useEffect } from 'react'
import LoadingDots from '@/components/icons/loading-dots'
import { LoadingDots } from '@/components/icons/loading-dots'
import { useMyAccounts } from '@/lib/hooks/useMyAccounts'
import { trpc } from '@/lib/trpc'
import { ProviderType } from '@/lib/types'

View File

@@ -2,7 +2,7 @@
import { useEffect, useState } from 'react'
import { IconGoogle } from '@/components/icons/IconGoogle'
import LoadingDots from '@/components/icons/loading-dots'
import { LoadingDots } from '@/components/icons/loading-dots'
import { Button } from '@/components/ui/button'
import {
GOOGLE_CLIENT_ID,

View File

@@ -1,6 +1,6 @@
'use client'
import LoadingDots from '@/components/icons/loading-dots'
import { LoadingDots } from '@/components/icons/loading-dots'
import { Button } from '@/components/ui/button'
import { useMyAccounts } from '@/lib/hooks/useMyAccounts'
import { extractErrorMessage } from '@/lib/extractErrorMessage'

View File

@@ -1,6 +1,6 @@
'use client'
import LoadingDots from '@/components/icons/loading-dots'
import { LoadingDots } from '@/components/icons/loading-dots'
import { trpc } from '@/lib/trpc'
import { SiteSettingForm } from './SiteSettingForm'

View File

@@ -1,7 +1,7 @@
'use client'
import { useForm } from 'react-hook-form'
import LoadingDots from '@/components/icons/loading-dots'
import { LoadingDots } from '@/components/icons/loading-dots'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'
import {

View File

@@ -1,6 +1,6 @@
'use client'
import LoadingDots from '@/components/icons/loading-dots'
import { LoadingDots } from '@/components/icons/loading-dots'
import { trpc } from '@/lib/trpc'
import { SocialSettingForm } from './SocialSettingForm'

View File

@@ -1,7 +1,7 @@
'use client'
import { useForm } from 'react-hook-form'
import LoadingDots from '@/components/icons/loading-dots'
import { LoadingDots } from '@/components/icons/loading-dots'
import { Button } from '@/components/ui/button'
import {
Form,

View File

@@ -1,6 +1,6 @@
'use client'
import LoadingDots from '@/components/icons/loading-dots'
import { LoadingDots } from '@/components/icons/loading-dots'
import { trpc } from '@/lib/trpc'
import { Web3SettingForm } from './Web3SettingForm'

View File

@@ -0,0 +1 @@
export * from './ContentRender'

View File

@@ -2,7 +2,7 @@
import { useEffect, useState } from 'react'
import { useForm } from 'react-hook-form'
import LoadingDots from '@/components/icons/loading-dots'
import { LoadingDots } from '@/components/icons/loading-dots'
import { Button } from '@/components/ui/button'
import {
Form,

View File

@@ -1,7 +1,7 @@
'use client'
import { useState } from 'react'
import LoadingDots from '@/components/icons/loading-dots'
import { LoadingDots } from '@/components/icons/loading-dots'
import {
Dialog,
DialogContent,

View File

@@ -21,7 +21,7 @@ import {
import { extractErrorMessage } from '@/lib/extractErrorMessage'
import { Trash2 } from 'lucide-react'
import { toast } from 'sonner'
import LoadingDots from './icons/loading-dots'
import { LoadingDots } from './icons/loading-dots'
import { Button } from './ui/button'
interface Props {

View File

@@ -1,5 +1,5 @@
import { forwardRef, useRef, useState } from 'react'
import LoadingDots from '@/components/icons/loading-dots'
import { LoadingDots } from '@/components/icons/loading-dots'
import { uploadFile } from '@/lib/uploadFile'
import { getUrl } from '@/lib/utils'
import { Edit3 } from 'lucide-react'

View File

@@ -2,7 +2,7 @@
import { PropsWithChildren, useEffect, useState } from 'react'
import { IconGoogle } from '@/components/icons/IconGoogle'
import LoadingDots from '@/components/icons/loading-dots'
import { LoadingDots } from '@/components/icons/loading-dots'
import { Button, ButtonProps } from '@/components/ui/button'
import { GOOGLE_CLIENT_ID, GOOGLE_OAUTH_REDIRECT_URI } from '@/lib/constants'
import { cn } from '@/lib/utils'

View File

@@ -3,7 +3,7 @@
import { useCallback, useEffect, useState } from 'react'
import { IconGoogle } from '@/components/icons/IconGoogle'
import LoadingCircle from '@/components/icons/loading-circle'
import LoadingDots from '@/components/icons/loading-dots'
import { LoadingDots } from '@/components/icons/loading-dots'
import {
Dialog,
DialogContent,

View File

@@ -1,5 +1,5 @@
import { forwardRef, useRef, useState } from 'react'
import LoadingDots from '@/components/icons/loading-dots'
import { LoadingDots } from '@/components/icons/loading-dots'
import { api, trpc } from '@/lib/trpc'
import { uploadFile } from '@/lib/uploadFile'
import { getUrl, isIPFSCID } from '@/lib/utils'

View File

@@ -1,5 +1,5 @@
import { forwardRef, useRef, useState } from 'react'
import LoadingDots from '@/components/icons/loading-dots'
import { LoadingDots } from '@/components/icons/loading-dots'
import { api } from '@/lib/trpc'
import { uploadFile } from '@/lib/uploadFile'
import { Post } from '@/server/db/schema'

View File

@@ -3,7 +3,7 @@
import { useEffect } from 'react'
import { useForm } from 'react-hook-form'
import { FileUpload } from '@/components/FileUpload'
import LoadingDots from '@/components/icons/loading-dots'
import { LoadingDots } from '@/components/icons/loading-dots'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import {

View File

@@ -9,7 +9,7 @@ import { cn } from '@/lib/utils'
import { store } from '@/lib/store'
import { PopoverClose } from '@radix-ui/react-popover'
import { useParams, usePathname } from 'next/navigation'
import LoadingDots from './icons/loading-dots'
import { LoadingDots } from './icons/loading-dots'
import { useSiteContext } from './SiteContext'
import { Button } from './ui/button'
import { Label } from './ui/label'

View File

@@ -14,5 +14,3 @@ export const LoadingDots = ({ className }: LoadingDotsProps) => {
</span>
)
}
export default LoadingDots

View File

@@ -1,6 +1,6 @@
import { forwardRef, useRef, useState } from 'react'
import { ITitleElement } from '@/components/editor/plugins/title-plugin'
import LoadingDots from '@/components/icons/loading-dots'
import { LoadingDots } from '@/components/icons/loading-dots'
import { uploadFile } from '@/lib/uploadFile'
import { cn, getUrl, isIPFSCID } from '@/lib/utils'
import { ImageIcon, X } from 'lucide-react'

View File

@@ -1,6 +1,6 @@
import { forwardRef, useRef, useState } from 'react'
import { ITitleElement } from '@/components/editor/plugins/title-plugin'
import LoadingDots from '@/components/icons/loading-dots'
import { LoadingDots } from '@/components/icons/loading-dots'
import { uploadFile } from '@/lib/uploadFile'
import { cn, getUrl, isIPFSCID } from '@/lib/utils'
import { PlateElementProps } from '@udecode/plate-common/react'

View File

@@ -1,6 +1,8 @@
'use client';
'use client'
import { useState } from 'react';
import { useState } from 'react'
import { LoadingDots } from '@/components/icons/loading-dots'
import { Button } from '@/components/ui/button'
import {
Dialog,
DialogContent,
@@ -9,22 +11,19 @@ import {
DialogTitle,
DialogTrigger,
} from '@/components/ui/dialog'
import { useDailyClaimCap } from '@/lib/hooks/useDailyClaimCap';
import { useWagmiConfig } from '@/lib/hooks/useWagmiConfig';
import { dailyClaimAbi } from '@/lib/abi';
import { addressMap } from '@/lib/address';
import { extractErrorMessage } from '@/lib/extractErrorMessage';
import useSession from '@/lib/useSession';
import { readContract, waitForTransactionReceipt } from '@wagmi/core';
import pRetry, { AbortError } from 'p-retry';
import { toast } from 'sonner';
import { useAccount, useReadContract, useWriteContract } from 'wagmi';
import LoadingDots from '../icons/loading-dots';
import { Button } from '../ui/button';
import { Skeleton } from '../ui/skeleton';
import { WalletConnectButton } from '../WalletConnectButton';
import { AirdropButton } from './AirdropButton';
import { Skeleton } from '@/components/ui/skeleton'
import { WalletConnectButton } from '@/components/WalletConnectButton'
import { dailyClaimAbi } from '@/lib/abi'
import { addressMap } from '@/lib/address'
import { extractErrorMessage } from '@/lib/extractErrorMessage'
import { useDailyClaimCap } from '@/lib/hooks/useDailyClaimCap'
import { useWagmiConfig } from '@/lib/hooks/useWagmiConfig'
import useSession from '@/lib/useSession'
import { readContract, waitForTransactionReceipt } from '@wagmi/core'
import pRetry, { AbortError } from 'p-retry'
import { toast } from 'sonner'
import { useAccount, useReadContract, useWriteContract } from 'wagmi'
import { AirdropButton } from './AirdropButton'
interface Props {}
@@ -161,4 +160,4 @@ function AuthenticatedContent() {
</div>
</>
)
}
}

View File

@@ -0,0 +1 @@
export * from './Airdrop'

View File

@@ -1,17 +1,18 @@
import { ReactNode } from 'react'
import { ModeToggle } from '@/components/ModeToggle'
import { cn } from '@/lib/utils'
import { Site } from '@penxio/types'
import SocialIcon from './social-icons'
import SocialIcon from './SocialIcon'
interface Props {
ModeToggle: () => ReactNode
site: Site
className?: string
}
export function Footer({ site, ModeToggle }: Props) {
export function Footer({ site, className }: Props) {
if (!site) return null
const socials = site.socials
return (
<footer className="mt-auto mb-8">
<footer className={cn('mt-auto mb-8', className)}>
<div className="mt-16 flex flex-col items-center">
<div className="mb-3 flex space-x-4 item-center">
<SocialIcon kind="mail" href={`mailto:${socials?.email}`} size={6} />

View File

@@ -6,7 +6,7 @@ interface Props {
className?: string
}
export default function PageTitle({ children, className }: Props) {
export function PageTitle({ children, className }: Props) {
return (
<h1
className={cn(

View File

@@ -1,8 +1,7 @@
/* eslint-disable jsx-a11y/anchor-is-valid */
'use client'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
import Link from './Link'
interface PaginationProps {
totalPages: number

View File

@@ -4,7 +4,6 @@ import { useState } from 'react'
import { useSiteContext } from '@/components/SiteContext'
import { Button } from '@/components/ui/button'
import { WalletConnectButton } from '@/components/WalletConnectButton'
import { AuthType } from '@/lib/types'
import useSession from '@/lib/useSession'
import { cn } from '@/lib/utils'
import { Post } from '@penxio/types'

View File

@@ -1,7 +1,7 @@
'use client'
import { Dispatch, SetStateAction, useState } from 'react'
import LoadingDots from '@/components/icons/loading-dots'
import { LoadingDots } from '@/components/icons/loading-dots'
import { useSiteContext } from '@/components/SiteContext'
import { Button } from '@/components/ui/button'
import {

View File

@@ -1,11 +1,11 @@
'use client'
import { Dispatch, SetStateAction } from 'react'
import { useSiteContext } from '@/components/SiteContext'
import { Skeleton } from '@/components/ui/skeleton'
import { useCreations } from '@/lib/hooks/useCreations'
import { Post } from '@penxio/types'
import { Bookmark } from 'lucide-react'
import { useSiteContext } from '../../SiteContext'
interface Props {
post: Post

View File

@@ -1,7 +1,7 @@
'use client'
import { useState } from 'react'
import LoadingDots from '@/components/icons/loading-dots'
import { LoadingDots } from '@/components/icons/loading-dots'
import { UserAvatar } from '@/components/UserAvatar'
import { trpc } from '@/lib/trpc'
import { getUserName } from '@/lib/utils'

View File

@@ -1,5 +1,5 @@
import { useState } from 'react'
import LoadingDots from '@/components/icons/loading-dots'
import { LoadingDots } from '@/components/icons/loading-dots'
import { Button } from '@/components/ui/button'
import { Textarea } from '@/components/ui/textarea'
import { WalletConnectButton } from '@/components/WalletConnectButton'

View File

@@ -10,12 +10,10 @@ import { TipTokenButton } from './TipToken/TipTokenButton'
interface Props {
post: Post
receivers: string[]
receivers?: string[]
}
export function PostActions({ post, receivers }: Props) {
const { data } = useTipStats(receivers)
export function PostActions({ post, receivers = [] }: Props) {
return (
<div className="flex items-center justify-between text-sm">
<div className="flex items-end gap-4">

View File

@@ -1,7 +1,7 @@
'use client'
import { Dispatch, useEffect, useState } from 'react'
import LoadingDots from '@/components/icons/loading-dots'
import { LoadingDots } from '@/components/icons/loading-dots'
import { Button } from '@/components/ui/button'
import {
Dialog,

View File

@@ -0,0 +1 @@
export * from './PostActions'

View File

@@ -1,18 +0,0 @@
'use client'
import React, { PropsWithChildren, useEffect, useState } from 'react'
export function ClientOnly({ children }: PropsWithChildren) {
// State / Props
const [hasMounted, setHasMounted] = useState(false)
// Hooks
useEffect(() => {
setHasMounted(true)
}, [])
// Render
if (!hasMounted) return null
return <>{children}</>
}

View File

@@ -1,47 +0,0 @@
import { ReactNode } from 'react'
import { Site } from '@penxio/types'
import SocialIcon from './social-icons'
interface Props {
ModeToggle: () => ReactNode
site: Site
}
export function Footer({ site, ModeToggle }: Props) {
if (!site) return null
const socials = site.socials
return (
<footer className="mt-auto mb-8">
<div className="mt-16 flex flex-col items-center">
<div className="mb-3 flex space-x-4">
<SocialIcon kind="mail" href={`mailto:${socials?.email}`} size={6} />
<SocialIcon kind="github" href={socials.github} size={6} />
<SocialIcon kind="facebook" href={socials.facebook} size={6} />
<SocialIcon kind="youtube" href={socials.youtube} size={6} />
<SocialIcon kind="linkedin" href={socials.linkedin} size={6} />
<SocialIcon kind="twitter" href={socials.twitter} size={6} />
<SocialIcon kind="x" href={socials.x} size={6} />
<SocialIcon kind="instagram" href={socials.instagram} size={6} />
<SocialIcon kind="threads" href={socials.threads} size={6} />
</div>
<div className="mb-2 flex space-x-2 text-sm item-center text-card-foreground/50">
<div className="flex items-center">{`© ${new Date().getFullYear()}`}</div>
<div className="flex items-center">{``}</div>
<div className="flex items-center">{site.name}</div>
<div className="flex items-center">{``}</div>
<div className="flex items-center gap-1">
Build with
<a
href="https://penx.io"
target="_blank"
className="text-brand-500"
>
PenX
</a>
</div>
<ModeToggle />
</div>
</div>
</footer>
)
}

View File

@@ -1,8 +1,10 @@
import { ReactNode, Suspense } from 'react'
import { Profile } from '@/components/Profile/Profile'
import { Airdrop } from '@/components/theme-ui/Airdrop'
import { cn } from '@/lib/utils'
import { Site } from '@penxio/types'
import { Merienda } from 'next/font/google'
import Link from './Link'
import Link from 'next/link'
const merienda = Merienda({
weight: ['400', '500', '600', '700'],
@@ -22,21 +24,9 @@ const headerNavLinksRight = [{ href: '/creator-fi', title: 'CreatorFi' }]
interface Props {
site: Site
Logo: () => ReactNode
ModeToggle: () => ReactNode
MobileNav: () => ReactNode
ConnectButton: () => ReactNode
Airdrop: () => ReactNode
}
export const Header = ({
site,
Logo,
ModeToggle,
MobileNav,
ConnectButton,
Airdrop,
}: Props) => {
export const Header = ({ site }: Props) => {
return (
<header className={cn('flex items-center w-full py-4 h-16 z-40')}>
<div className="flex-1 no-scrollbar hidden items-center space-x-4 overflow-x-auto sm:flex sm:space-x-6">
@@ -91,19 +81,10 @@ export const Header = ({
})}
</div>
{MobileNav && <MobileNav />}
{Airdrop && (
<div className="flex items-center">
<Airdrop />
</div>
)}
{ConnectButton && (
<Suspense fallback={<div></div>}>
<ConnectButton />
</Suspense>
)}
<div className="flex items-center">
<Airdrop />
</div>
<Profile></Profile>
</div>
</header>
)

View File

@@ -1,9 +0,0 @@
import NextImage, { ImageProps } from 'next/image'
const basePath = process.env.BASE_PATH
const Image = ({ src, ...rest }: ImageProps) => (
<NextImage src={`${basePath || ''}${src}`} {...rest} />
)
export default Image

View File

@@ -1,34 +0,0 @@
import { AnchorHTMLAttributes } from 'react'
import Link from 'next/link'
import type { LinkProps } from 'next/link'
const CustomLink = ({
href,
...rest
}: LinkProps & AnchorHTMLAttributes<HTMLAnchorElement>) => {
const isInternalLink = href && href.startsWith('/')
const isAnchorLink = href && href.startsWith('#')
const isProd = process.env.NODE_ENV === 'production'
if (isInternalLink) {
// if (isProd) href = href + '.html'
return <Link className="break-words" href={href} {...rest} />
}
if (isAnchorLink) {
return <a className="break-words" href={href} {...rest} />
}
return (
<a
className="break-words"
target="_blank"
rel="noopener noreferrer"
href={href}
{...rest}
/>
)
}
export default CustomLink

View File

@@ -1,20 +0,0 @@
import { ReactNode } from 'react'
import { cn } from '@/lib/utils'
interface Props {
children: ReactNode
className?: string
}
export default function PageTitle({ children, className }: Props) {
return (
<h1
className={cn(
'text-2xl font-semibold leading-9 tracking-tight sm:text-4xl sm:leading-10 md:text-4xl md:leading-13 mt-12 mb-8 text-foreground/90',
className,
)}
>
{children}
</h1>
)
}

View File

@@ -1,7 +1,6 @@
import { formatDate } from '@/lib/utils'
import { Post } from '@penxio/types'
import Image from './Image'
import Link from './Link'
import Link from 'next/link'
import Tag from './Tag'
interface PostItemProps {
@@ -18,11 +17,9 @@ export function PostItem({ post }: PostItemProps) {
className="object-cover w-full h-52 bg-foreground/10 rounded-lg overflow-hidden hover:scale-105 transition-all"
>
{!!post?.image && (
<Image
<img
src={post.image || ''}
alt=""
width={400}
height={400}
className="object-cover w-full h-52"
/>
)}

View File

@@ -1,5 +1,5 @@
import { Pagination } from '@/components/theme-ui/Pagination'
import { Post } from '@penxio/types'
import { Pagination } from './Pagination'
import { PostItem } from './PostItem'
interface PaginationProps {

View File

@@ -1,5 +1,5 @@
import { PageTitle } from '@/components/theme-ui/PageTitle'
import { Post, Tag } from '@penxio/types'
import PageTitle from './PageTitle'
import { PostList } from './PostList'
import { TagList } from './TagList'

View File

@@ -2,8 +2,8 @@
import { Tag } from '@penxio/types'
import { slug } from 'github-slugger'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
import Link from './Link'
interface PostListWithTagProps {
tags: Tag[]

View File

@@ -1,58 +0,0 @@
import {
Facebook,
Github,
Instagram,
Linkedin,
Mail,
Mastodon,
Threads,
Twitter,
X,
Youtube,
} from './icons'
const components = {
mail: Mail,
github: Github,
facebook: Facebook,
youtube: Youtube,
linkedin: Linkedin,
twitter: Twitter,
x: X,
mastodon: Mastodon,
threads: Threads,
instagram: Instagram,
}
type SocialIconProps = {
kind: keyof typeof components
href: string | undefined
size?: number
}
const SocialIcon = ({ kind, href, size = 8 }: SocialIconProps) => {
if (
!href ||
(kind === 'mail' &&
!/^mailto:[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(href))
)
return null
const SocialSvg = components[kind]
return (
<a
className="text-sm transition text-foreground/60 hover:text-foreground/70"
target="_blank"
rel="noopener noreferrer"
href={href}
>
<span className="sr-only">{kind}</span>
<SocialSvg
className={`fill-current text-foreground/70 hover:text-brand-500 dark:hover:text-brand-400 h-${size} w-${size}`}
/>
</a>
)
}
export default SocialIcon

View File

@@ -1,39 +1,19 @@
import { ReactNode } from 'react'
import { Footer } from '../components/Footer'
import { Footer } from '@/components/theme-ui/Footer'
import { Header } from '../components/Header'
import SectionContainer from '../components/SectionContainer'
interface Props {
site: any
Logo: () => ReactNode
ModeToggle: () => ReactNode
MobileNav: () => ReactNode
ConnectButton: () => ReactNode
Airdrop: () => ReactNode
children: ReactNode
}
export function SiteLayout({
children,
site,
Logo,
ModeToggle,
MobileNav,
ConnectButton,
Airdrop,
}: Props) {
export function SiteLayout({ children, site }: Props) {
return (
<SectionContainer>
<Header
site={site}
Logo={Logo}
ModeToggle={ModeToggle}
MobileNav={MobileNav}
ConnectButton={ConnectButton}
Airdrop={Airdrop}
/>
<Header site={site} />
<main className="mb-auto">{children}</main>
<Footer site={site} ModeToggle={ModeToggle} />
<Footer site={site} />
</SectionContainer>
)
}

View File

@@ -1,13 +1,12 @@
import { ContentRender } from '@/components/ContentRender'
import { PageTitle } from '@/components/theme-ui/PageTitle'
import { Site } from '@penxio/types'
import Image from '../components/Image'
import PageTitle from '../components/PageTitle'
interface Props {
site: Site
ContentRender: (props: { content: any[]; className?: string }) => JSX.Element
}
export function AboutPage({ site, ContentRender }: Props) {
export function AboutPage({ site }: Props) {
return (
<>
<div className="">
@@ -15,11 +14,9 @@ export function AboutPage({ site, ContentRender }: Props) {
<div className="">
<div className="flex flex-col items-center space-x-2 pt-8">
{site.logo && (
<Image
<img
src={site.logo}
alt="avatar"
width={192}
height={192}
className="h-48 w-48 rounded-full"
/>
)}

View File

@@ -1,5 +1,5 @@
import { Post } from '@penxio/types'
import PageTitle from '../components/PageTitle'
import { PageTitle } from '@/components/theme-ui/PageTitle'
import { PostList } from '../components/PostList'
interface Props {

View File

@@ -1,6 +1,6 @@
import { ContentRender } from '@/components/ContentRender'
import { Post, Site } from '@penxio/types'
import Image from '../components/Image'
import Link from '../components/Link'
import Link from 'next/link'
import { PostItem } from '../components/PostItem'
const POSTS_PER_PAGE = Number(process.env.NEXT_PUBLIC_POSTS_PER_PAGE || 10)
@@ -8,20 +8,17 @@ const POSTS_PER_PAGE = Number(process.env.NEXT_PUBLIC_POSTS_PER_PAGE || 10)
interface Props {
site: Site
posts: Post[]
ContentRender: (props: { content: any[]; className?: string }) => JSX.Element
}
export function HomePage({ posts = [], site, ContentRender }: Props) {
export function HomePage({ posts = [], site }: Props) {
return (
<div className="mt-12">
<div className="max-w-none mb-10 hover:text-foreground text-foreground/80">
<div className="flex flex-col items-center flex-shrink-0">
{site.logo && (
<Image
<img
src={site.logo}
alt="avatar"
width={192}
height={192}
className="h-48 w-48 rounded-full"
/>
)}

View File

@@ -1,10 +1,11 @@
import { ReactNode } from 'react'
import { ContentRender } from '@/components/ContentRender'
import { PageTitle } from '@/components/theme-ui/PageTitle'
import { PostActions } from '@/components/theme-ui/PostActions'
import { formatDate } from '@/lib/utils'
import { Post } from '@penxio/types'
import { ExternalLink } from 'lucide-react'
import Image from '../components/Image'
import Link from '../components/Link'
import PageTitle from '../components/PageTitle'
import Link from 'next/link'
import SectionContainer from '../components/SectionContainer'
interface LayoutProps {
@@ -13,18 +14,9 @@ interface LayoutProps {
className?: string
next?: Post
prev?: Post
PostActions?: (props: { post: Post; className?: string }) => JSX.Element
ContentRender?: (props: { content: any; className?: string }) => JSX.Element
}
export function PostDetail({
post,
PostActions,
className,
ContentRender,
next,
prev,
}: LayoutProps) {
export function PostDetail({ post, className, next, prev }: LayoutProps) {
return (
<SectionContainer className={className}>
<article className="mt-20 mx-auto w-full lg:max-w-3xl">
@@ -42,22 +34,20 @@ export function PostDetail({
</dl>
</div>
{PostActions && <PostActions post={post} />}
<PostActions post={post} />
</header>
{!!post.image && (
<Image
<img
src={post.image || ''}
alt=""
width={1000}
height={800}
className="object-cover w-full max-h-96 rounded-2xl"
/>
)}
<div className="grid-rows-[auto_1fr]">
<div className="prose max-w-none pb-8 dark:prose-invert">
{ContentRender && <ContentRender content={post.content} />}
<ContentRender content={post.content} />
</div>
{post.cid && (

View File

@@ -1,5 +1,5 @@
import { Tag } from '@penxio/types'
import PageTitle from '../components/PageTitle'
import { PageTitle } from '@/components/theme-ui/PageTitle'
import { TagList } from '../components/TagList'
interface Props {

View File

@@ -1,18 +0,0 @@
'use client'
import React, { PropsWithChildren, useEffect, useState } from 'react'
export function ClientOnly({ children }: PropsWithChildren) {
// State / Props
const [hasMounted, setHasMounted] = useState(false)
// Hooks
useEffect(() => {
setHasMounted(true)
}, [])
// Render
if (!hasMounted) return null
return <>{children}</>
}

View File

@@ -1,47 +0,0 @@
import { ReactNode } from 'react'
import { Site } from '@penxio/types'
import SocialIcon from './social-icons'
interface Props {
ModeToggle: () => ReactNode
site: Site
}
export function Footer({ site, ModeToggle }: Props) {
if (!site) return null
const socials = site.socials
return (
<footer className="mt-auto mb-8">
<div className="mt-16 flex flex-col items-center">
<div className="mb-3 flex space-x-4 item-center">
<SocialIcon kind="mail" href={`mailto:${socials?.email}`} size={6} />
<SocialIcon kind="github" href={socials.github} size={6} />
<SocialIcon kind="facebook" href={socials.facebook} size={6} />
<SocialIcon kind="youtube" href={socials.youtube} size={6} />
<SocialIcon kind="linkedin" href={socials.linkedin} size={6} />
<SocialIcon kind="twitter" href={socials.twitter} size={6} />
<SocialIcon kind="x" href={socials.x} size={6} />
<SocialIcon kind="instagram" href={socials.instagram} size={6} />
<SocialIcon kind="threads" href={socials.threads} size={6} />
</div>
<div className="flex gap-2 text-sm justify-center item-center text-foreground/50">
<div className="flex items-center">{`© ${new Date().getFullYear()}`}</div>
<div className="flex items-center">{``}</div>
<div className="flex items-center">{site.name}</div>
<div className="flex items-center">{``}</div>
<div className="flex items-center gap-1">
Build with
<a
href="https://penx.io"
target="_blank"
className="text-brand-500"
>
PenX
</a>
</div>
<ModeToggle />
</div>
</div>
</footer>
)
}

View File

@@ -1,8 +1,9 @@
import { ReactNode, Suspense } from 'react'
import { Site } from '@penxio/types'
import { Profile } from '@/components/Profile/Profile'
import { Airdrop } from '@/components/theme-ui/Airdrop'
import { cn } from '@/lib/utils'
import { Site } from '@penxio/types'
import { Lobster } from 'next/font/google'
import Link from './Link'
import Link from 'next/link'
import { PostTypeNav } from './PostTypeNav'
const lobster = Lobster({
@@ -22,14 +23,9 @@ const headerNavLinks = [
interface Props {
site: Site
Logo: () => ReactNode
ModeToggle: () => ReactNode
MobileNav: () => ReactNode
ConnectButton: () => ReactNode
Airdrop: () => ReactNode
}
export const Header = ({ site, Airdrop, ConnectButton }: Props) => {
export const Header = ({ site }: Props) => {
return (
<header className="">
<div className="flex items-start w-full justify-between py-4 z-40 bg-background/40 backdrop-blur-sm">
@@ -89,17 +85,11 @@ export const Header = ({ site, Airdrop, ConnectButton }: Props) => {
>
About
</Link>
{Airdrop && (
<div className="flex items-center">
<Airdrop />
</div>
)}
{!!ConnectButton && (
<Suspense fallback={<div></div>}>
<ConnectButton />
</Suspense>
)}
<div className="flex items-center">
<Airdrop />
</div>
<Profile />
</div>
</div>
<PostTypeNav className="flex md:hidden" />

View File

@@ -1,9 +0,0 @@
import NextImage, { ImageProps } from 'next/image'
const basePath = process.env.BASE_PATH
const Image = ({ src, ...rest }: ImageProps) => (
<NextImage src={`${basePath || ''}${src}`} {...rest} />
)
export default Image

View File

@@ -1,34 +0,0 @@
import { AnchorHTMLAttributes } from 'react'
import Link from 'next/link'
import type { LinkProps } from 'next/link'
const CustomLink = ({
href,
...rest
}: LinkProps & AnchorHTMLAttributes<HTMLAnchorElement>) => {
const isInternalLink = href && href.startsWith('/')
const isAnchorLink = href && href.startsWith('#')
const isProd = process.env.NODE_ENV === 'production'
if (isInternalLink) {
// if (isProd) href = href + '.html'
return <Link className="break-words" href={href} {...rest} />
}
if (isAnchorLink) {
return <a className="break-words" href={href} {...rest} />
}
return (
<a
className="break-words"
target="_blank"
rel="noopener noreferrer"
href={href}
{...rest}
/>
)
}
export default CustomLink

View File

@@ -1,20 +0,0 @@
import { ReactNode } from 'react'
import { cn } from '@/lib/utils'
interface Props {
children: ReactNode
className?: string
}
export default function PageTitle({ children, className }: Props) {
return (
<h1
className={cn(
'text-2xl font-semibold leading-9 tracking-tight sm:text-4xl sm:leading-10 md:text-4xl md:leading-13 mt-12 mb-8 text-foreground/90',
className,
)}
>
{children}
</h1>
)
}

View File

@@ -1,60 +0,0 @@
/* eslint-disable jsx-a11y/anchor-is-valid */
'use client'
import { usePathname } from 'next/navigation'
import Link from './Link'
interface PaginationProps {
totalPages: number
currentPage: number
}
export function Pagination({ totalPages, currentPage }: PaginationProps) {
const pathname = usePathname()!
const basePath = pathname.split('/')[1]
const prevPage = currentPage - 1 > 0
const nextPage = currentPage + 1 <= totalPages
return (
<div className="space-y-2 pb-8 pt-6 md:space-y-5">
<nav className="flex justify-between">
{!prevPage && (
<button
className="cursor-auto disabled:opacity-50"
disabled={!prevPage}
>
Previous
</button>
)}
{prevPage && (
<Link
href={
currentPage - 1 === 1
? `/${basePath}/`
: `/${basePath}/page/${currentPage - 1}`
}
rel="prev"
>
Previous
</Link>
)}
<span>
{currentPage} of {totalPages}
</span>
{!nextPage && (
<button
className="cursor-auto disabled:opacity-50"
disabled={!nextPage}
>
Next
</button>
)}
{nextPage && (
<Link href={`/${basePath}/page/${currentPage + 1}`} rel="next">
Next
</Link>
)}
</nav>
</div>
)
}

View File

@@ -1,5 +1,5 @@
import { Pagination } from '@/components/theme-ui/Pagination'
import { Post } from '@penxio/types'
import { Pagination } from './Pagination'
import { PostItem } from './PostItem'
interface PaginationProps {

View File

@@ -1,5 +1,5 @@
import { Post, Tag } from '@penxio/types'
import PageTitle from './PageTitle'
import { PageTitle } from '@/components/theme-ui/PageTitle'
import { PostList } from './PostList'
import { TagList } from './TagList'

View File

@@ -2,8 +2,8 @@
import { Tag } from '@penxio/types'
import { slug } from 'github-slugger'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
import Link from './Link'
interface PostListWithTagProps {
tags: Tag[]

View File

@@ -1,95 +0,0 @@
import { SVGProps } from 'react'
// Icons taken from: https://simpleicons.org/
// To add a new icon, add a new function here and add it to components in social-icons/index.tsx
export function Facebook(svgProps: SVGProps<SVGSVGElement>) {
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" {...svgProps}>
<title>Facebook</title>
<path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"></path>
</svg>
)
}
export function Github(svgProps: SVGProps<SVGSVGElement>) {
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" {...svgProps}>
<title>GitHub</title>
<path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"></path>
</svg>
)
}
export function Linkedin(svgProps: SVGProps<SVGSVGElement>) {
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" {...svgProps}>
<title>Linkedin</title>
<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"></path>
</svg>
)
}
export function Mail(svgProps: SVGProps<SVGSVGElement>) {
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" {...svgProps}>
<title>Mail</title>
<path d="M2.003 5.884L10 9.882l7.997-3.998A2 2 0 0016 4H4a2 2 0 00-1.997 1.884z"></path>
<path d="M18 8.118l-8 4-8-4V14a2 2 0 002 2h12a2 2 0 002-2V8.118z"></path>
</svg>
)
}
export function Twitter(svgProps: SVGProps<SVGSVGElement>) {
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" {...svgProps}>
<title>Twitter</title>
<path d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z"></path>
</svg>
)
}
export function X(svgProps: SVGProps<SVGSVGElement>) {
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" {...svgProps}>
<title>X</title>
<path d="M18.901 1.153h3.68l-8.04 9.19L24 22.846h-7.406l-5.8-7.584-6.638 7.584H.474l8.6-9.83L0 1.154h7.594l5.243 6.932ZM17.61 20.644h2.039L6.486 3.24H4.298Z" />
</svg>
)
}
export function Youtube(svgProps: SVGProps<SVGSVGElement>) {
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" {...svgProps}>
<title>Youtube</title>
<path d="M23.499 6.203a3.008 3.008 0 00-2.089-2.089c-1.87-.501-9.4-.501-9.4-.501s-7.509-.01-9.399.501a3.008 3.008 0 00-2.088 2.09A31.258 31.26 0 000 12.01a31.258 31.26 0 00.523 5.785 3.008 3.008 0 002.088 2.089c1.869.502 9.4.502 9.4.502s7.508 0 9.399-.502a3.008 3.008 0 002.089-2.09 31.258 31.26 0 00.5-5.784 31.258 31.26 0 00-.5-5.808zm-13.891 9.4V8.407l6.266 3.604z"></path>
</svg>
)
}
export function Mastodon(svgProps: SVGProps<SVGSVGElement>) {
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" {...svgProps}>
<title>Mastodon</title>
<path d="M23.268 5.313c-.35-2.578-2.617-4.61-5.304-5.004C17.51.242 15.792 0 11.813 0h-.03c-3.98 0-4.835.242-5.288.309C3.882.692 1.496 2.518.917 5.127.64 6.412.61 7.837.661 9.143c.074 1.874.088 3.745.26 5.611.118 1.24.325 2.47.62 3.68.55 2.237 2.777 4.098 4.96 4.857 2.336.792 4.849.923 7.256.38.265-.061.527-.132.786-.213.585-.184 1.27-.39 1.774-.753a.057.057 0 0 0 .023-.043v-1.809a.052.052 0 0 0-.02-.041.053.053 0 0 0-.046-.01 20.282 20.282 0 0 1-4.709.545c-2.73 0-3.463-1.284-3.674-1.818a5.593 5.593 0 0 1-.319-1.433.053.053 0 0 1 .066-.054c1.517.363 3.072.546 4.632.546.376 0 .75 0 1.125-.01 1.57-.044 3.224-.124 4.768-.422.038-.008.077-.015.11-.024 2.435-.464 4.753-1.92 4.989-5.604.008-.145.03-1.52.03-1.67.002-.512.167-3.63-.024-5.545zm-3.748 9.195h-2.561V8.29c0-1.309-.55-1.976-1.67-1.976-1.23 0-1.846.79-1.846 2.35v3.403h-2.546V8.663c0-1.56-.617-2.35-1.848-2.35-1.112 0-1.668.668-1.67 1.977v6.218H4.822V8.102c0-1.31.337-2.35 1.011-3.12.696-.77 1.608-1.164 2.74-1.164 1.311 0 2.302.5 2.962 1.498l.638 1.06.638-1.06c.66-.999 1.65-1.498 2.96-1.498 1.13 0 2.043.395 2.74 1.164.675.77 1.012 1.81 1.012 3.12z" />
</svg>
)
}
export function Threads(svgProps: SVGProps<SVGSVGElement>) {
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" {...svgProps}>
<title>Threads</title>
<path d="M12.186 24h-.007c-3.581-.024-6.334-1.205-8.184-3.509C2.35 18.44 1.5 15.586 1.472 12.01v-.017c.03-3.579.879-6.43 2.525-8.482C5.845 1.205 8.6.024 12.18 0h.014c2.746.02 5.043.725 6.826 2.098 1.677 1.29 2.858 3.13 3.509 5.467l-2.04.569c-1.104-3.96-3.898-5.984-8.304-6.015-2.91.022-5.11.936-6.54 2.717C4.307 6.504 3.616 8.914 3.589 12c.027 3.086.718 5.496 2.057 7.164 1.43 1.783 3.631 2.698 6.54 2.717 2.623-.02 4.358-.631 5.8-2.045 1.647-1.613 1.618-3.593 1.09-4.798-.31-.71-.873-1.3-1.634-1.75-.192 1.352-.622 2.446-1.284 3.272-.886 1.102-2.14 1.704-3.73 1.79-1.202.065-2.361-.218-3.259-.801-1.063-.689-1.685-1.74-1.752-2.964-.065-1.19.408-2.285 1.33-3.082.88-.76 2.119-1.207 3.583-1.291a13.853 13.853 0 0 1 3.02.142c-.126-.742-.375-1.332-.75-1.757-.513-.586-1.308-.883-2.359-.89h-.029c-.844 0-1.992.232-2.721 1.32L7.734 7.847c.98-1.454 2.568-2.256 4.478-2.256h.044c3.194.02 5.097 1.975 5.287 5.388.108.046.216.094.321.142 1.49.7 2.58 1.761 3.154 3.07.797 1.82.871 4.79-1.548 7.158-1.85 1.81-4.094 2.628-7.277 2.65Zm1.003-11.69c-.242 0-.487.007-.739.021-1.836.103-2.98.946-2.916 2.143.067 1.256 1.452 1.839 2.784 1.767 1.224-.065 2.818-.543 3.086-3.71a10.5 10.5 0 0 0-2.215-.221z" />
</svg>
)
}
export function Instagram(svgProps: SVGProps<SVGSVGElement>) {
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" {...svgProps}>
<title>Instagram</title>
<path d="M12 0C8.74 0 8.333.015 7.053.072 5.775.132 4.905.333 4.14.63c-.789.306-1.459.717-2.126 1.384S.935 3.35.63 4.14C.333 4.905.131 5.775.072 7.053.012 8.333 0 8.74 0 12s.015 3.667.072 4.947c.06 1.277.261 2.148.558 2.913.306.788.717 1.459 1.384 2.126.667.666 1.336 1.079 2.126 1.384.766.296 1.636.499 2.913.558C8.333 23.988 8.74 24 12 24s3.667-.015 4.947-.072c1.277-.06 2.148-.262 2.913-.558.788-.306 1.459-.718 2.126-1.384.666-.667 1.079-1.335 1.384-2.126.296-.765.499-1.636.558-2.913.06-1.28.072-1.687.072-4.947s-.015-3.667-.072-4.947c-.06-1.277-.262-2.149-.558-2.913-.306-.789-.718-1.459-1.384-2.126C21.319 1.347 20.651.935 19.86.63c-.765-.297-1.636-.499-2.913-.558C15.667.012 15.26 0 12 0zm0 2.16c3.203 0 3.585.016 4.85.071 1.17.055 1.805.249 2.227.415.562.217.96.477 1.382.896.419.42.679.819.896 1.381.164.422.36 1.057.413 2.227.057 1.266.07 1.646.07 4.85s-.015 3.585-.074 4.85c-.061 1.17-.256 1.805-.421 2.227-.224.562-.479.96-.899 1.382-.419.419-.824.679-1.38.896-.42.164-1.065.36-2.235.413-1.274.057-1.649.07-4.859.07-3.211 0-3.586-.015-4.859-.074-1.171-.061-1.816-.256-2.236-.421-.569-.224-.96-.479-1.379-.899-.421-.419-.69-.824-.9-1.38-.165-.42-.359-1.065-.42-2.235-.045-1.26-.061-1.649-.061-4.844 0-3.196.016-3.586.061-4.861.061-1.17.255-1.814.42-2.234.21-.57.479-.96.9-1.381.419-.419.81-.689 1.379-.898.42-.166 1.051-.361 2.221-.421 1.275-.045 1.65-.06 4.859-.06l.045.03zm0 3.678c-3.405 0-6.162 2.76-6.162 6.162 0 3.405 2.76 6.162 6.162 6.162 3.405 0 6.162-2.76 6.162-6.162 0-3.405-2.76-6.162-6.162-6.162zM12 16c-2.21 0-4-1.79-4-4s1.79-4 4-4 4 1.79 4 4-1.79 4-4 4zm7.846-10.405c0 .795-.646 1.44-1.44 1.44-.795 0-1.44-.646-1.44-1.44 0-.794.646-1.439 1.44-1.439.793-.001 1.44.645 1.44 1.439z" />
</svg>
)
}

View File

@@ -1,46 +1,20 @@
import { ReactNode } from 'react'
import { Footer } from '@/components/theme-ui/Footer'
import { Site } from '@penxio/types'
import { Footer } from '../components/Footer'
import { Header } from '../components/Header'
import SectionContainer from '../components/SectionContainer'
interface Props {
site: Site
Logo: () => ReactNode
ModeToggle: () => ReactNode
MobileNav: () => ReactNode
ConnectButton: () => ReactNode
Airdrop: () => ReactNode
children: ReactNode
}
export function SiteLayout({
children,
site,
Logo,
ModeToggle,
MobileNav,
ConnectButton,
Airdrop,
}: Props) {
export function SiteLayout({ children, site }: Props) {
return (
<SectionContainer>
{ModeToggle && (
<div className="absolute top-3 right-3 hidden xs:block">
<ModeToggle />
</div>
)}
<Header
site={site}
Logo={Logo}
ModeToggle={ModeToggle}
MobileNav={MobileNav}
ConnectButton={ConnectButton}
Airdrop={Airdrop}
/>
<Header site={site} />
<main className="mb-auto">{children}</main>
<Footer site={site} ModeToggle={ModeToggle} />
<Footer site={site} />
</SectionContainer>
)
}

View File

@@ -1,25 +1,21 @@
import { ContentRender } from '@/components/ContentRender'
import { PageTitle } from '@/components/theme-ui/PageTitle'
import { Site } from '@penxio/types'
import Image from '../components/Image'
import PageTitle from '../components/PageTitle'
import SocialIcon from '../components/social-icons'
interface Props {
site: Site
ContentRender: (props: { content: any[]; className?: string }) => JSX.Element
}
export function AboutPage({ site, ContentRender }: Props) {
export function AboutPage({ site }: Props) {
return (
<div className="mx-auto max-w-3xl">
<PageTitle>About</PageTitle>
<div className="">
<div className="flex flex-col items-center space-x-2 pt-8">
{site.logo && (
<Image
<img
src={site.logo}
alt="avatar"
width={192}
height={192}
className="h-48 w-48 rounded-full"
/>
)}

View File

@@ -1,5 +1,5 @@
import { Post } from '@penxio/types'
import PageTitle from '../components/PageTitle'
import { PageTitle } from '@/components/theme-ui/PageTitle'
import { PostList } from '../components/PostList'
interface Props {

Some files were not shown because too many files have changed in this diff Show More