feat: followers pagination loading

This commit is contained in:
DIYgod 2022-09-15 01:06:53 +01:00
parent 8dd03aa6d5
commit 657ac8090a
No known key found for this signature in database
GPG Key ID: D159328F47A80DCA
6 changed files with 131 additions and 49 deletions

View File

@ -71,6 +71,7 @@
"react-dom": "^18.1.0",
"react-hook-form": "^7.31.1",
"react-hot-toast": "^2.2.0",
"react-infinite-scroller": "^1.2.6",
"react-scroll": "^1.8.7",
"react-sortablejs": "^6.1.1",
"refractor": "^4.7.0",
@ -115,6 +116,7 @@
"@types/react": "^18.0.9",
"@types/react-avatar-editor": "^12.0.0",
"@types/react-dom": "^18.0.3",
"@types/react-infinite-scroller": "^1.2.3",
"@types/react-scroll": "^1.8.4",
"@types/sortablejs": "^1.13.0",
"@types/ua-parser-js": "^0.7.36",

View File

@ -43,6 +43,7 @@ specifiers:
'@types/react': ^18.0.9
'@types/react-avatar-editor': ^12.0.0
'@types/react-dom': ^18.0.3
'@types/react-infinite-scroller': ^1.2.3
'@types/react-scroll': ^1.8.4
'@types/sortablejs': ^1.13.0
'@types/ua-parser-js': ^0.7.36
@ -88,6 +89,7 @@ specifiers:
react-dom: ^18.1.0
react-hook-form: ^7.31.1
react-hot-toast: ^2.2.0
react-infinite-scroller: ^1.2.6
react-scroll: ^1.8.7
react-sortablejs: ^6.1.1
refractor: ^4.7.0
@ -170,6 +172,7 @@ dependencies:
react-dom: 18.2.0_react@18.2.0
react-hook-form: 7.33.0_react@18.2.0
react-hot-toast: 2.2.0_biqbaboplfbrettd7655fr4n2y
react-infinite-scroller: 1.2.6_react@18.2.0
react-scroll: 1.8.7_biqbaboplfbrettd7655fr4n2y
react-sortablejs: 6.1.4_yi7d4rnqh6snv6o2bvvwtjnph4
refractor: 4.7.0
@ -214,6 +217,7 @@ devDependencies:
'@types/react': 18.0.14
'@types/react-avatar-editor': 12.0.0
'@types/react-dom': 18.0.5
'@types/react-infinite-scroller': 1.2.3
'@types/react-scroll': 1.8.4
'@types/sortablejs': 1.13.0
'@types/ua-parser-js': 0.7.36
@ -2177,6 +2181,12 @@ packages:
'@types/react': 18.0.14
dev: true
/@types/react-infinite-scroller/1.2.3:
resolution: {integrity: sha512-l60JckVoO+dxmKW2eEG7jbliEpITsTJvRPTe97GazjF5+ylagAuyYdXl8YY9DQsTP9QjhqGKZROknzgscGJy0A==}
dependencies:
'@types/react': 18.0.14
dev: true
/@types/react-scroll/1.8.4:
resolution: {integrity: sha512-DpHA9PYw42/rBrfKbGE/kAEvHRfyDL/ACfKB/ORWUYuCLi/yGrntxSzYXmg/7TLgQsJ5ma13GCDOzFSOz+8XOA==}
dependencies:
@ -7997,6 +8007,15 @@ packages:
- csstype
dev: false
/react-infinite-scroller/1.2.6_react@18.2.0:
resolution: {integrity: sha512-mGdMyOD00YArJ1S1F3TVU9y4fGSfVVl6p5gh/Vt4u99CJOptfVu/q5V/Wlle72TMgYlBwIhbxK5wF0C/R33PXQ==}
peerDependencies:
react: ^0.14.9 || ^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0
dependencies:
prop-types: 15.8.1
react: 18.2.0
dev: false
/react-is/16.13.1:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}

View File

@ -0,0 +1,74 @@
import clsx from "clsx"
import { Modal } from "~/components/ui/Modal"
import InfiniteScroll from "react-infinite-scroller"
import { Button } from "../ui/Button"
import { UniLink } from "../ui/UniLink"
import { CSB_SCAN, CSB_IO } from "~/lib/env"
import { Avatar } from "../ui/Avatar"
import { BlockchainIcon } from "~/components/icons/BlockchainIcon"
export const CharacterList: React.FC<{
open: boolean
setOpen: (open: boolean) => void
hasMore: boolean
loadMore: () => Promise<void>
list: any[]
title: string
}> = ({ open, setOpen, hasMore, loadMore, list, title }) => {
return (
<Modal open={open} setOpen={setOpen} title={title}>
<div className="px-5 overflow-auto flex-1">
<InfiniteScroll
loadMore={loadMore}
hasMore={hasMore}
loader={
<div className="text-sm py-3 text-center" key={0}>
Loading ...
</div>
}
useWindow={false}
>
{list?.length ? (
list.map((sub: any, index) => (
<div
className="py-3 flex items-center space-x-2 text-sm"
key={index}
>
<UniLink
href={CSB_IO && `${CSB_IO}/@${sub?.character?.handle}`}
className="flex items-center space-x-2 text-sm"
>
<Avatar
className="align-middle border-2 border-white"
images={sub.character?.metadata?.content?.avatars || []}
name={
sub.character?.metadata?.content?.name ||
sub.character?.handle
}
size={40}
/>
<span>{sub.character?.metadata?.content?.name}</span>
<span className="text-zinc-400 truncate max-w-xs">
@{sub.character?.handle}
</span>
</UniLink>
<UniLink href={CSB_SCAN + "/tx/" + sub.metadata?.proof}>
<BlockchainIcon />
</UniLink>
</div>
))
) : (
<div className="py-3 text-center text-zinc-300">
No Content Yet.
</div>
)}
</InfiniteScroll>
</div>
<div className="h-16 border-t flex items-center px-5 py-4">
<Button isBlock onClick={() => setOpen(false)}>
Close
</Button>
</div>
</Modal>
)
}

