feat: showcase

This commit is contained in:
DIYgod 2022-10-12 19:00:50 +01:00
parent d71a2c53a4
commit 7e0aafff19
No known key found for this signature in database
GPG Key ID: D159328F47A80DCA
8 changed files with 209 additions and 83 deletions

View File

@ -1,4 +1,4 @@
Copyright (c) 2022-present Proselog.
Copyright (c) 2022-present xLog.
Portions of this software are licensed as follows:

View File

@ -41,6 +41,7 @@
"@trpc/next": "^9.27.2",
"@trpc/react": "^9.27.2",
"@trpc/server": "^9.27.2",
"@urql/core": "^3.0.3",
"@web-std/form-data": "^3.0.2",
"async-busboy": "^1.1.0",
"axios": "^0.27.2",

View File

@ -51,6 +51,7 @@ specifiers:
'@types/uuid': ^8.3.4
'@unocss/cli': ^0.45.25
'@unocss/preset-icons': ^0.45.25
'@urql/core': ^3.0.3
'@web-std/form-data': ^3.0.2
async-busboy: ^1.1.0
autoprefixer: ^10.4.12
@ -153,6 +154,7 @@ dependencies:
'@trpc/next': 9.27.2_dyodlyrl4jlccmeg4rab7q5slm
'@trpc/react': 9.27.2_r3nohbkvzkycnbsdj5gzhfx2rm
'@trpc/server': 9.27.2
'@urql/core': 3.0.3
'@web-std/form-data': 3.0.2
async-busboy: 1.1.0
axios: 0.27.2
@ -1318,6 +1320,12 @@ packages:
react-dom: 18.2.0_react@18.2.0
dev: false
/@graphql-typed-document-node/core/3.1.1:
resolution: {integrity: sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg==}
peerDependencies:
graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0
dev: false
/@graphql-typed-document-node/core/3.1.1_graphql@16.5.0:
resolution: {integrity: sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg==}
peerDependencies:
@ -2774,6 +2782,15 @@ packages:
wonka: 4.0.15
dev: false
/@urql/core/3.0.3:
resolution: {integrity: sha512-raQP51ERNtg5BvlN8x8mHVRvk4K0ugWQ69n53BdkjKpXVV5kuWp7trnwriGv1fQKa8HuiGNSCfyslUucc0OVQg==}
peerDependencies:
graphql: ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0
dependencies:
'@graphql-typed-document-node/core': 3.1.1
wonka: 6.1.0
dev: false
/@vanilla-extract/css/1.7.0:
resolution: {integrity: sha512-2Abpr5aJskwKS+waMWxWnTBqq/rj4J93KtGteW0VSORMUsijrupwjOXECd7Llh+nSFfx/Y6usuexF5/ffyDF8A==}
dependencies:
@ -11096,6 +11113,10 @@ packages:
resolution: {integrity: sha512-U0IUQHKXXn6PFo9nqsHphVCE5m3IntqZNB9Jjn7EB1lrR7YTDY3YWgFvEvwniTzXSvOH/XMzAZaIfJF/LvHYXg==}
dev: false
/wonka/6.1.0:
resolution: {integrity: sha512-VgiMCz7BXOiDbgpVhf5iNhK7hurteY5Jv0fDJewUkY0s4fbxQD2iKqfGxNXNTwp2v3bgT8QVu2l5H7YdkZ5WIA==}
dev: false
/word-wrap/1.2.3:
resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==}
engines: {node: '>=0.10.0'}

48
showcase.json Normal file
View File

@ -0,0 +1,48 @@
[
"xlog",
"diygod",
"rss3",
"crossbell-blog",
"hey",
"song",
"atlas-thinking",
"joshua",
"jeff",
"walter",
"crossbell-io-updates",
"xtao",
"polebug",
"pengchao",
"boyuand",
"lilo",
"coolcat",
"dao",
"0x55f110395c844963b075674e2956eb414018a7a7",
"139",
"just1lovewrl",
"kanikig",
"coosir",
"atryte",
"qihangsun",
"brucexx",
"0x1",
"duogeyu",
"stone",
"lastmelody",
"12138",
"isheep",
"atryte",
"henryqw",
"whost",
"twifag",
"hominsu",
"exodus-exodus",
"zoo",
"colojayce",
"stars",
"somehacker",
"nss",
"somehacker",
"novonine",
"nafidurmus"
]

