feat: followers pagination loading
This commit is contained in:
parent
8dd03aa6d5
commit
657ac8090a
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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==}
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
Loading…
Reference in New Issue