diff --git a/package.json b/package.json index b83e5827..6f77206b 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 45f23f93..87837d1f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -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==} diff --git a/src/components/common/CharacterList.tsx b/src/components/common/CharacterList.tsx new file mode 100644 index 00000000..d3954798 --- /dev/null +++ b/src/components/common/CharacterList.tsx @@ -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 + list: any[] + title: string +}> = ({ open, setOpen, hasMore, loadMore, list, title }) => { + return ( + +
+ + Loading ... +
+ } + useWindow={false} + > + {list?.length ? ( + list.map((sub: any, index) => ( +
+ + + {sub.character?.metadata?.content?.name} + + @{sub.character?.handle} + + + + + +
+ )) + ) : ( +
+ No Content Yet. +
+ )} + + +
+ +
+
+ ) +} diff --git a/src/components/site/SiteHeader.tsx b/src/components/site/SiteHeader.tsx index 784cb5d5..6cca1c6c 100644 --- a/src/components/site/SiteHeader.tsx +++ b/src/components/site/SiteHeader.tsx @@ -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([]) + const [cursor, setCursor] = useState() + 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 (
@@ -266,47 +282,14 @@ export const SiteHeader: React.FC<{
- -
    - {siteSubscriptions.data?.list?.map((sub: any, index) => ( -
  • - - - {sub.character?.metadata?.content?.name} - - @{sub.character?.handle} - - - - - -
  • - ))} -
-
- -
-
+ loadMore={loadMoreSubscriptions} + hasMore={!!cursor} + list={siteSubscriptionList} + >
) } diff --git a/src/components/ui/Modal.tsx b/src/components/ui/Modal.tsx index 62562561..9a04a7e3 100644 --- a/src/components/ui/Modal.tsx +++ b/src/components/ui/Modal.tsx @@ -28,7 +28,7 @@ export const Modal: React.FC<{ {/* The actual dialog panel */} {title && ( - + {titleIcon && {titleIcon}} {title} diff --git a/src/models/site.model.ts b/src/models/site.model.ts index bfc08579..d2bd7bcd 100644 --- a/src/models/site.model.ts +++ b/src/models/site.model.ts @@ -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(