View File

@ -1,20 +1,14 @@
import clsx from "clsx"
import { useRouter } from "next/router"
import { IS_PROD } from "~/lib/constants"
import { SITE_URL, CSB_SCAN, CSB_IO } from "~/lib/env"
import { useStore } from "~/lib/store"
import { Viewer } from "~/lib/types"
import { getUserContentsUrl } from "~/lib/user-contents"
import { truthy } from "~/lib/utils"
import { DashboardIcon } from "../icons/DashboardIcon"
import { Avatar } from "../ui/Avatar"
import { Button } from "../ui/Button"
import { UniLink } from "../ui/UniLink"
import { Profile } from "~/lib/types"
import { ConnectButton } from "../common/ConnectButton"
import { useAccount } from "wagmi"
import unidata from "~/lib/unidata"
import { Fragment, useEffect, useState } from "react"
import { useEffect, useState } from "react"
import toast from "react-hot-toast"
import {
useGetSubscription,
@ -26,7 +20,9 @@ import {
import { useConnectModal } from "@rainbow-me/rainbowkit"
import { BlockchainIcon } from "~/components/icons/BlockchainIcon"
import { DotsHorizontalIcon, RssIcon } from "@heroicons/react/solid"
import { Modal } from "~/components/ui/Modal"
import type { Link } from "unidata.js"
import { getSiteSubscriptions } from "~/models/site.model"
import { CharacterList } from "~/components/common/CharacterList"
export type HeaderLinkType = {
icon?: React.ReactNode
@ -160,6 +156,26 @@ export const SiteHeader: React.FC<{
},
]
const [siteSubscriptionList, setSiteSubscriptionList] = useState<Link[]>([])
const [cursor, setCursor] = useState<string>()
useEffect(() => {
if (siteSubscriptions.isSuccess) {
setSiteSubscriptionList(siteSubscriptions.data?.list || [])
setCursor(siteSubscriptions.data?.cursor)
}
}, [siteSubscriptions.isSuccess])
const loadMoreSubscriptions = async () => {
if (cursor) {
const subs = await getSiteSubscriptions({
siteId: site?.username || "",
cursor,
})
setSiteSubscriptionList((prev) => [...prev, ...(subs?.list || [])])
setCursor(subs?.cursor)
}
}
return (
<header className="border-b">
<div className="px-5 max-w-screen-md mx-auto">
@ -266,47 +282,14 @@ export const SiteHeader: React.FC<{
<ConnectButton variant="text" />
</div>
</div>
<Modal
<CharacterList
open={isFollowListOpen}
setOpen={setIsFollowListOpen}
title="Follow List"
>
<ul className="px-5">
{siteSubscriptions.data?.list?.map((sub: any, index) => (
<li
className="py-3 flex items-center space-x-2 text-sm"
key={index}
>
<UniLink
href={CSB_IO && `${CSB_IO}/@${sub?.character?.handle}`}
className="flex items-center space-x-2 text-sm"
>
<Avatar
className="align-middle border-2 border-white"
images={sub.character?.metadata?.content?.avatars || []}
name={
sub.character?.metadata?.content?.name ||
sub.character?.handle
}
size={40}
/>
<span>{sub.character?.metadata?.content?.name}</span>
<span className="text-zinc-400 truncate max-w-xs">
@{sub.character?.handle}
</span>
</UniLink>
<UniLink href={CSB_SCAN + "/tx/" + sub.metadata?.proof}>
<BlockchainIcon />
</UniLink>
</li>
))}
</ul>
<div className="h-16 border-t flex items-center px-5">
<Button isBlock onClick={() => setIsFollowListOpen(false)}>
Close
</Button>
</div>
</Modal>
loadMore={loadMoreSubscriptions}
hasMore={!!cursor}
list={siteSubscriptionList}
></CharacterList>
</header>
)
}

View File

@ -28,7 +28,7 @@ export const Modal: React.FC<{
{/* The actual dialog panel */}
<Dialog.Panel
className={clsx(
`mx-auto rounded-lg bg-white w-full shadow-modal max-h-full overflow-y-auto`,
`mx-auto rounded-lg bg-white w-full shadow-modal max-h-full overflow-y-auto flex flex-col`,
size === "md"
? `max-w-md`
@ -38,7 +38,7 @@ export const Modal: React.FC<{
)}
>
{title && (
<Dialog.Title className="text-lg border-b h-14 flex items-center px-5 space-x-2">
<Dialog.Title className="text-lg border-b h-14 flex items-center px-5 space-x-2 py-4">
{titleIcon && <span>{titleIcon}</span>}
<span>{title}</span>
</Dialog.Title>

View File

@ -115,12 +115,16 @@ export const getSubscription = async (data: {
return !!links?.list?.length
}
export const getSiteSubscriptions = async (data: { siteId: string }) => {
export const getSiteSubscriptions = async (data: {
siteId: string
cursor?: string
}) => {
const links = await unidata.links.get({
source: "Crossbell Link",
identity: data.siteId,
platform: "Crossbell",
reversed: true,
cursor: data.cursor,
})
await Promise.all(