432 lines
16 KiB
TypeScript
432 lines
16 KiB
TypeScript
import { GetServerSideProps } from "next"
|
|
import { useEffect, useState } from "react"
|
|
import { DashboardIcon } from "~/components/icons/DashboardIcon"
|
|
import { MainLayout } from "~/components/main/MainLayout"
|
|
import { UniLink } from "~/components/ui/UniLink"
|
|
import { LoveIcon } from "~/components/icons/LoveIcon"
|
|
import { BlockchainIcon } from "~/components/icons/BlockchainIcon"
|
|
import { LaughIcon } from "~/components/icons/LaughIcon"
|
|
import { Button } from "~/components/ui/Button"
|
|
import { useAccountState, useConnectModal } from "@crossbell/connect-kit"
|
|
import { useRefCallback } from "@crossbell/util-hooks"
|
|
import { useRouter } from "next/router"
|
|
import { GITHUB_LINK, APP_NAME, CSB_SCAN } from "~/lib/env"
|
|
import { getSiteLink } from "~/lib/helpers"
|
|
import { Link, Element } from "react-scroll"
|
|
import { Image } from "~/components/ui/Image"
|
|
import { dehydrate, QueryClient } from "@tanstack/react-query"
|
|
import { prefetchGetSites } from "~/queries/site.server"
|
|
import { useGetSites } from "~/queries/site"
|
|
import showcase from "../../showcase.json"
|
|
import { CharacterFloatCard } from "~/components/common/CharacterFloatCard"
|
|
import { useAccountSites, useSubscribeToSites } from "~/queries/site"
|
|
import { SITE_URL } from "~/lib/env"
|
|
import { BoltIcon } from "@heroicons/react/24/outline"
|
|
|
|
export const getServerSideProps: GetServerSideProps = async (ctx) => {
|
|
const queryClient = new QueryClient()
|
|
await prefetchGetSites(showcase, queryClient)
|
|
|
|
return {
|
|
props: {
|
|
dehydratedState: dehydrate(queryClient),
|
|
},
|
|
}
|
|
}
|
|
|
|
export default function Home() {
|
|
const isConnected = useAccountState((s) => !!s.computed.account)
|
|
const { show: openConnectModal } = useConnectModal()
|
|
const router = useRouter()
|
|
const showcaseSites = useGetSites(showcase)
|
|
|
|
const [isTry, setIsTry] = useState(false)
|
|
const tryNow = () => {
|
|
setIsTry(true)
|
|
openConnectModal?.()
|
|
}
|
|
|
|
useEffect(() => {
|
|
if (isTry && isConnected) {
|
|
router.push("/dashboard")
|
|
setIsTry(false)
|
|
}
|
|
}, [isTry, isConnected, router])
|
|
|
|
const description = [
|
|
{
|
|
screenshot: {
|
|
src: "/screenshot1.png",
|
|
width: 3947,
|
|
height: 1888,
|
|
},
|
|
icon: <LaughIcon className="inline mb-2" />,
|
|
title: "Elegent",
|
|
text: <>Out-of-the-box, good looking, no friction.</>,
|
|
},
|
|
{
|
|
screenshot: {
|
|
src: "/screenshot4.png",
|
|
width: 1542,
|
|
height: 467,
|
|
},
|
|
icon: <BlockchainIcon className="inline mb-2" />,
|
|
title: "On-chain",
|
|
text: (
|
|
<>
|
|
All blog data, including configs, posts, following, comments, etc.,
|
|
are permanently stored on the blockchain <b>with your own hands</b>{" "}
|
|
and can only be controlled by yourself and not the platform.
|
|
</>
|
|
),
|
|
},
|
|
{
|
|
screenshot: {
|
|
src: "/screenshot5.png",
|
|
width: 1712,
|
|
height: 1866,
|
|
},
|
|
icon: <BoltIcon className="inline mb-2 w-8 h-8" />,
|
|
title: "Fast",
|
|
text: (
|
|
<>
|
|
Blockchain doesn't always mean inefficiency. xLog's
|
|
experienced open-source maintainers have been making optimizations to
|
|
get xLog to peak speed.
|
|
</>
|
|
),
|
|
},
|
|
{
|
|
screenshot: {
|
|
src: "/screenshot2.png",
|
|
width: 1528,
|
|
height: 758,
|
|
},
|
|
icon: <LoveIcon className="inline mb-2" />,
|
|
title: "Rich Interactions",
|
|
text: "You can follow, comment, like, and mint your blog and posts, all on the blockchain of course.",
|
|
},
|
|
{
|
|
screenshot: {
|
|
src: "/screenshot3.png",
|
|
width: 4394,
|
|
height: 1854,
|
|
},
|
|
icon: <DashboardIcon className="inline mb-2" />,
|
|
title: "Highly Customizable",
|
|
text: "Domain name, navigation bar, custom styles, all as you wish, and stored on the blockchain.",
|
|
},
|
|
]
|
|
|
|
const [followProgress, setFollowProgress] = useState<boolean>(false)
|
|
const userSite = useAccountSites()
|
|
const subscribeToSites = useSubscribeToSites()
|
|
|
|
const doSubscribeToSites = useRefCallback(() => {
|
|
subscribeToSites.mutate({
|
|
characterIds: showcaseSites.data
|
|
?.map((s: { characterId?: string }) => s.characterId)
|
|
.filter(Boolean)
|
|
.map(Number),
|
|
siteIds: showcaseSites.data?.map((s: { handle: string }) => s.handle),
|
|
} as any)
|
|
})
|
|
|
|
const followAll = async () => {
|
|
if (!isConnected) {
|
|
setFollowProgress(true)
|
|
openConnectModal?.()
|
|
} else if (!userSite.data?.[0]) {
|
|
router.push(`${SITE_URL}/dashboard/new-site`)
|
|
} else {
|
|
doSubscribeToSites()
|
|
}
|
|
}
|
|
|
|
useEffect(() => {
|
|
if (
|
|
followProgress &&
|
|
isConnected &&
|
|
showcaseSites.isSuccess &&
|
|
userSite.data?.[0] &&
|
|
userSite.isSuccess
|
|
) {
|
|
if (!userSite.data) {
|
|
router.push(`${SITE_URL}/dashboard/new-site`)
|
|
}
|
|
doSubscribeToSites()
|
|
setFollowProgress(false)
|
|
}
|
|
}, [
|
|
userSite.isSuccess,
|
|
userSite.data,
|
|
router,
|
|
followProgress,
|
|
isConnected,
|
|
showcaseSites.isSuccess,
|
|
showcaseSites.data,
|
|
subscribeToSites,
|
|
doSubscribeToSites,
|
|
])
|
|
|
|
return (
|
|
<MainLayout tabs={["Features", "Showcase", "Integration"]}>
|
|
<section>
|
|
<div className="max-w-screen-lg px-5 mx-auto">
|
|
<div className="h-screen w-full flex justify-center flex-col relative">
|
|
<div className="w-28 h-28 mb-16">
|
|
<Image alt="logo" src="/logo.svg" width={200} height={200} />
|
|
</div>
|
|
<h2 className="text-3xl sm:text-5xl font-bold mb-5">Blog Free</h2>
|
|
<h3 className="mt-5 text-zinc-800 text-4xl sm:text-6xl font-light">
|
|
{APP_NAME}, the first{" "}
|
|
<UniLink className="underline decoration-2" href={CSB_SCAN}>
|
|
on-chain
|
|
</UniLink>{" "}
|
|
and{" "}
|
|
<UniLink className="underline decoration-2" href={GITHUB_LINK}>
|
|
open-source
|
|
</UniLink>{" "}
|
|
blogging platform for everyone.
|
|
</h3>
|
|
<div className="my-10 sm:my-16">
|
|
<Button
|
|
className="text-accent w-80 h-10"
|
|
onClick={() =>
|
|
isConnected ? router.push("/dashboard") : openConnectModal?.()
|
|
}
|
|
size="xl"
|
|
>
|
|
{isConnected ? (
|
|
<>
|
|
<span className="i-bi-grid text-lg mr-2"></span>
|
|
<span>Dashboard</span>
|
|
</>
|
|
) : (
|
|
"Connect"
|
|
)}
|
|
</Button>
|
|
</div>
|
|
<div className="text-center absolute bottom-20 w-full">
|
|
<Link
|
|
to="Features"
|
|
spy={true}
|
|
smooth={true}
|
|
duration={500}
|
|
className="cursor-pointer inline-block i-fluent:ios-arrow-rtl-24-filled text-3xl rotate-90"
|
|
></Link>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<Element name="Features">
|
|
<ul className="pt-28 space-y-24">
|
|
{description.map((item, index) => (
|
|
<li className="relative w-full" key={index}>
|
|
<p className="text-4xl font-bold">
|
|
<span className="mr-4">{item.icon}</span>
|
|
{item.title}
|
|
</p>
|
|
<p className="text-3xl font-light mt-4 leading-normal mb-8">
|
|
{item.text}
|
|
</p>
|
|
<div>
|
|
<Image
|
|
src={item.screenshot.src}
|
|
alt={item.title}
|
|
width={item.screenshot.width}
|
|
height={item.screenshot.height}
|
|
></Image>
|
|
</div>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</Element>
|
|
<Element name="Showcase">
|
|
<div className="pt-28 text-4xl font-bold">Showcase</div>
|
|
<div className="my-10 text-zinc-700">
|
|
<p className="text-lg">
|
|
Discover these awesome teams and geeks on xLog (sorted by
|
|
update time)
|
|
</p>
|
|
<Button
|
|
size="xl"
|
|
className="mt-5"
|
|
onClick={followAll}
|
|
isLoading={
|
|
followProgress ||
|
|
showcaseSites.isLoading ||
|
|
userSite.isLoading ||
|
|
subscribeToSites.isLoading
|
|
}
|
|
>
|
|
🥳 Follow All!
|
|
</Button>
|
|
<ul className="mt-10 grid grid-cols-2 md:grid-cols-3 gap-10">
|
|
{showcaseSites.data?.map((site: any) => (
|
|
<li className="inline-flex align-middle" key={site.handle}>
|
|
<UniLink
|
|
href={
|
|
site.custom_domain
|
|
? `https://${site.custom_domain}`
|
|
: getSiteLink({
|
|
subdomain: site.handle,
|
|
})
|
|
}
|
|
className="inline-flex align-middle w-full"
|
|
>
|
|
<span className="w-14 h-14">
|
|
<CharacterFloatCard siteId={site.handle}>
|
|
<span className="w-full h-full">
|
|
<Image
|
|
className="rounded-full"
|
|
src={
|
|
site.metadata.content?.avatars?.[0] ||
|
|
"ipfs://bafkreiabgixxp63pg64moxnsydz7hewmpdkxxi3kdsa4oqv4pb6qvwnmxa"
|
|
}
|
|
alt={site.handle}
|
|
width="56"
|
|
height="56"
|
|
></Image>
|
|
</span>
|
|
</CharacterFloatCard>
|
|
</span>
|
|
<span className="ml-3 min-w-0 flex-1 justify-center inline-flex flex-col">
|
|
<span className="truncate w-full inline-block font-medium">
|
|
{site.metadata.content?.name}
|
|
</span>
|
|
{site.metadata.content?.bio && (
|
|
<span className="text-gray-500 text-xs truncate w-full inline-block mt-1">
|
|
{site.metadata.content?.bio}
|
|
</span>
|
|
)}
|
|
</span>
|
|
</UniLink>
|
|
</li>
|
|
))}
|
|
<li className="inline-flex h-14 align-middle items-center">
|
|
<UniLink
|
|
className="inline-block text-accent text-center"
|
|
href={`${GITHUB_LINK}/edit/dev/showcase.json`}
|
|
>
|
|
Submit yours
|
|
</UniLink>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</Element>
|
|
<Element name="Integration">
|
|
<div className="pt-28 text-4xl font-bold">Integration</div>
|
|
<div className="my-10 text-zinc-700">
|
|
<p className="text-xl">
|
|
You can integrate xLog with all of Crossbell's
|
|
eco-projects without friction
|
|
</p>
|
|
<ul className="space-y-8 mt-8">
|
|
<li>
|
|
<UniLink href="https://crossbell.io/@xlog">
|
|
<p className="font-bold text-xl">Crossbell.io</p>
|
|
<p className="text-zinc-500 my-4 text-lg">
|
|
Check the updates of the xLogs you follow and the posts
|
|
you collected
|
|
</p>
|
|
<Image
|
|
className="rounded-lg"
|
|
src="/integration1.png"
|
|
alt="integration1"
|
|
width="3726"
|
|
height="1888"
|
|
></Image>
|
|
</UniLink>
|
|
</li>
|
|
<li>
|
|
<UniLink href="https://rss3.io/result?search=xlog.csb">
|
|
<p className="font-bold text-xl">RSS3</p>
|
|
<p className="text-zinc-500 my-4 text-lg">
|
|
All your activities on xLog are supported and displayed
|
|
by RSS3 and all eco-projects of RSS3
|
|
</p>
|
|
<Image
|
|
className="rounded-lg"
|
|
src="/integration5.png"
|
|
alt="integration5"
|
|
width="3730"
|
|
height="1888"
|
|
></Image>
|
|
</UniLink>
|
|
</li>
|
|
<li>
|
|
<UniLink href="https://www.raycast.com/Songkeys/crossbell">
|
|
<p className="font-bold text-xl">Raycast Crossbell</p>
|
|
<p className="text-zinc-500 my-4 text-lg">
|
|
Searching and browsing xLog in Raycast
|
|
</p>
|
|
<Image
|
|
className="rounded-lg"
|
|
src="/integration2.png"
|
|
alt="integration2"
|
|
width="1724"
|
|
height="1172"
|
|
></Image>
|
|
</UniLink>
|
|
</li>
|
|
<li>
|
|
<UniLink href="https://export.crossbell.io">
|
|
<p className="font-bold text-xl">Export Crossbell Data</p>
|
|
<p className="text-zinc-500 my-4 text-lg">
|
|
Thinking of leaving or backing up? Pack all your xLog
|
|
site data and interaction data in one click here
|
|
</p>
|
|
<Image
|
|
className="rounded-lg"
|
|
src="/integration3.png"
|
|
alt="integration3"
|
|
width="3726"
|
|
height="1888"
|
|
></Image>
|
|
</UniLink>
|
|
</li>
|
|
<li>
|
|
<UniLink href="https://github.com/Crossbell-Box/crossbell.js">
|
|
<p className="font-bold text-xl">
|
|
Crossbell SDK and Indexer API
|
|
</p>
|
|
<p className="text-zinc-500 my-4 text-lg">
|
|
Create your own application or integration using
|
|
Crossbell's powerful SDK and Indexer API
|
|
</p>
|
|
<Image
|
|
className="rounded-lg"
|
|
src="/integration4.png"
|
|
alt="integration4"
|
|
width="3726"
|
|
height="1888"
|
|
></Image>
|
|
</UniLink>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</Element>
|
|
</div>
|
|
<div className="my-20 text-center">
|
|
<div className="w-20 h-20 mx-auto mb-8">
|
|
<Image alt="logo" src="/logo.svg" width={100} height={100} />
|
|
</div>
|
|
{isConnected ? (
|
|
<UniLink
|
|
href="/dashboard"
|
|
className="text-accent inline-flex items-center space-x-2"
|
|
>
|
|
<Button size="xl">Try {APP_NAME} Today</Button>
|
|
</UniLink>
|
|
) : (
|
|
<Button onClick={tryNow} size="xl">
|
|
Try {APP_NAME} Today
|
|
</Button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</MainLayout>
|
|
)
|
|
}
|