diff --git a/packages/client/dashboard/src/App.tsx b/packages/client/dashboard/src/App.tsx index 9f0e641..d4cf6f9 100644 --- a/packages/client/dashboard/src/App.tsx +++ b/packages/client/dashboard/src/App.tsx @@ -36,7 +36,7 @@ import NoMatch from './container/NoMatch' import Header from './components/nav/Header' import Nav from './components/nav/Nav' -import ModelList from './components/model/ModelList' +import DappModelAndComposites from './components/model/DappModelAndComposites' import ExploreComposite from './container/ExploreComposite' dayjs.extend(relativeTime) @@ -182,7 +182,7 @@ function ModelEditorLayout () { return ( - { setSelectModel(data) diff --git a/packages/client/dashboard/src/components/icons/CheckronDown.tsx b/packages/client/dashboard/src/components/icons/ChevronDown.tsx similarity index 100% rename from packages/client/dashboard/src/components/icons/CheckronDown.tsx rename to packages/client/dashboard/src/components/icons/ChevronDown.tsx diff --git a/packages/client/dashboard/src/components/model/CodeDownload.tsx b/packages/client/dashboard/src/components/model/CodeDownload.tsx index 51e3dd7..9b33fae 100644 --- a/packages/client/dashboard/src/components/model/CodeDownload.tsx +++ b/packages/client/dashboard/src/components/model/CodeDownload.tsx @@ -1,7 +1,7 @@ import FileSaver from 'file-saver' import { useState } from 'react' import styled from 'styled-components' -import ChevronDown from '../icons/CheckronDown' +import ChevronDown from '../icons/ChevronDown' import DownloadIcon from '../icons/DownloadIcon' import { Code } from './ModelSDK' diff --git a/packages/client/dashboard/src/components/model/CreateCompositeModal.tsx b/packages/client/dashboard/src/components/model/CreateNewComposite.tsx similarity index 99% rename from packages/client/dashboard/src/components/model/CreateCompositeModal.tsx rename to packages/client/dashboard/src/components/model/CreateNewComposite.tsx index cb03568..d739c97 100644 --- a/packages/client/dashboard/src/components/model/CreateCompositeModal.tsx +++ b/packages/client/dashboard/src/components/model/CreateNewComposite.tsx @@ -11,7 +11,7 @@ import { createCompositeFromBrowser } from '../../utils/composeDBUtils' import { useCeramicNodeCtx } from '../../context/CeramicNodeCtx' import { startIndexModels } from '../../api/model' -export default function CreateCompositeModal ({ +export default function CreateNewComposite ({ closeModal, loadDappComposites, defaultName, diff --git a/packages/client/dashboard/src/components/model/ModelList.tsx b/packages/client/dashboard/src/components/model/DappModelAndComposites.tsx similarity index 83% rename from packages/client/dashboard/src/components/model/ModelList.tsx rename to packages/client/dashboard/src/components/model/DappModelAndComposites.tsx index 51920b3..9bb7f70 100644 --- a/packages/client/dashboard/src/components/model/ModelList.tsx +++ b/packages/client/dashboard/src/components/model/DappModelAndComposites.tsx @@ -11,11 +11,11 @@ import { ModalOverlay, Popover } from 'react-aria-components' -import { useLocation, useNavigate } from 'react-router-dom' +import { useLocation } from 'react-router-dom' import styled from 'styled-components' import { deleteDappComposites, getDappComposites } from '../../api/composite' import { updateDapp } from '../../api/dapp' -import { getDappModels, getModelsInfoByIds } from '../../api/model' +import { getModelsInfoByIds } from '../../api/model' import { useAppCtx } from '../../context/AppCtx' import { useCeramicNodeCtx } from '../../context/CeramicNodeCtx' import useIsOwner from '../../hooks/useIsOwner' @@ -26,17 +26,19 @@ import { ModelStream, Network } from '../../types.d' +import { getCompositeDefaultSchema } from '../../utils/composedb-types/schemas' import { shortPubKey } from '../../utils/shortPubKey' import CopyTint from '../common/CopyTint' import MergeIcon from '../icons/MergeIcon' import PlusIcon from '../icons/PlusIcon' import TrashIcon from '../icons/TrashIcon' import NoCeramicNodeModal from '../node/NoCeramicNodeModal' -import CreateCompositeModal from './CreateCompositeModal' +import CreateNewComposite from './CreateNewComposite' import CreateNewModel from './CreateNewModel' -import FavoriteModel from './FavoriteModal' import MergeModal from './MergeModal' -import { getCompositeDefaultSchema } from '../../utils/composedb-types/schemas' +import CloseIcon from '../icons/CloseIcon' +import ModelList from './ExploreModelList' +import { CompositeList } from './ExploreCompositeList' enum OPEN_MODAL { NONE, @@ -44,11 +46,11 @@ enum OPEN_MODAL { ADD_FROM_ALL_MODELS, ADD_FROM_FAVORITE_MODELS, CREATE_NEW_COMPOSITE, - ADD_FROM_ALL_COMPOSITES, - ADD_FROM_FAVORITE_COMPOSITES + ADD_FROM_ALL_COMPOSITES + // ADD_FROM_FAVORITE_COMPOSITES } -export default function ModelList ({ +export default function DappModelAndComposites ({ editable, selectComposite, setSelectComposite, @@ -66,7 +68,6 @@ export default function ModelList ({ const { loadDapps, currDapp } = useAppCtx() const { appId, selectedDapp } = useSelectedDapp() const { currCeramicNode } = useCeramicNodeCtx() - const navigate = useNavigate() const [loading, setLoading] = useState(false) const [dappModels, setDappModels] = useState() const [dappComposites, setDappComposites] = useState([]) @@ -200,16 +201,16 @@ export default function ModelList ({ const [openModal, setOpenModal] = useState(OPEN_MODAL.NONE) if (loading) { return ( - +
-
+ ) } return ( - +

Models

{editable && @@ -237,7 +238,7 @@ export default function ModelList ({ { if (id === 'explore') { - navigate(`/dapp/${appId}/explore/model`) + setOpenModal(OPEN_MODAL.ADD_FROM_ALL_MODELS) return } if (id === 'favorite') { @@ -250,7 +251,7 @@ export default function ModelList ({ } }} > - Explore Models + Add From Popular Models Add From Favorite Create New Model @@ -308,7 +309,7 @@ export default function ModelList ({ { if (id === 'explore') { - navigate(`/dapp/${appId}/explore/composite`) + setOpenModal(OPEN_MODAL.ADD_FROM_ALL_COMPOSITES) return } if (id === 'create') { @@ -317,7 +318,9 @@ export default function ModelList ({ } }} > - Explore Composites + + Add From Popular Composites + Create New Composites @@ -334,19 +337,21 @@ export default function ModelList ({ /> )} + {/* CREATE_NEW_MODEL modal */} setOpenModal(OPEN_MODAL.NONE)} > {({ close }) => } + {/* CREATE_NEW_COMPOSITE modal */} setOpenModal(OPEN_MODAL.NONE)} > {({ close }) => ( - + {/* ADD_FROM_FAVORITE_MODELS modal */} setOpenModal(OPEN_MODAL.NONE)} > - {({ close }) => } + + {({ close }) => ( + +
+

My Favorite Models

+ +
+
+ +
+
+ )} +
+
+ {/* ADD_FROM_ALL_MODELS modal */} + setOpenModal(OPEN_MODAL.NONE)} + > + + {({ close }) => ( + +
+

Popular Models

+ +
+
+ +
+
+ )} +
+
+ {/* ADD_FROM_ALL_COMPOSITES modal */} + setOpenModal(OPEN_MODAL.NONE)} + > + + {({ close }) => ( + +
+

Popular Composites

+ +
+
+ +
+
+ )} +
{editable && isOwner && ( @@ -383,7 +445,7 @@ export default function ModelList ({ )} - + ) } @@ -530,7 +592,7 @@ function RemoveButton ({ ) } -const ListBox = styled.div` +const Box = styled.div` background: #1b1e23; border: 1px solid #39424c; border-radius: 20px; @@ -651,3 +713,36 @@ const MergeBox = styled.div` gap: 10px; } ` +const DialogBox = styled.div` + display: flex; + flex-direction: column; + padding: 20px; + gap: 20px; + height: calc(100vh - 100px); + position: relative; + width: 1240px; + margin: 0 auto; + + background: #1b1e23; + border-radius: 20px; + + > div.title { + flex: 0 0 42px; + display: flex; + align-items: center; + justify-content: space-between; + + color: #ffffff; + > h1 { + margin: 0; + font-style: italic; + font-weight: 700; + font-size: 24px; + line-height: 28px; + } + } + > div.content { + height: 0; + flex: 1; + } +` diff --git a/packages/client/dashboard/src/components/model/ExploreCompositeList.tsx b/packages/client/dashboard/src/components/model/ExploreCompositeList.tsx new file mode 100644 index 0000000..367a3d7 --- /dev/null +++ b/packages/client/dashboard/src/components/model/ExploreCompositeList.tsx @@ -0,0 +1,297 @@ +import { useSession } from '@us3r-network/auth-with-rainbowkit' +import dayjs from 'dayjs' +import { useCallback, useEffect, useRef, useState } from 'react' +import { + Button, + Dialog, + DialogTrigger, + Modal, + ModalOverlay +} from 'react-aria-components' +import InfiniteScroll from 'react-infinite-scroll-component' +import styled from 'styled-components' +import { bindingDappComposites, getComposites } from '../../api/composite' +import { updateDapp } from '../../api/dapp' +import { startIndexModels } from '../../api/model' +import { PAGE_SIZE, S3_SCAN_URL } from '../../constants' +import { useAppCtx } from '../../context/AppCtx' +import { useCeramicNodeCtx } from '../../context/CeramicNodeCtx' +import useSelectedDapp from '../../hooks/useSelectedDapp' +import { CeramicStatus, DappCompositeDto, Network } from '../../types.d' +import { shortPubKey } from '../../utils/shortPubKey' +import { TableBox, TableContainer } from '../common/TableBox' +import CheckCircleIcon from '../icons/CheckCircleIcon' +import LayoutIcon from '../icons/LayoutIcon' +import PlusCircleIcon from '../icons/PlusCircleIcon' +import CreateCompositeModal from './CreateNewComposite' +import { Dapps } from '../model/ExploreModelList' +import NoCeramicNodeModal from '../node/NoCeramicNodeModal' + +export function CompositeList ({ + searchText +}: { + searchText?: string +}) { + const { selectedDapp } = useSelectedDapp() + const { currCeramicNode } = useCeramicNodeCtx() + const [composites, setComposites] = useState>([]) + const [hasMore, setHasMore] = useState(true) + const pageNum = useRef(1) + const fetchComposites = useCallback(async () => { + setComposites([]) + setHasMore(true) + const resp = await getComposites({ + name: searchText, + network: (selectedDapp?.network as Network) || Network.TESTNET + }) + const list = resp.data.data + setComposites(list) + setHasMore(list.length >= PAGE_SIZE) + pageNum.current = 1 + }, [searchText, selectedDapp?.network]) + + const fetchMoreComposites = useCallback( + async (pageNumber: number) => { + const resp = await getComposites({ + name: searchText, + pageNumber, + network: (selectedDapp?.network as Network) || Network.TESTNET + }) + const list = resp.data.data + setHasMore(list.length >= PAGE_SIZE) + setComposites([...composites, ...list]) + }, + [composites, searchText, selectedDapp?.network] + ) + + useEffect(() => { + fetchComposites() + }, [fetchComposites]) + + const lists = composites + return ( + <> + { + pageNum.current += 1 + fetchMoreComposites(pageNum.current) + console.log('fetch more') + }} + hasMore={hasMore} + loader={Loading...} + > + + + + + Composite Name + Stream ID + Release Date + Dapps + + + + + + {lists.map((item, idx) => { + return ( + + +
{item.name}
+ + +
+ {item.streamId ? ( + + {shortPubKey(item.streamId, { + len: 8, + split: '-' + })} + + ) : ( + '-' + )} +
+ + +
+ {dayjs(item.createdAt).format('YYYY-MM-DD HH:mm:ss') || + '-'} +
+ + + + + + {/* */} + dapp.id === selectedDapp?.id + ) !== -1 + } + ceramicNodeId={ + currCeramicNode && + currCeramicNode.status === CeramicStatus.RUNNING + ? currCeramicNode?.id + : undefined + } + /> + + + ) + })} + +
+
+
+ {!hasMore && no more data} + + ) +} + +function Actions ({ + composite, + hasIndexed, + ceramicNodeId +}: { + composite: DappCompositeDto + hasIndexed: boolean + ceramicNodeId?: number +}) { + const session = useSession() + + const { loadDapps } = useAppCtx() + const { selectedDapp } = useSelectedDapp() + const [adding, setAdding] = useState(false) + + const bindComposite = useCallback(async () => { + if (!session || !selectedDapp) return + if (!ceramicNodeId) return + if (!hasIndexed) { + bindingDappComposites({ + compositeId: composite.id, + dapp: selectedDapp, + did: session.serialize() + }) + .then( + () => { + const models = JSON.parse(composite.composite).models + console.log(Object.keys(models)) + const modelIds = Object.keys(models) + startIndexModels({ + modelIds, + network: selectedDapp.network, + didSession: session.serialize() + }).catch(console.error) + }, + err => { + console.error(err) + } + ) + .catch(console.error) + } + try { + setAdding(true) + const composites = selectedDapp.composites || [] + composites.push(composite) + await updateDapp( + { ...selectedDapp, composites }, + session.serialize(), + ceramicNodeId + ) + await loadDapps() + } catch (err) { + console.error(err) + } finally { + setAdding(false) + } + }, [session, selectedDapp, ceramicNodeId, hasIndexed, composite, loadDapps]) + return ( + + + + + + + {({ close }) => ( + + )} + + + + + {adding ? ( + + ) : ( + <> + {hasIndexed ? ( + + ) : ceramicNodeId ? ( + + ) : ( + + + + + + {({ close }) => } + + + + + )} + + )} + + ) +} + +const OpsBox = styled.div` + display: flex; + align-items: center; + gap: -5px; + img { + width: 17px; + } +` + +const Loading = styled.div` + padding: 20px; + text-align: center; + color: gray; +` diff --git a/packages/client/dashboard/src/components/model/ExploreModelList.tsx b/packages/client/dashboard/src/components/model/ExploreModelList.tsx new file mode 100644 index 0000000..462e2e4 --- /dev/null +++ b/packages/client/dashboard/src/components/model/ExploreModelList.tsx @@ -0,0 +1,531 @@ +import { useSession } from '@us3r-network/auth-with-rainbowkit' +import dayjs from 'dayjs' +import { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import { + Button, + Dialog, + DialogTrigger, + Modal, + ModalOverlay +} from 'react-aria-components' +import InfiniteScroll from 'react-infinite-scroll-component' +import styled from 'styled-components' +import { updateDapp } from '../../api/dapp' +import { PAGE_SIZE } from '../../constants' +import { + getModelStreamList, + getModelsInfoByIds, + startIndexModel +} from '../../api/model' +import { ImgOrName } from '../common/ImgOrName' +import { TableBox, TableContainer } from '../common/TableBox' +import CheckCircleIcon from '../icons/CheckCircleIcon' +import PlusCircleIcon from '../icons/PlusCircleIcon' +import StarGoldIcon from '../icons/StarGoldIcon' +import StarIcon from '../icons/StarIcon' +import NoCeramicNodeModal from '../node/NoCeramicNodeModal' +import { S3_SCAN_URL } from '../../constants' +import { PersonalCollection, useAppCtx } from '../../context/AppCtx' +import { useCeramicNodeCtx } from '../../context/CeramicNodeCtx' +import useSelectedDapp from '../../hooks/useSelectedDapp' +import { CeramicStatus, ClientDApp, ModelStream, Network } from '../../types.d' +import { shortPubKey } from '../../utils/shortPubKey' + +export default function ModelList ({ + searchText, + filterStar +}: { + searchText?: string + filterStar?: boolean +}) { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { s3ModelCollection, selectedDapp } = useSelectedDapp() + const { currCeramicNode } = useCeramicNodeCtx() + const session = useSession() + const [models, setModels] = useState>([]) + const [starModels, setStarModels] = useState>([]) + const [hasMore, setHasMore] = useState(true) + const pageNum = useRef(1) + const [personalCollections, setPersonalCollections] = useState< + PersonalCollection[] + >([]) + const fetchPersonalCollections = useCallback(async () => { + if (!session) return + s3ModelCollection.authComposeClient(session) + try { + const personal = await s3ModelCollection.queryPersonalCollections({ + first: 500 + }) + if (personal.errors) throw new Error(personal.errors[0].message) + const collected = personal.data?.viewer.modelCollectionList + + if (collected) { + setPersonalCollections( + collected?.edges + .filter(item => item.node && item.node.revoke === false) + .map(item => { + return { + modelId: item.node.modelID, + id: item.node.id!, + revoke: !!item.node.revoke + } + }) + ) + } + } catch (error) { + console.error('error -----', error) + } + }, [s3ModelCollection, session]) + + const fetchStarModels = useCallback(async () => { + const ids = personalCollections + .filter(item => item.revoke === false) + .map(item => { + return item.modelId + }) + if (ids.length === 0) { + setStarModels([]) + return + } + + try { + const resp = await getModelsInfoByIds({ + network: (selectedDapp?.network as Network) || Network.TESTNET, + ids + }) + if (resp.data.code !== 0) { + throw new Error(resp.data.msg) + } + + const list = resp.data.data + setStarModels([...list]) + } catch (error) { + console.error(error) + } + }, [personalCollections, selectedDapp?.network]) + + const fetchModel = useCallback(async () => { + setModels([]) + setHasMore(true) + const resp = await getModelStreamList({ + name: searchText || '', + network: (selectedDapp?.network as Network) || Network.TESTNET + }) + const list = resp.data.data + setModels(list) + setHasMore(list.length >= PAGE_SIZE) + pageNum.current = 1 + }, [searchText, selectedDapp?.network]) + + const fetchMoreModel = useCallback( + async (pageNumber: number) => { + const resp = await getModelStreamList({ + name: searchText || '', + network: (selectedDapp?.network as Network) || Network.TESTNET, + pageNumber + }) + const list = resp.data.data + setHasMore(list.length >= PAGE_SIZE) + setModels([...models, ...list]) + }, + [models, searchText, selectedDapp?.network] + ) + + useEffect(() => { + fetchModel() + fetchPersonalCollections() + }, [fetchModel, fetchPersonalCollections]) + + useEffect(() => { + fetchStarModels().catch(err => { + setStarModels([]) + console.error(err) + }) + }, [fetchStarModels]) + + const lists = useMemo(() => { + if (!filterStar) return models + return starModels + }, [filterStar, models, starModels]) + + return ( + + { + pageNum.current += 1 + fetchMoreModel(pageNum.current) + console.log('fetch more') + }} + hasMore={filterStar ? false : hasMore} + loader={Loading...} + > + + + + + Model Name + Description + ID + Usage Count + 7 Days Usage + Release Date + Dapps + + + + + + {lists.map((item, idx) => { + const hasStarItem = personalCollections.find( + starItem => starItem.modelId === item.stream_id + ) + return ( + + + + + +
{item.stream_content.description}
+ + + + + + + + {item.recentlyUseCount || '-'} + +
+ {(item.last_anchored_at && + dayjs(item.created_at).format( + 'YYYY-MM-DD HH:mm:ss' + )) || + '-'} +
+ + + + + + {/* */} + + + + ) + })} + +
+
+
+ {!filterStar && !hasMore && no more data} +
+ ) +} + +export function Dapps ({ dapps }: { dapps: ClientDApp[] }) { + const apps = useMemo(() => { + const data = [...dapps] + if (data.length > 3) + return { data: data.slice(0, 3), left: data.length - 3 } + return { data, left: 0 } + }, [dapps]) + + return ( + + {apps.data.length > 0 + ? apps.data.map((item, idx) => { + return ( + + + + ) + }) + : 'None'} + {apps.left > 0 && {apps.left}+} + + ) +} + +const Box = styled.div` + height: 100%; + overflow: auto; + position: relative; +` + +const DappBox = styled.div` + display: flex; + gap: 5px; + overflow: hidden; + color: #fff; + font-family: Rubik; + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: normal; + a { + > span { + color: #fff; + width: 36px; + height: 36px; + border-radius: 10px; + border: 1px solid #718096; + display: inline-flex; + align-items: center; + justify-content: center; + overflow: hidden; + &.name { + font-size: 20px; + font-weight: 500; + } + &.left { + border: none; + color: #fff; + justify-content: start; + font-family: Rubik; + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: normal; + } + > img { + width: 100%; + height: 100%; + object-fit: cover; + flex-shrink: 0; + } + } + } +` + +function Actions ({ + hasStarItem, + fetchPersonal, + stream_id, + hasIndexed, + ceramicNodeId +}: { + hasIndexed: boolean + stream_id: string + hasStarItem: + | { + modelId: string + id: string + revoke: boolean + } + | undefined + fetchPersonal: () => void + ceramicNodeId?: number +}) { + const session = useSession() + const { s3ModelCollection } = useSelectedDapp() + const [staring, setStaring] = useState(false) + + const { loadDapps } = useAppCtx() + const { selectedDapp } = useSelectedDapp() + const [adding, setAdding] = useState(false) + const addModelToDapp = useCallback( + async (modelId: string) => { + if (!session || !selectedDapp) return + if (!ceramicNodeId) return + if (!hasIndexed) { + startIndexModel({ + modelId, + network: selectedDapp.network as Network, + didSession: session.serialize() + }).catch(console.error) + } + try { + setAdding(true) + const models = selectedDapp.models || [] + models.push(modelId) + await updateDapp( + { ...selectedDapp, models }, + session.serialize(), + ceramicNodeId + ) + await loadDapps() + } catch (err) { + console.error(err) + } finally { + setAdding(false) + } + }, + [loadDapps, selectedDapp, session, setAdding, hasIndexed, ceramicNodeId] + ) + + const collectModel = useCallback( + async (modelId: string, id?: string, revoke?: boolean) => { + if (staring) return + try { + if (!session) return + s3ModelCollection.authComposeClient(session) + setStaring(true) + if (id) { + const resp = await s3ModelCollection.updateCollection(id, { + revoke: !revoke + }) + if (resp.errors) { + throw new Error(resp.errors[0].message) + } + } else { + const resp = await s3ModelCollection.createCollection({ + modelID: modelId, + revoke: false + }) + if (resp.errors) { + throw new Error(resp.errors[0].message) + } + } + await fetchPersonal() + } catch (error) { + console.error(error) + } finally { + setStaring(false) + } + }, + [session, s3ModelCollection, fetchPersonal, staring] + ) + + const modelId = stream_id + + return ( + + {(staring && ( + + )) || ( + + )} + + {adding ? ( + + ) : ( + <> + {selectedDapp?.models?.includes(modelId) ? ( + + ) : ceramicNodeId ? ( + + ) : ( + + + + + + {({ close }) => } + + + + + )} + + )} + + ) +} + +const OpsBox = styled.div` + display: flex; + align-items: center; + gap: 5px; + + img { + width: 17px; + } +` +const Loading = styled.div` + padding: 20px; + text-align: center; + color: gray; +` diff --git a/packages/client/dashboard/src/components/model/FavoriteModal.tsx b/packages/client/dashboard/src/components/model/FavoriteModal.tsx deleted file mode 100644 index 90d92ff..0000000 --- a/packages/client/dashboard/src/components/model/FavoriteModal.tsx +++ /dev/null @@ -1,308 +0,0 @@ -import { useSession } from '@us3r-network/auth-with-rainbowkit' -import dayjs from 'dayjs' -import { useCallback, useEffect, useState } from 'react' -import styled from 'styled-components' -import { updateDapp } from '../../api/dapp' -import { getModelsInfoByIds, startIndexModel } from '../../api/model' -import { S3_SCAN_URL } from '../../constants' -import { PersonalCollection, useAppCtx } from '../../context/AppCtx' -import { useCeramicNodeCtx } from '../../context/CeramicNodeCtx' -import useSelectedDapp from '../../hooks/useSelectedDapp' -import { CeramicStatus, ModelStream, Network } from '../../types.d' -import { shortPubKey } from '../../utils/shortPubKey' -import { TableBox, TableContainer } from '../common/TableBox' -import CheckCircleIcon from '../icons/CheckCircleIcon' -import CloseIcon from '../icons/CloseIcon' -import PlusCircleIcon from '../icons/PlusCircleIcon' -import StarGoldIcon from '../icons/StarGoldIcon' - -export default function FavoriteModal({ - closeModal, -}: { - closeModal: () => void -}) { - return ( - -
-

My Favorite Models

- -
-
- -
-
- ) -} - -function ModelList() { - const { s3ModelCollection, selectedDapp } = useSelectedDapp() - const session = useSession() - const { currCeramicNode } = useCeramicNodeCtx() - const [starModels, setStarModels] = useState>([]) - const [personalCollections, setPersonalCollections] = useState< - PersonalCollection[] - >([]) - const fetchPersonalCollections = useCallback(async () => { - if (!session) return - s3ModelCollection.authComposeClient(session) - try { - const personal = await s3ModelCollection.queryPersonalCollections({ - first: 500, - }) - if (personal.errors) throw new Error(personal.errors[0].message) - const collected = personal.data?.viewer.modelCollectionList - - if (collected) { - setPersonalCollections( - collected?.edges - .filter((item) => item.node && item.node.revoke === false) - .map((item) => { - return { - modelId: item.node.modelID, - id: item.node.id!, - revoke: !!item.node.revoke, - } - }) - ) - } - } catch (error) { - console.error(error) - } - }, [s3ModelCollection, session]) - - const fetchStarModels = useCallback(async () => { - const ids = personalCollections - .filter((item) => item.revoke === false) - .map((item) => { - return item.modelId - }) - if (ids.length === 0) { - setStarModels([]) - return - } - - try { - const resp = await getModelsInfoByIds({ - network: (selectedDapp?.network as Network) || Network.TESTNET, - ids, - }) - if (resp.data.code !== 0) { - throw new Error(resp.data.msg) - } - - const list = resp.data.data - setStarModels([...list]) - } catch (error) { - console.error(error) - } - }, [personalCollections, selectedDapp?.network]) - - useEffect(() => { - fetchPersonalCollections() - }, [fetchPersonalCollections]) - - useEffect(() => { - fetchStarModels().catch(console.error) - }, [fetchStarModels]) - - return ( - - - - - - Model Name - Description - ID - Usage Count - Release Date - - - - - {starModels.map((item, idx) => { - return ( - - - - - -
- {item.stream_content.description} -
- - - - - - - - -
- {(item.last_anchored_at && - dayjs(item.created_at).format('YYYY-MM-DD HH:mm:ss')) || - '-'} -
- - - - - - - ) - })} - -
-
-
- ) -} - -export function OpsBtns({ - modelId, - hasIndexed, - ceramicNodeId, -}: { - modelId: string - hasIndexed: boolean - ceramicNodeId?: number -}) { - const { loadDapps } = useAppCtx() - const session = useSession() - const { selectedDapp } = useSelectedDapp() - const [adding, setAdding] = useState(false) - const addToModelList = useCallback( - async (modelId: string) => { - if (!session || !selectedDapp) return - if (!hasIndexed) { - startIndexModel({ - modelId, - network: selectedDapp.network as Network, - didSession: session.serialize(), - }).catch(console.error) - } - if (!ceramicNodeId) return - try { - setAdding(true) - const models = selectedDapp.models || [] - models.push(modelId) - await updateDapp({ ...selectedDapp, models }, session.serialize(),ceramicNodeId) - await loadDapps() - } catch (err) { - console.error(err) - } finally { - setAdding(false) - } - }, - [ceramicNodeId, hasIndexed, loadDapps, selectedDapp, session] - ) - return ( -
- - {adding ? ( - - ) : ( - <> - {selectedDapp?.models?.includes(modelId) ? ( - - ) : ( - - )} - - )} -
- ) -} - -const FavoriteBox = styled.div` - display: flex; - flex-direction: column; - padding: 20px; - gap: 20px; - min-height: calc(100vh - 300px); - position: relative; - width: 1240px; - margin: 0 auto; - - background: #1b1e23; - border-radius: 20px; - - > div.title { - display: flex; - align-items: center; - justify-content: space-between; - - color: #ffffff; - > h1 { - margin: 0; - font-style: italic; - font-weight: 700; - font-size: 24px; - line-height: 28px; - } - } -` - -const ListBox = styled.div` - .btns { - display: flex; - align-items: center; - - .loading { - width: 20px; - } - } -` diff --git a/packages/client/dashboard/src/components/nav/Nav.tsx b/packages/client/dashboard/src/components/nav/Nav.tsx index 1b2e2bf..aa217a5 100644 --- a/packages/client/dashboard/src/components/nav/Nav.tsx +++ b/packages/client/dashboard/src/components/nav/Nav.tsx @@ -1,5 +1,5 @@ import React, { useMemo, useState } from 'react' -import { Link, NavLink, useSearchParams } from 'react-router-dom' +import { Link, NavLink } from 'react-router-dom' import styled from 'styled-components' import { DOCS_URL } from '../../constants' import ChartIcon from '../icons/ChartIcon' @@ -12,113 +12,167 @@ import InfoIcon from '../icons/InfoIcon' import LayoutIcon from '../icons/LayoutIcon' import NodeIcon from '../icons/NodeIcon' import SdkIcon from '../icons/SdkIcon' -import StarGoldIcon from '../icons/StarGoldIcon' -import StarIcon from '../icons/StarIcon' import TerminalIcon from '../icons/TerminalIcon' +import ChevronDown from '../icons/ChevronDown' -export default function Nav({ appId }: { appId: string }) { - const [open, setOpen] = useState(true) - const [searchParams] = useSearchParams() +type NavItem = { + path?: string + name: string + icon: any + items?: NavItem[] +} - const filterStar = useMemo(() => { - return searchParams.get('filterStar') || '' - }, [searchParams]) +export default function Nav ({ appId }: { appId: string }) { + const [open, setOpen] = useState(true) + const navItems = useMemo(() => { + return [ + { + path: `/dapp/${appId}/index`, + name: 'Home', + icon: HomeIcon + }, + { + // path: `/dapp/${appId}/explore`, + name: 'Explore', + icon: ExploreIcon, + items: [ + { + path: `/dapp/${appId}/explore/model`, + name: 'Models', + icon: ExploreIcon + }, + { + path: `/dapp/${appId}/explore/composite`, + name: 'Composites', + icon: ExploreIcon + }, + { + path: `/dapp/${appId}/components`, + name: 'Components', + icon: ComponentIcon + } + ] + }, + { + // path: `/dapp/${appId}/build`, + name: 'Build', + icon: InfoIcon, + items: [ + { + path: `/dapp/${appId}/model-editor`, + name: 'Compose', + icon: LayoutIcon + }, + { + path: `/dapp/${appId}/model-playground`, + name: 'Playground', + icon: TerminalIcon + }, + { + path: `/dapp/${appId}/model-sdk`, + name: 'SDK', + icon: SdkIcon + }, + { + path: `/dapp/${appId}/statistic`, + name: 'Metrics', + icon: ChartIcon + } + ] + }, + { + path: `/dapp/${appId}/node`, + name: 'Node', + icon: NodeIcon + }, + { + path: `/dapp/${appId}/info`, + name: 'Info', + icon: InfoIcon + } + ] + }, [appId]) return (
-
-
- {[ - { - path: `/dapp/${appId}/index`, - name: 'Home', - icon: HomeIcon, - }, - { - path: `/dapp/${appId}/node`, - name: 'Node Deployment', - icon: NodeIcon, - }, - { - path: `/dapp/${appId}/model-editor`, - name: 'Model Editor', - icon: LayoutIcon, - }, - { - path: `/dapp/${appId}/model-playground`, - name: 'Model Playground', - icon: TerminalIcon, - }, - { - path: `/dapp/${appId}/model-sdk`, - name: 'Model SDK', - icon: SdkIcon, - }, - { - path: `/dapp/${appId}/statistic`, - name: 'Model Metrics', - icon: ChartIcon, - }, - { - path: `/dapp/${appId}/components`, - name: 'S3 Components', - icon: ComponentIcon, - }, - { - path: `/dapp/${appId}/info`, - name: 'Info', - icon: InfoIcon, - }, - ].map((item) => { - return ( - - {({ isActive }) => ( -
- {React.createElement(item.icon, { isActive })} - {item.name} -
- )} -
- ) - })} +
+
+ {navItems.map(item => ( + + ))}
-
- - {({ isActive }) => ( -
- - Explore -
- )} -
- - -
- {(filterStar && ) || } - Favorite +
+ +
+ + S3 Scan
- - -
+ + +
Document
-
setOpen(!open)}> + {/*
setOpen(!open)}> -
+
*/}
) } + +function NavItemRenderer ({ + item, + level = 0, +}: { + item: NavItem + level?: number +}) { + return ( + +
+ {item.path ? ( + + {({ isActive }) => ( +
+ {React.createElement(item.icon, { isActive })} + {item.name} +
+ )} +
+ ) : ( +
+ {React.createElement(item.icon)} + {item.name} +
+ )} + {item.items && ( +
+ +
+ )} +
+ {item.items && ( +
+ {item.items.map(item => ( + + ))} +
+ )} +
+ ) +} + + const NavContainer = styled.nav<{ open?: boolean }>` > div { height: calc(100vh - 60px); - width: ${(props) => (props.open ? '200px' : '57px')}; + width: ${props => (props.open ? '200px' : '57px')}; top: 60px; bottom: 0; @@ -134,44 +188,46 @@ const NavContainer = styled.nav<{ open?: boolean }>` align-items: center; justify-content: space-between; - .active .item { - background: #14171a; - color: #fff; - } - .item { - padding: 10px; - border-radius: 10px; - color: #718096; - position: relative; + .item-container { display: flex; + flex-direction: row; + justify-content: space-between; align-items: center; - gap: 10px; - margin-top: 20px; - overflow: hidden; - > span { - opacity: ${(props) => (props.open ? 1 : 0)}; - position: absolute; - left: 37px; - width: 150px; - transition: opacity 0.09s ease-in-out; - } + gap: 1px; + } + .item { + flex: 1; + padding: 10px; + border-radius: 10px; + color: #718096; + position: relative; + display: flex; + align-items: center; + gap: 10px; + overflow: hidden; + > span { + opacity: ${props => (props.open ? 1 : 0)}; + transition: opacity 0.09s ease-in-out; + } - &.active { - background: #14171a; - color: #fff; + &.active { + background: #14171a; + color: #fff; + } } - } .top { width: 100%; padding: 10px; box-sizing: border-box; - display: block; + display: flex; + flex-direction: column; + gap: 20px; } .bottom { width: 100%; - margin-bottom: 20px; + margin-bottom: 10px; padding: 10px; box-sizing: border-box; display: block; @@ -194,14 +250,22 @@ const NavContainer = styled.nav<{ open?: boolean }>` justify-content: center; cursor: pointer; transition: transform 0.1s ease-in-out; - transform: ${(props) => + transform: ${props => props.open ? `rotate(180deg)` : `rotate(0deg)`}; } } } } +` - .star { - cursor: pointer; +const NavItemBox = styled.div` + display: flex; + flex-direction: column; + gap: 6px; + .sub { + margin-left: 24px; + display: flex; + flex-direction: column; + gap: 6px; } ` diff --git a/packages/client/dashboard/src/container/DappHome.tsx b/packages/client/dashboard/src/container/DappHome.tsx index 85b9bab..ba6a076 100644 --- a/packages/client/dashboard/src/container/DappHome.tsx +++ b/packages/client/dashboard/src/container/DappHome.tsx @@ -6,7 +6,7 @@ import styled from 'styled-components' import Dashboard from '../components/dapp/home/Dashboard' import Header from '../components/dapp/home/Header' import CheckCircleIcon from '../components/icons/CheckCircleIcon' -import ChevronDown from '../components/icons/CheckronDown' +import ChevronDown from '../components/icons/ChevronDown' import { ChevronRightDoubleWhite } from '../components/icons/ChevronRightDouble' import DisabledIcon from '../components/icons/DisabledIcon' import LightbulbIcon from '../components/icons/LightbulbIcon' diff --git a/packages/client/dashboard/src/container/ExploreComposite.tsx b/packages/client/dashboard/src/container/ExploreComposite.tsx index 768100b..19632cb 100644 --- a/packages/client/dashboard/src/container/ExploreComposite.tsx +++ b/packages/client/dashboard/src/container/ExploreComposite.tsx @@ -1,153 +1,14 @@ -import { useSession } from '@us3r-network/auth-with-rainbowkit' -import dayjs from 'dayjs' -import { useCallback, useEffect, useRef, useState } from 'react' -import { - Button, - Dialog, - DialogTrigger, - Modal, - ModalOverlay -} from 'react-aria-components' -import InfiniteScroll from 'react-infinite-scroll-component' +import { useState } from 'react' import { useSearchParams } from 'react-router-dom' import styled from 'styled-components' -import { updateDapp } from '../api/dapp' -import { PAGE_SIZE } from '../constants' -import { ImgOrName } from '../components/common/ImgOrName' import Search from '../components/common/Search' -import { TableBox, TableContainer } from '../components/common/TableBox' -import CheckCircleIcon from '../components/icons/CheckCircleIcon' -import PlusCircleIcon from '../components/icons/PlusCircleIcon' -import NoCeramicNodeModal from '../components/node/NoCeramicNodeModal' -import { S3_SCAN_URL } from '../constants' -import { useAppCtx } from '../context/AppCtx' -import { useCeramicNodeCtx } from '../context/CeramicNodeCtx' -import useSelectedDapp from '../hooks/useSelectedDapp' -import { CeramicStatus, DappCompositeDto, Network } from '../types.d' -import { shortPubKey } from '../utils/shortPubKey' -import { bindingDappComposites, getComposites } from '../api/composite' -import { Dapps } from './ExploreModel' -import CreateCompositeModal from '../components/model/CreateCompositeModal' -import LayoutIcon from '../components/icons/LayoutIcon' -import { startIndexModels } from '../api/model' +import { CompositeList } from '../components/model/ExploreCompositeList' export default function ExploreComposite () { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [searchParams, setSearchParams] = useSearchParams() - const { s3ModelCollection, selectedDapp } = useSelectedDapp() - const { currCeramicNode } = useCeramicNodeCtx() - const session = useSession() - const [composites, setComposites] = useState>([]) - // const [starModels, setStarModels] = useState>([]) - const [hasMore, setHasMore] = useState(true) - const searchText = useRef('') - const pageNum = useRef(1) - // const [personalCollections, setPersonalCollections] = useState< - // PersonalCollection[] - // >([]) - // const fetchPersonalCollections = useCallback(async () => { - // if (!session) return - // s3ModelCollection.authComposeClient(session) - // try { - // const personal = await s3ModelCollection.queryPersonalCollections({ - // first: 500 - // }) - // if (personal.errors) throw new Error(personal.errors[0].message) - // const collected = personal.data?.viewer.modelCollectionList - - // if (collected) { - // setPersonalCollections( - // collected?.edges - // .filter(item => item.node && item.node.revoke === false) - // .map(item => { - // return { - // modelId: item.node.modelID, - // id: item.node.id!, - // revoke: !!item.node.revoke - // } - // }) - // ) - // } - // } catch (error) { - // console.error('error -----', error) - // } - // }, [s3ModelCollection, session]) - - // const fetchStarModels = useCallback(async () => { - // const ids = personalCollections - // .filter(item => item.revoke === false) - // .map(item => { - // return item.modelId - // }) - // if (ids.length === 0) { - // setStarModels([]) - // return - // } - - // try { - // const resp = await getModelsInfoByIds({ - // network: (selectedDapp?.network as Network) || Network.TESTNET, - // ids - // }) - // if (resp.data.code !== 0) { - // throw new Error(resp.data.msg) - // } - - // const list = resp.data.data - // setStarModels([...list]) - // } catch (error) { - // console.error(error) - // } - // }, [personalCollections, selectedDapp?.network]) - - const fetchComposites = useCallback(async () => { - setComposites([]) - setHasMore(true) - const resp = await getComposites({ - name: searchText.current, - network: (selectedDapp?.network as Network) || Network.TESTNET - }) - const list = resp.data.data - setComposites(list) - setHasMore(list.length >= PAGE_SIZE) - pageNum.current = 1 - }, [selectedDapp?.network]) - - const fetchMoreComposites = useCallback( - async (pageNumber: number) => { - const resp = await getComposites({ - name: searchText.current, - pageNumber, - network: (selectedDapp?.network as Network) || Network.TESTNET - }) - const list = resp.data.data - setHasMore(list.length >= PAGE_SIZE) - setComposites([...composites, ...list]) - }, - [composites, selectedDapp?.network] + const [searchParams] = useSearchParams() + const [searchText, setSearchText] = useState( + searchParams.get('searchText') || '' ) - - useEffect(() => { - fetchComposites() - // fetchPersonalCollections() - }, [fetchComposites]) - - // useEffect(() => { - // fetchStarModels().catch(err => { - // setStarModels([]) - // console.error(err) - // }) - // }, [fetchStarModels]) - - // const filterStar = useMemo(() => { - // return searchParams.get('filterStar') || '' - // }, [searchParams]) - - // const lists = useMemo(() => { - // if (!filterStar) return models - // return starModels - // }, [filterStar, models, starModels]) - const lists = composites return (
@@ -155,235 +16,19 @@ export default function ExploreComposite () {
{ - searchText.current = text - setComposites([]) - fetchComposites() + setSearchText(text) }} - placeholder={'Search by model name'} + placeholder={'Search by name'} />
- { - pageNum.current += 1 - fetchMoreComposites(pageNum.current) - console.log('fetch more') - }} - hasMore={hasMore} - loader={Loading...} - > - - - - - Composite Name - Stream ID - Release Date - Dapps - - - - - - {lists.map((item, idx) => { - return ( - - -
{item.name}
- - -
- {item.streamId ? ( - - {shortPubKey(item.streamId, { - len: 8, - split: '-' - })} - - ) : ( - '-' - )} -
- - -
- {dayjs(item.createdAt).format('YYYY-MM-DD HH:mm:ss') || - '-'} -
- - - - - - {/* */} - dapp.id === selectedDapp?.id - ) !== -1 - } - ceramicNodeId={ - currCeramicNode && - currCeramicNode.status === CeramicStatus.RUNNING - ? currCeramicNode?.id - : undefined - } - /> - - - ) - })} - -
-
-
- {!hasMore && no more data} +
) } -function Actions ({ - composite, - hasIndexed, - ceramicNodeId -}: { - composite: DappCompositeDto - hasIndexed: boolean - ceramicNodeId?: number -}) { - const session = useSession() - - const { loadDapps } = useAppCtx() - const { selectedDapp } = useSelectedDapp() - const [adding, setAdding] = useState(false) - - const bindComposite = useCallback(async () => { - if (!session || !selectedDapp) return - if (!ceramicNodeId) return - if (!hasIndexed) { - bindingDappComposites({ - compositeId: composite.id, - dapp: selectedDapp, - did: session.serialize() - }) - .then( - () => { - const models = JSON.parse(composite.composite).models - console.log(Object.keys(models)) - const modelIds = Object.keys(models) - startIndexModels({ - modelIds, - network: selectedDapp.network, - didSession: session.serialize() - }).catch(console.error) - }, - err => { - console.error(err) - } - ) - .catch(console.error) - } - try { - setAdding(true) - const composites = selectedDapp.composites || [] - composites.push(composite) - await updateDapp( - { ...selectedDapp, composites }, - session.serialize(), - ceramicNodeId - ) - await loadDapps() - } catch (err) { - console.error(err) - } finally { - setAdding(false) - } - }, [session, selectedDapp, ceramicNodeId, hasIndexed, composite, loadDapps]) - return ( - - - - - - - {({ close }) => ( - - )} - - - - - {adding ? ( - - ) : ( - <> - {hasIndexed ? ( - - ) : ceramicNodeId ? ( - - ) : ( - - - - - - {({ close }) => } - - - - - )} - - )} - - ) -} - -const OpsBox = styled.div` - display: flex; - align-items: center; - gap: -5px; - img { - width: 17px; - } -` - const ExploreModelContainer = styled.div` margin-top: 25px; margin-bottom: 25px; @@ -469,9 +114,3 @@ const ExploreModelContainer = styled.div` font-size: 18px; } ` - -const Loading = styled.div` - padding: 20px; - text-align: center; - color: gray; -` diff --git a/packages/client/dashboard/src/container/ExploreModel.tsx b/packages/client/dashboard/src/container/ExploreModel.tsx index 9f9fd10..4738932 100644 --- a/packages/client/dashboard/src/container/ExploreModel.tsx +++ b/packages/client/dashboard/src/container/ExploreModel.tsx @@ -1,153 +1,17 @@ -import { useSession } from '@us3r-network/auth-with-rainbowkit' -import dayjs from 'dayjs' -import { useCallback, useEffect, useMemo, useRef, useState } from 'react' -import { - Button, - Dialog, - DialogTrigger, - Modal, - ModalOverlay -} from 'react-aria-components' -import InfiniteScroll from 'react-infinite-scroll-component' +import { useMemo, useState } from 'react' import { useSearchParams } from 'react-router-dom' import styled from 'styled-components' -import { updateDapp } from '../api/dapp' -import { PAGE_SIZE } from '../constants' -import { - getModelStreamList, - getModelsInfoByIds, - startIndexModel -} from '../api/model' -import { ImgOrName } from '../components/common/ImgOrName' import Search from '../components/common/Search' -import { TableBox, TableContainer } from '../components/common/TableBox' -import CheckCircleIcon from '../components/icons/CheckCircleIcon' -import PlusCircleIcon from '../components/icons/PlusCircleIcon' -import StarGoldIcon from '../components/icons/StarGoldIcon' -import StarIcon from '../components/icons/StarIcon' -import NoCeramicNodeModal from '../components/node/NoCeramicNodeModal' -import { S3_SCAN_URL } from '../constants' -import { PersonalCollection, useAppCtx } from '../context/AppCtx' -import { useCeramicNodeCtx } from '../context/CeramicNodeCtx' -import useSelectedDapp from '../hooks/useSelectedDapp' -import { CeramicStatus, ClientDApp, ModelStream, Network } from '../types.d' -import { shortPubKey } from '../utils/shortPubKey' +import ModelList from '../components/model/ExploreModelList' export default function ExploreModel () { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [searchParams, setSearchParams] = useSearchParams() - const { s3ModelCollection, selectedDapp } = useSelectedDapp() - const { currCeramicNode } = useCeramicNodeCtx() - const session = useSession() - const [models, setModels] = useState>([]) - const [starModels, setStarModels] = useState>([]) - const [hasMore, setHasMore] = useState(true) - const searchText = useRef('') - const pageNum = useRef(1) - const [personalCollections, setPersonalCollections] = useState< - PersonalCollection[] - >([]) - const fetchPersonalCollections = useCallback(async () => { - if (!session) return - s3ModelCollection.authComposeClient(session) - try { - const personal = await s3ModelCollection.queryPersonalCollections({ - first: 500 - }) - if (personal.errors) throw new Error(personal.errors[0].message) - const collected = personal.data?.viewer.modelCollectionList - - if (collected) { - setPersonalCollections( - collected?.edges - .filter(item => item.node && item.node.revoke === false) - .map(item => { - return { - modelId: item.node.modelID, - id: item.node.id!, - revoke: !!item.node.revoke - } - }) - ) - } - } catch (error) { - console.error('error -----', error) - } - }, [s3ModelCollection, session]) - - const fetchStarModels = useCallback(async () => { - const ids = personalCollections - .filter(item => item.revoke === false) - .map(item => { - return item.modelId - }) - if (ids.length === 0) { - setStarModels([]) - return - } - - try { - const resp = await getModelsInfoByIds({ - network: (selectedDapp?.network as Network) || Network.TESTNET, - ids - }) - if (resp.data.code !== 0) { - throw new Error(resp.data.msg) - } - - const list = resp.data.data - setStarModels([...list]) - } catch (error) { - console.error(error) - } - }, [personalCollections, selectedDapp?.network]) - - const fetchModel = useCallback(async () => { - setModels([]) - setHasMore(true) - const resp = await getModelStreamList({ - name: searchText.current, - network: (selectedDapp?.network as Network) || Network.TESTNET - }) - const list = resp.data.data - setModels(list) - setHasMore(list.length >= PAGE_SIZE) - pageNum.current = 1 - }, [selectedDapp?.network]) - - const fetchMoreModel = useCallback( - async (pageNumber: number) => { - const resp = await getModelStreamList({ - pageNumber - }) - const list = resp.data.data - setHasMore(list.length >= PAGE_SIZE) - setModels([...models, ...list]) - }, - [models] - ) - - useEffect(() => { - fetchModel() - fetchPersonalCollections() - }, [fetchModel, fetchPersonalCollections]) - - useEffect(() => { - fetchStarModels().catch(err => { - setStarModels([]) - console.error(err) - }) - }, [fetchStarModels]) - + const [searchParams] = useSearchParams() const filterStar = useMemo(() => { - return searchParams.get('filterStar') || '' + return searchParams.get('filterStar') === 'true' }, [searchParams]) - - const lists = useMemo(() => { - if (!filterStar) return models - return starModels - }, [filterStar, models, starModels]) - + const [searchText, setSearchText] = useState( + searchParams.get('searchText') || '' + ) return (
@@ -155,385 +19,19 @@ export default function ExploreModel () {
{ - searchText.current = text - setModels([]) - fetchModel() + setSearchText(text) }} placeholder={'Search by model name'} />
- { - pageNum.current += 1 - fetchMoreModel(pageNum.current) - console.log('fetch more') - }} - hasMore={filterStar ? false : hasMore} - loader={Loading...} - > - - - - - Model Name - Description - ID - Usage Count - 7 Days Usage - Release Date - Dapps - - - - - - {lists.map((item, idx) => { - const hasStarItem = personalCollections.find( - starItem => starItem.modelId === item.stream_id - ) - return ( - - - - - -
{item.stream_content.description}
- - - - - - - - {item.recentlyUseCount || '-'} - -
- {(item.last_anchored_at && - dayjs(item.created_at).format( - 'YYYY-MM-DD HH:mm:ss' - )) || - '-'} -
- - - - - - {/* */} - - - - ) - })} - -
-
-
- {!filterStar && !hasMore && no more data} +
) } -export function Dapps ({ dapps }: { dapps: ClientDApp[] }) { - const apps = useMemo(() => { - const data = [...dapps] - if (data.length > 3) - return { data: data.slice(0, 3), left: data.length - 3 } - return { data, left: 0 } - }, [dapps]) - - return ( - - {apps.data.length > 0 - ? apps.data.map((item, idx) => { - return ( - - - - ) - }) - : 'None'} - {apps.left > 0 && {apps.left}+} - - ) -} - -const DappBox = styled.div` - display: flex; - gap: 5px; - overflow: hidden; - color: #fff; - font-family: Rubik; - font-size: 12px; - font-style: normal; - font-weight: 400; - line-height: normal; - a { - > span { - color: #fff; - width: 36px; - height: 36px; - border-radius: 10px; - border: 1px solid #718096; - display: inline-flex; - align-items: center; - justify-content: center; - overflow: hidden; - &.name { - font-size: 20px; - font-weight: 500; - } - &.left { - border: none; - color: #fff; - justify-content: start; - font-family: Rubik; - font-size: 12px; - font-style: normal; - font-weight: 400; - line-height: normal; - } - > img { - width: 100%; - height: 100%; - object-fit: cover; - flex-shrink: 0; - } - } - } -` - -function ModelStarItem ({ - hasStarItem, - fetchPersonal, - stream_id, - hasIndexed, - ceramicNodeId -}: { - hasIndexed: boolean - stream_id: string - hasStarItem: - | { - modelId: string - id: string - revoke: boolean - } - | undefined - fetchPersonal: () => void - ceramicNodeId?: number -}) { - const session = useSession() - const { s3ModelCollection } = useSelectedDapp() - const [staring, setStaring] = useState(false) - - const { loadDapps } = useAppCtx() - const { selectedDapp } = useSelectedDapp() - const [adding, setAdding] = useState(false) - const addToModelList = useCallback( - async (modelId: string) => { - if (!session || !selectedDapp) return - if (!ceramicNodeId) return - if (!hasIndexed) { - startIndexModel({ - modelId, - network: selectedDapp.network as Network, - didSession: session.serialize() - }).catch(console.error) - } - try { - setAdding(true) - const models = selectedDapp.models || [] - models.push(modelId) - await updateDapp( - { ...selectedDapp, models }, - session.serialize(), - ceramicNodeId - ) - await loadDapps() - } catch (err) { - console.error(err) - } finally { - setAdding(false) - } - }, - [loadDapps, selectedDapp, session, setAdding, hasIndexed, ceramicNodeId] - ) - - const starModelAction = useCallback( - async (modelId: string, id?: string, revoke?: boolean) => { - if (staring) return - try { - if (!session) return - s3ModelCollection.authComposeClient(session) - setStaring(true) - if (id) { - const resp = await s3ModelCollection.updateCollection(id, { - revoke: !revoke - }) - if (resp.errors) { - throw new Error(resp.errors[0].message) - } - } else { - const resp = await s3ModelCollection.createCollection({ - modelID: modelId, - revoke: false - }) - if (resp.errors) { - throw new Error(resp.errors[0].message) - } - } - await fetchPersonal() - } catch (error) { - console.error(error) - } finally { - setStaring(false) - } - }, - [session, s3ModelCollection, fetchPersonal, staring] - ) - - const modelId = stream_id - - return ( - - {(staring && ( - - )) || ( - - )} - - {adding ? ( - - ) : ( - <> - {selectedDapp?.models?.includes(modelId) ? ( - - ) : ceramicNodeId ? ( - - ) : ( - - - - - - {({ close }) => } - - - - - )} - - )} - - ) -} - -const OpsBox = styled.div` - display: flex; - align-items: center; - gap: 5px; - - img { - width: 17px; - } -` - const ExploreModelContainer = styled.div` margin-top: 25px; margin-bottom: 25px; @@ -619,9 +117,3 @@ const ExploreModelContainer = styled.div` font-size: 18px; } ` - -const Loading = styled.div` - padding: 20px; - text-align: center; - color: gray; -`