View File

@ -4,6 +4,8 @@ import unidata from "~/queries/unidata.server"
import { renderPageContent } from "~/markdown"
import { toGateway } from "~/lib/ipfs-parser"
import type Unidata from "unidata.js"
import { createClient } from "@urql/core"
import axios from "axios"
export const checkSubdomain = async ({
subdomain,
@ -94,6 +96,72 @@ export const getSite = async (input: string, customUnidata?: Unidata) => {
return site
}
export const getSites = async (input: string[]) => {
const client = createClient({
url: "https://indexer.crossbell.io/v1/graphql",
})
const result = await client
.query(
`
query getCharacters($identities: [String!], $limit: Int) {
characters( where: { handle: { in: $identities } }, orderBy: [{ updatedAt: desc }], take: $limit ) {
handle
updatedAt
notes {
updatedAt
}
metadata {
uri
content
}
}
}`,
{
identities: input,
},
)
.toPromise()
await Promise.all(
result.data?.characters?.map(async (site: any) => {
if (!site?.metadata?.content && site?.metadata?.uri) {
site.metadata.content = (
await axios.get(
toGateway(site?.metadata?.uri, {
needRequestAtServerSide: true,
}),
{
...(typeof window === "undefined" && {
headers: {
"User-Agent":
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36",
},
}),
},
)
).data
}
site.metadata.content.name = site.metadata.content.name || site.handle
site.custom_domain =
site.metadata?.content?.attributes?.find(
(a: any) => a.trait_type === "xlog_custom_domain",
)?.value || ""
site.updatedAt = [...site.notes, site]
.map((i) => i.updatedAt)
.sort()
.pop()
}),
)
result.data?.characters?.sort((a: any, b: any) => {
return b.updatedAt > a.updatedAt ? 1 : -1
})
return result.data?.characters
}
export const getSubscription = async (
data: {
userId: string

View File

@ -16,10 +16,18 @@ 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"
export const getServerSideProps: GetServerSideProps = async (ctx) => {
const queryClient = new QueryClient()
await prefetchGetSites(showcase, queryClient)
return {
props: {
dehydratedState: dehydrate(queryClient),
region: FLY_REGION,
},
}
@ -30,6 +38,7 @@ export default function Home({ region }: { region: string | null }) {
const { address } = useAccount()
const { openConnectModal } = useConnectModal()
const router = useRouter()
const showcaseSites = useGetSites(showcase)
useEffect(() => {
console.log("-> region", region)
@ -99,60 +108,8 @@ export default function Home({ region }: { region: string | null }) {
},
]
const comparing = [
"Only Controlled by Yourself",
"Open Source",
"Self-hosting",
"Like Posts",
"Comment Posts",
"Custom Domain",
"Custom CSS",
"Navigation",
"Pages",
"Scheduled Publishing",
]
const stars = [
{
username: "rss3",
avartar: "bafkreigjinhazhgvqaj423fhuuev7ormzf5vsm4iu4vrx2zgk2l5nugm4m",
},
{
username: "crossbell-blog",
avartar: "bafkreic5k3zvarbsondfrowy7kpbj6xo7cj25hobksgileqbbupjvvmkoq",
},
{
username: "diygod",
avartar: "bafybeibefx2tyow77m2wcnsh5anaaxfy7ypxbcuapb52c4h255onqp72ye",
},
{
username: "hey",
avartar: "bafkreigfhmqizqi7jwg5sw522kwcqilq7m6g4mbicpyit4avezaztvovey",
},
{
username: "song",
avartar: "QmQArk2yEF9t2Ud7vbhMyAQu8DJnTzc3EduSm7rcF2LQv4",
},
{
username: "atlas-thinking",
avartar: "QmNks3cJWDXzG5WSBzTVv1regkhXhzChK6LhJYu1HiBLS2",
},
{
username: "joshua",
avartar: "QmTUYz29S5bMqozK2uyQ1Y91DZzka4PLjReAD7J2nkbLMN",
},
{
username: "jeff",
avartar: "QmdvVh2w375eVMfDmi9yb2hks1X3MDDEAbbJB7FH2FVKxd",
},
{
username: "walter",
avartar: "QmRXkowLwfqjMKZS41KrnjM6JjAG5eerGWyFbo6ZzB1uUD",
},
]
return (
<MainLayout tabs={["Features", `Why ${APP_NAME}`]}>
<MainLayout tabs={["Features", "Showcase"]}>
<section>
<div className="max-w-screen-lg px-5 mx-auto">
<div className="h-screen w-full flex justify-center flex-col relative">
@ -223,45 +180,57 @@ export default function Home({ region }: { region: string | null }) {
))}
</ul>
</Element>
<Element name={`Why ${APP_NAME}`}>
<div className="pt-28 text-4xl font-bold">Why {APP_NAME}</div>
<div className="text-xl mt-10 leading-normal">
We believe freedom of expression is a universal right. In your
long-form writing, you should be able to express your thoughts
freely; without fear of your post being removed or censored.
xLog ensures that whatever you write, it will be immutable and
permanently stored.
</div>
<Element name="Showcase">
<div className="pt-28 text-4xl font-bold">Showcase</div>
<div className="my-10 text-zinc-500">
Trusted by these awesome teams and geeks
<ul className="mt-4 space-x-4">
{stars.map((item) => (
Discover these awesome teams and geeks (sorted by update time)
<ul className="mt-4 grid grid-cols-2 md:grid-cols-3 gap-4">
{showcaseSites.data?.map((site: any) => (
<li
className="inline-flex align-middle"
key={item.username}
className="inline-flex align-middle py-2"
key={site.handle}
>
<UniLink
href={getSiteLink({
subdomain: item.username,
})}
className="w-14 h-14"
href={
site.custom_domain
? `https://${site.custom_domain}`
: getSiteLink({
subdomain: site.handle,
})
}
className="inline-flex align-middle w-full"
>
<Image
className="rounded-full"
src={`/_ipfs/${item.avartar}`}
alt={item.username}
width="56"
height="56"
></Image>
<span className="w-14 h-14">
<Image
className="rounded-full"
src={
site.metadata.content?.avatars?.[0] ||
"ipfs://bafkreiabgixxp63pg64moxnsydz7hewmpdkxxi3kdsa4oqv4pb6qvwnmxa"
}
alt={site.handle}
width="56"
height="56"
></Image>
</span>
<span className="ml-4 min-w-0 flex-1">
<span className="truncate w-full inline-block">
{site.metadata.content?.name}
</span>
{site.metadata.content?.bio && (
<span className="text-gray-500 text-xs truncate w-full inline-block">
{site.metadata.content?.bio}
</span>
)}
</span>
</UniLink>
</li>
))}
<li className="inline-flex h-5 align-middle items-center">
<li className="inline-flex h-full align-middle items-center">
<UniLink
className="inline-block"
href="https://github.com/Crossbell-Box/xLog/issues/new"
className="inline-block text-accent text-center pl-2"
href="https://github.com/Crossbell-Box/xLog/edit/dev/showcase.json"
>
<EllipsisHorizontalIcon className="w-14 h-4" />
Submit yours
</UniLink>
</li>
</ul>

View File

@ -28,3 +28,13 @@ export const prefetchGetSiteSubscriptions = async (
return cacheGet(key, () => siteModel.getSiteSubscriptions(input))
})
}
export const prefetchGetSites = async (
input: string[],
queryClient: QueryClient,
) => {
const key = ["getSites", input]
return await queryClient.fetchQuery(key, async () => {
return cacheGet(key, () => siteModel.getSites(input))
})
}

View File

@ -22,6 +22,15 @@ export const useGetSite = (input: string) => {
})
}
export const useGetSites = (input: string[]) => {
return useQuery(["getSites", input], async () => {
if (!input) {
return null
}
return siteModel.getSites(input)
})
}
export const useGetSubscription = (data: {
userId: string
siteId: string