diff --git a/src/components/search/Spark/Meta/Meta.module.scss b/src/components/search/Spark/Meta/Meta.module.scss index e5d423fce..18bbfb7f3 100644 --- a/src/components/search/Spark/Meta/Meta.module.scss +++ b/src/components/search/Spark/Meta/Meta.module.scss @@ -5,7 +5,6 @@ // temp z-index: 1; - font-size: 14px; & > :first-child { @@ -13,7 +12,3 @@ top: 4px; } } - -.size { - white-space: nowrap; -} diff --git a/src/components/search/Spark/Meta/Meta.tsx b/src/components/search/Spark/Meta/Meta.tsx index e7802d1e7..5f3221305 100644 --- a/src/components/search/Spark/Meta/Meta.tsx +++ b/src/components/search/Spark/Meta/Meta.tsx @@ -1,10 +1,7 @@ -import useQueueIpfsContent from 'src/hooks/useQueueIpfsContent'; -import { formatCurrency } from 'src/utils/utils'; -import { PREFIXES } from 'src/containers/ipfs/components/metaInfo'; import { useNavigate } from 'react-router-dom'; import { routes } from 'src/routes'; -import { useEffect } from 'react'; import useCyberlinksCount from 'src/features/cyberlinks/hooks/useCyberlinksCount'; +import ParticleSize from 'src/features/particle/ParticleSize/ParticleSize'; import Links from './Links/Links'; import styles from './Meta.module.scss'; @@ -13,18 +10,10 @@ type Props = { }; function Meta({ cid }: Props) { - const { content, fetchParticle } = useQueueIpfsContent(cid); - const { data: count } = useCyberlinksCount(cid); - useEffect(() => { - fetchParticle && (async () => fetchParticle(cid))(); - }, [cid, fetchParticle]); - const navigate = useNavigate(); - const size = content?.meta?.size; - return (
- {size && ( - - 🟥 {formatCurrency(size, 'B', 0, PREFIXES)} - - )} +
); } diff --git a/src/containers/ipfs/components/AdviserMeta/AdviserMeta.tsx b/src/containers/ipfs/components/AdviserMeta/AdviserMeta.tsx index 2e20793d0..a70878bb9 100644 --- a/src/containers/ipfs/components/AdviserMeta/AdviserMeta.tsx +++ b/src/containers/ipfs/components/AdviserMeta/AdviserMeta.tsx @@ -1,23 +1,22 @@ import { Account } from 'src/components'; -import { timeSince, formatCurrency } from 'src/utils/utils'; +import { timeSince } from 'src/utils/utils'; import useRank from 'src/features/cyberlinks/rank/useRank'; import { Link } from 'react-router-dom'; import { routes } from 'src/routes'; +import ParticleSize from 'src/features/particle/ParticleSize/ParticleSize'; import { LLMAvatar, useIsLLMPageParam, } from 'src/containers/Search/LLMSpark/LLMSpark'; import useGetCreator from '../../hooks/useGetCreator'; -import { PREFIXES } from '../metaInfo'; import styles from './AdviserMeta.module.scss'; type Props = { cid: string; type: string | undefined; - size: number | bigint | undefined; }; -function AdviserMeta({ cid, type, size }: Props) { +function AdviserMeta({ cid, type }: Props) { const { creator } = useGetCreator(cid); const rank = useRank(cid); @@ -62,9 +61,7 @@ function AdviserMeta({ cid, type, size }: Props) { )}
- - 🟥 {size ? formatCurrency(size, 'B', 0, PREFIXES) : 'unknown'} - + 🌓
diff --git a/src/containers/ipfs/ipfs.tsx b/src/containers/ipfs/ipfs.tsx index bbc8c914e..d6ce459ff 100644 --- a/src/containers/ipfs/ipfs.tsx +++ b/src/containers/ipfs/ipfs.tsx @@ -26,6 +26,7 @@ function Ipfs() { const { setAdviser } = useAdviser(); const isText = useMemo(() => !query.match(PATTERN_IPFS_HASH), [query]); + useEffect(() => { if (!isReady) { return; @@ -41,7 +42,7 @@ function Ipfs() { })(); } }, [isText, isReady, query, ipfsApi, isIpfsInitialized]); - useEffect(() => {}, [details]); + useEffect(() => { if (!status) { return; @@ -61,14 +62,7 @@ function Ipfs() { 'yellow' ); } else if (status === 'completed') { - setAdviser( - , - 'purple' - ); + setAdviser(, 'purple'); } }, [details, setAdviser, cid, content, status]); diff --git a/src/features/cyberlinks/hooks/useCyberlinksCount.ts b/src/features/cyberlinks/hooks/useCyberlinksCount.ts index 7c668f115..26a5e7011 100644 --- a/src/features/cyberlinks/hooks/useCyberlinksCount.ts +++ b/src/features/cyberlinks/hooks/useCyberlinksCount.ts @@ -4,7 +4,7 @@ const getVar = (type: 'from' | 'to', cid: string, neuron) => { return { [`particle_${type}`]: { _eq: cid }, neuron: { _eq: neuron } }; }; -function useCyberlinksCount(cid: string, neuron) { +function useCyberlinksCount(cid: string, neuron?: string) { const toCountQuery = useCyberlinksCountByParticleQuery({ variables: { where: getVar('to', cid, neuron) }, }); diff --git a/src/features/particle/ParticleSize/ParticleSize.module.scss b/src/features/particle/ParticleSize/ParticleSize.module.scss new file mode 100644 index 000000000..4e0cd13f0 --- /dev/null +++ b/src/features/particle/ParticleSize/ParticleSize.module.scss @@ -0,0 +1,3 @@ +.size { + white-space: nowrap; +} diff --git a/src/features/particle/ParticleSize/ParticleSize.tsx b/src/features/particle/ParticleSize/ParticleSize.tsx new file mode 100644 index 000000000..96d5104f2 --- /dev/null +++ b/src/features/particle/ParticleSize/ParticleSize.tsx @@ -0,0 +1,36 @@ +import { formatCurrency } from 'src/utils/utils'; +import { PREFIXES } from 'src/containers/ipfs/components/metaInfo'; +import useParticle from '../../../hooks/useParticle'; +import styles from './ParticleSize.module.scss'; + +// for future use if no UI needed +// eslint-disable-next-line import/no-unused-modules +export function useParticleSize(cid: string) { + const { content, details, status } = useParticle(cid); + + const size = content?.meta?.size || details?.content?.length; + + return { + size, + isLoading: status === 'pending' || status === 'executing', + }; +} + +function ParticleSize({ cid }: { cid: string }) { + const { isLoading, size } = useParticleSize(cid); + + let content; + + if (isLoading) { + // use Loading component + content = 'loading...'; + } else if (size) { + content = formatCurrency(size, 'B', 0, PREFIXES); + } else { + content = 'unknown'; + } + + return 🟥 {content}; +} + +export default ParticleSize; diff --git a/src/generated/graphql.ts b/src/generated/graphql.ts index 4ab8dcdd9..ed4916e7c 100644 --- a/src/generated/graphql.ts +++ b/src/generated/graphql.ts @@ -8843,6 +8843,13 @@ export type CyberlinksCountByNeuronQueryVariables = Exact<{ export type CyberlinksCountByNeuronQuery = { cyberlinks_aggregate: { aggregate?: { count: number } | null } }; +export type CyberlinksCountByNeuron2QueryVariables = Exact<{ + address?: InputMaybe; +}>; + + +export type CyberlinksCountByNeuron2Query = { cyberlinks_aggregate: { aggregate?: { count: number } | null } }; + export type CyberlinksCountByParticleQueryVariables = Exact<{ cid?: InputMaybe; where?: InputMaybe; @@ -8883,6 +8890,22 @@ export type MessagesByAddressSenseWsSubscriptionVariables = Exact<{ export type MessagesByAddressSenseWsSubscription = { messages_by_address: Array<{ transaction_hash: string, index: any, value: any, type: string, transaction?: { success: boolean, memo?: string | null, block: { timestamp: any, height: any } } | null }> }; +export type ParticlesQueryVariables = Exact<{ + neuron?: InputMaybe; + limit?: InputMaybe; + offset?: InputMaybe; +}>; + + +export type ParticlesQuery = { particles: Array<{ id: number, particle: string, timestamp: any, transaction_hash: string }> }; + +export type ParticlesAggregateQueryVariables = Exact<{ + neuron?: InputMaybe; +}>; + + +export type ParticlesAggregateQuery = { particles_aggregate: { aggregate?: { count: number } | null } }; + export type TransactionCountQueryVariables = Exact<{ [key: string]: never; }>; @@ -9213,6 +9236,48 @@ export type CyberlinksCountByNeuronQueryHookResult = ReturnType; export type CyberlinksCountByNeuronSuspenseQueryHookResult = ReturnType; export type CyberlinksCountByNeuronQueryResult = Apollo.QueryResult; +export const CyberlinksCountByNeuron2Document = gql` + query CyberlinksCountByNeuron2($address: String) { + cyberlinks_aggregate(where: {neuron: {_eq: $address}}) { + aggregate { + count + } + } +} + `; + +/** + * __useCyberlinksCountByNeuron2Query__ + * + * To run a query within a React component, call `useCyberlinksCountByNeuron2Query` and pass it any options that fit your needs. + * When your component renders, `useCyberlinksCountByNeuron2Query` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useCyberlinksCountByNeuron2Query({ + * variables: { + * address: // value for 'address' + * }, + * }); + */ +export function useCyberlinksCountByNeuron2Query(baseOptions?: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(CyberlinksCountByNeuron2Document, options); + } +export function useCyberlinksCountByNeuron2LazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(CyberlinksCountByNeuron2Document, options); + } +export function useCyberlinksCountByNeuron2SuspenseQuery(baseOptions?: Apollo.SuspenseQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useSuspenseQuery(CyberlinksCountByNeuron2Document, options); + } +export type CyberlinksCountByNeuron2QueryHookResult = ReturnType; +export type CyberlinksCountByNeuron2LazyQueryHookResult = ReturnType; +export type CyberlinksCountByNeuron2SuspenseQueryHookResult = ReturnType; +export type CyberlinksCountByNeuron2QueryResult = Apollo.QueryResult; export const CyberlinksCountByParticleDocument = gql` query cyberlinksCountByParticle($cid: String, $where: cyberlinks_bool_exp) { cyberlinks_aggregate(where: $where) { @@ -9412,6 +9477,93 @@ export function useMessagesByAddressSenseWsSubscription(baseOptions?: Apollo.Sub } export type MessagesByAddressSenseWsSubscriptionHookResult = ReturnType; export type MessagesByAddressSenseWsSubscriptionResult = Apollo.SubscriptionResult; +export const ParticlesDocument = gql` + query particles($neuron: String, $limit: Int = 10, $offset: Int = 0) { + particles(where: {neuron: {_eq: $neuron}}, limit: $limit, offset: $offset) { + id + particle + timestamp + transaction_hash + } +} + `; + +/** + * __useParticlesQuery__ + * + * To run a query within a React component, call `useParticlesQuery` and pass it any options that fit your needs. + * When your component renders, `useParticlesQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useParticlesQuery({ + * variables: { + * neuron: // value for 'neuron' + * limit: // value for 'limit' + * offset: // value for 'offset' + * }, + * }); + */ +export function useParticlesQuery(baseOptions?: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(ParticlesDocument, options); + } +export function useParticlesLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(ParticlesDocument, options); + } +export function useParticlesSuspenseQuery(baseOptions?: Apollo.SuspenseQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useSuspenseQuery(ParticlesDocument, options); + } +export type ParticlesQueryHookResult = ReturnType; +export type ParticlesLazyQueryHookResult = ReturnType; +export type ParticlesSuspenseQueryHookResult = ReturnType; +export type ParticlesQueryResult = Apollo.QueryResult; +export const ParticlesAggregateDocument = gql` + query particlesAggregate($neuron: String) { + particles_aggregate(where: {neuron: {_eq: $neuron}}) { + aggregate { + count + } + } +} + `; + +/** + * __useParticlesAggregateQuery__ + * + * To run a query within a React component, call `useParticlesAggregateQuery` and pass it any options that fit your needs. + * When your component renders, `useParticlesAggregateQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useParticlesAggregateQuery({ + * variables: { + * neuron: // value for 'neuron' + * }, + * }); + */ +export function useParticlesAggregateQuery(baseOptions?: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(ParticlesAggregateDocument, options); + } +export function useParticlesAggregateLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(ParticlesAggregateDocument, options); + } +export function useParticlesAggregateSuspenseQuery(baseOptions?: Apollo.SuspenseQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useSuspenseQuery(ParticlesAggregateDocument, options); + } +export type ParticlesAggregateQueryHookResult = ReturnType; +export type ParticlesAggregateLazyQueryHookResult = ReturnType; +export type ParticlesAggregateSuspenseQueryHookResult = ReturnType; +export type ParticlesAggregateQueryResult = Apollo.QueryResult; export const TransactionCountDocument = gql` query transactionCount { transaction_aggregate { diff --git a/src/pages/robot/Brain/Brain.tsx b/src/pages/robot/Brain/Brain.tsx index 8d8df016c..d96286390 100644 --- a/src/pages/robot/Brain/Brain.tsx +++ b/src/pages/robot/Brain/Brain.tsx @@ -8,6 +8,7 @@ import TreedView from './ui/TreedView'; import styles from './Brain.module.scss'; import GraphView from './ui/GraphView'; import useGraphLimit from './useGraphLimit'; +import Particles from './ui/Particles/Particles'; enum TabsKey { graph3d = 'graph3d', @@ -56,6 +57,11 @@ function Brain() { to: './list', text: 'last cyberlinks', }, + { + key: TabsKey.list, + to: './particles', + text: 'particles', + }, ]} selected={selected} /> @@ -71,6 +77,7 @@ function Brain() { ))} } /> + } /> } /> diff --git a/src/pages/robot/Brain/ui/Particles/Particles.module.scss b/src/pages/robot/Brain/ui/Particles/Particles.module.scss new file mode 100644 index 000000000..275cba72a --- /dev/null +++ b/src/pages/robot/Brain/ui/Particles/Particles.module.scss @@ -0,0 +1,5 @@ +.particleContent { + max-height: 60px; + display: block; + overflow: auto; +} diff --git a/src/pages/robot/Brain/ui/Particles/Particles.tsx b/src/pages/robot/Brain/ui/Particles/Particles.tsx new file mode 100644 index 000000000..a6aa106bb --- /dev/null +++ b/src/pages/robot/Brain/ui/Particles/Particles.tsx @@ -0,0 +1,197 @@ +import useAdviserTexts from 'src/features/adviser/useAdviserTexts'; +import { + ParticlesQuery, + useCyberlinksCountByNeuron2Query, + useParticlesAggregateQuery, + useParticlesQuery, +} from 'src/generated/graphql'; +import { useRobotContext } from 'src/pages/robot/robot.context'; + +import { createColumnHelper } from '@tanstack/react-table'; +import Table from 'src/components/Table/Table'; +import ContentItem from 'src/components/ContentItem/contentItem'; +import { Display } from 'src/components'; +import { useState } from 'react'; +import InfiniteScroll from 'react-infinite-scroll-component'; +import Loader2 from 'src/components/ui/Loader2'; +import ParticleSize from 'src/features/particle/ParticleSize/ParticleSize'; +import { Link, useNavigate } from 'react-router-dom'; +import { routes } from 'src/routes'; +import useRank from 'src/features/cyberlinks/rank/useRank'; +import styles from './Particles.module.scss'; + +const columnHelper = createColumnHelper(); + +const LIMIT = 20; + +function Rank2({ cid }) { + const rank = useRank(cid); + + if (!rank) { + return null; + } + + return ( + + {rank / 10 ** 15} + {/* {rank.toLocaleString().replaceAll(',', ' ')} */} + + ); +} + +function Particles() { + const { address } = useRobotContext(); + const [hasMore, setHasMore] = useState(true); + // const [offset, setOffset] = useState(0); + + const navigate = useNavigate(); + + const { data, loading, error, fetchMore } = useParticlesQuery({ + variables: { + neuron: address, + limit: LIMIT, + }, + }); + + console.log(data); + + const particleAggregateQuery = useParticlesAggregateQuery({ + variables: { + neuron: address, + }, + }); + + const cyberlinksCountQuery = useCyberlinksCountByNeuron2Query({ + variables: { + address, + }, + }); + + function fetch() { + fetchMore({ + variables: { + offset: data?.particles.length, + }, + updateQuery: (prev, { fetchMoreResult }) => { + if (!fetchMoreResult) { + return prev; + } + + setHasMore(fetchMoreResult.particles.length > 0); + + return { + ...prev, + particles: [...prev.particles, ...fetchMoreResult.particles], + }; + }, + }); + } + + useAdviserTexts({ + defaultText: 'Particles', + isLoading: loading, + error, + }); + + console.log(data, cyberlinksCountQuery); + + const total = + particleAggregateQuery.data?.particles_aggregate.aggregate?.count; + + return ( +
+ +

{total} particles

+ +

+ {cyberlinksCountQuery.data?.cyberlinks_aggregate.aggregate?.count}{' '} + cyberlinks +

+
+
+ + } + > + { + const cid = data!.particles[row].particle; + + navigate(routes.oracle.ask.getLink(cid)); + }} + columns={[ + columnHelper.accessor('particle', { + header: 'CID', + id: 'cid', + cell: (info) => { + return ( + + ); + }, + }), + columnHelper.accessor('timestamp', { + header: 'timestamp', + cell: (info) => { + const hash = info.row.original.transaction_hash; + // return new Date(info.getValue()).toLocaleString(); + return ( + + {new Intl.DateTimeFormat(navigator.language, { + dateStyle: 'medium', + timeStyle: 'short', + }).format(new Date(info.getValue()))} + + ); + }, + }), + columnHelper.accessor('particle', { + header: 'size', + id: 'size', + cell: (info) => { + return ; + }, + }), + columnHelper.accessor('particle', { + header: ( + <> + {/* rank{' '} */} + {/* + 🦠 + */} + probability of observation + + ), + id: 'rank', + cell: (info) => { + return ; + }, + }), + ]} + data={data?.particles || []} + /> + + + + ); +} + +export default Particles; diff --git a/src/services/backend/services/indexer/graphql/cyberlinksCountByNeuron.graphql b/src/services/backend/services/indexer/graphql/cyberlinksCountByNeuron.graphql index 880c18a98..abaa4986b 100644 --- a/src/services/backend/services/indexer/graphql/cyberlinksCountByNeuron.graphql +++ b/src/services/backend/services/indexer/graphql/cyberlinksCountByNeuron.graphql @@ -17,3 +17,12 @@ query CyberlinksCountByNeuron( } } } + +# refactor +query CyberlinksCountByNeuron2($address: String) { + cyberlinks_aggregate(where: { neuron: { _eq: $address } }) { + aggregate { + count + } + } +} diff --git a/src/services/backend/services/indexer/graphql/particles/particles.graphql b/src/services/backend/services/indexer/graphql/particles/particles.graphql new file mode 100644 index 000000000..9b30bb771 --- /dev/null +++ b/src/services/backend/services/indexer/graphql/particles/particles.graphql @@ -0,0 +1,23 @@ +# input OrderByInput { +# timestamp: order_by +# id: order_by +# } + +query particles( + $neuron: String + $limit: Int = 10 + $offset: Int = 0 +) # $orderBy: OrderByInput +{ + particles( + where: { neuron: { _eq: $neuron } } + # order_by: $orderBy + limit: $limit + offset: $offset + ) { + id + particle + timestamp + transaction_hash + } +} diff --git a/src/services/backend/services/indexer/graphql/particles/particlesAggregate.graphql b/src/services/backend/services/indexer/graphql/particles/particlesAggregate.graphql new file mode 100644 index 000000000..ad735485d --- /dev/null +++ b/src/services/backend/services/indexer/graphql/particles/particlesAggregate.graphql @@ -0,0 +1,7 @@ +query particlesAggregate($neuron: String) { + particles_aggregate(where: { neuron: { _eq: $neuron } }) { + aggregate { + count + } + } +} diff --git a/src/utils/date.ts b/src/utils/date.ts index 8cab5cdb3..9e4f8da02 100644 --- a/src/utils/date.ts +++ b/src/utils/date.ts @@ -14,10 +14,6 @@ function roundMilliseconds(dateTimeString: string) { date.setMilliseconds(roundedMilliseconds); return dateFormat(date, 'yyyy-mm-dd"T"HH:MM:ss.l'); } -function getCurrentTimezoneOffset() { - const now = new Date(); - return -now.getTimezoneOffset() / 60; -} function pluralizeUnit(quantity: number, unit: string): string { return quantity === 1 ? unit : `${unit}s`;