@xyo-network/react-payload 2.26.35 → 2.26.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/docs.json +1 -1
  2. package/package.json +9 -9
  3. package/src/components/Details/DataDetails.tsx +65 -0
  4. package/src/components/Details/Details.stories.tsx +62 -0
  5. package/src/components/Details/Details.tsx +27 -0
  6. package/src/components/Details/HashSourceDetails.tsx +41 -0
  7. package/src/components/Details/JsonDetails.tsx +44 -0
  8. package/src/components/Details/MetaDetails.tsx +38 -0
  9. package/src/components/Details/ValidationDetails/ValidationDetails.stories.tsx +55 -0
  10. package/src/components/Details/ValidationDetails/ValidationDetails.tsx +42 -0
  11. package/src/components/Details/ValidationDetails/ValidationDetailsProps.ts +8 -0
  12. package/src/components/Details/ValidationDetails/index.ts +2 -0
  13. package/src/components/Details/index.ts +5 -0
  14. package/src/components/Table/PayloadTableColumnConfig.ts +28 -0
  15. package/src/components/Table/Table.stories.tsx +43 -0
  16. package/src/components/Table/Table.tsx +60 -0
  17. package/src/components/Table/TableRow.tsx +104 -0
  18. package/src/components/Table/index.ts +3 -0
  19. package/src/components/index.ts +2 -0
  20. package/src/contexts/Payload/Context.ts +5 -0
  21. package/src/contexts/Payload/Provider.tsx +31 -0
  22. package/src/contexts/Payload/State.ts +8 -0
  23. package/src/contexts/Payload/index.ts +4 -0
  24. package/src/contexts/Payload/use.ts +7 -0
  25. package/src/contexts/index.ts +1 -0
  26. package/src/hooks/ResolvePayloadArgs.ts +6 -0
  27. package/src/hooks/index.ts +6 -0
  28. package/src/hooks/lib/FetchHuriHashOptions.ts +3 -0
  29. package/src/hooks/lib/findHuriNetwork.ts +13 -0
  30. package/src/hooks/lib/index.ts +2 -0
  31. package/src/hooks/useGetSchema.stories.tsx +70 -0
  32. package/src/hooks/useGetSchema.tsx +51 -0
  33. package/src/hooks/useHuriHash.stories.tsx +108 -0
  34. package/src/hooks/useHuriHash.tsx +38 -0
  35. package/src/hooks/useLoadPayload.stories.tsx +69 -0
  36. package/src/hooks/useLoadPayload.tsx +60 -0
  37. package/src/hooks/useResolveHuri.tsx +74 -0
  38. package/src/index.ts +3 -0
  39. package/src/types/images.d.ts +5 -0
@@ -0,0 +1,104 @@
1
+ import { TableCell, TableCellProps, TableRow, TableRowProps, Typography } from '@mui/material'
2
+ import { useBreakpoint } from '@xylabs/react-shared'
3
+ import { XyoPayloadWithMeta, XyoPayloadWithPartialMeta, XyoPayloadWrapper, XyoPayloadWrapperValidator } from '@xyo-network/payload'
4
+ import { useNetwork } from '@xyo-network/react-network'
5
+ import { HashTableCell } from '@xyo-network/react-shared'
6
+ import { DateTime } from 'luxon'
7
+ import { MdClear, MdDone } from 'react-icons/md'
8
+
9
+ import { PayloadTableColumnConfig, payloadTableColumnConfigDefaults, PayloadTableColumnSlug } from './PayloadTableColumnConfig'
10
+
11
+ export interface PayloadTableRowProps extends TableRowProps {
12
+ payload?: XyoPayloadWithPartialMeta
13
+ exploreDomain?: string
14
+ columns?: PayloadTableColumnConfig
15
+ network?: string
16
+ }
17
+
18
+ export const PayloadTableRow: React.FC<PayloadTableRowProps> = ({ exploreDomain, network: networkProp, payload, columns = payloadTableColumnConfigDefaults(), ...props }) => {
19
+ const breakPoint = useBreakpoint()
20
+ const timeStamp = payload?._timestamp ? DateTime.fromMillis(payload?._timestamp) : undefined
21
+ const wrapper = payload ? new XyoPayloadWrapper(payload) : undefined
22
+ const { network } = useNetwork()
23
+
24
+ const archive: React.FC<TableCellProps> = (props) => (
25
+ <TableCell key="archive" align="center" {...props}>
26
+ <Typography fontFamily="monospace" variant="body2" noWrap>
27
+ {payload?._archive}
28
+ </Typography>
29
+ </TableCell>
30
+ )
31
+
32
+ const client: React.FC<TableCellProps> = (props) => (
33
+ <TableCell key="client" align="center" {...props}>
34
+ <Typography fontFamily="monospace" variant="body2" noWrap>
35
+ {payload?._client}
36
+ </Typography>
37
+ </TableCell>
38
+ )
39
+
40
+ const date: React.FC<TableCellProps> = (props) => (
41
+ <TableCell key="date" align="center" {...props}>
42
+ <Typography fontFamily="monospace" variant="body2" noWrap>
43
+ {timeStamp?.toLocaleString(DateTime.DATE_SHORT)}
44
+ </Typography>
45
+ </TableCell>
46
+ )
47
+
48
+ const hash: React.FC<TableCellProps> = (props) => (
49
+ <HashTableCell
50
+ key="hash"
51
+ width="100%"
52
+ value={payload?._hash}
53
+ archive={payload?._archive}
54
+ dataType="payload"
55
+ exploreDomain={exploreDomain}
56
+ network={networkProp ?? network?.slug}
57
+ {...props}
58
+ />
59
+ )
60
+
61
+ const schema: React.FC<TableCellProps> = (props) => (
62
+ <TableCell key="payloads" align="center" {...props}>
63
+ <Typography fontFamily="monospace" variant="body2" noWrap>
64
+ {payload?.schema}
65
+ </Typography>
66
+ </TableCell>
67
+ )
68
+
69
+ const time: React.FC<TableCellProps> = (props) => (
70
+ <TableCell key="time" align="center" {...props}>
71
+ <Typography fontFamily="monospace" variant="body2" noWrap>
72
+ {timeStamp?.toLocaleString(DateTime.TIME_SIMPLE)}
73
+ </Typography>
74
+ </TableCell>
75
+ )
76
+
77
+ const isValid = wrapper ? new XyoPayloadWrapperValidator(wrapper as XyoPayloadWrapper<XyoPayloadWithMeta>).validate().length === 0 : undefined
78
+
79
+ const valid: React.FC<TableCellProps> = (props) => (
80
+ <TableCell key="valid" align="center" {...props}>
81
+ <Typography fontFamily="monospace" variant="body2" noWrap>
82
+ {isValid === undefined ? <MdDone fontSize={18} color="yellow" /> : isValid ? <MdDone fontSize={18} color="green" /> : <MdClear color="red" fontSize={18} />}
83
+ </Typography>
84
+ </TableCell>
85
+ )
86
+
87
+ const tableCells: Record<PayloadTableColumnSlug, React.FC<TableCellProps>> = {
88
+ archive,
89
+ client,
90
+ date,
91
+ hash,
92
+ schema,
93
+ time,
94
+ valid,
95
+ }
96
+
97
+ return breakPoint ? (
98
+ <TableRow style={{ maxWidth: '100vw' }} {...props}>
99
+ {columns[breakPoint]?.map((column) => {
100
+ return tableCells[column]({})
101
+ })}
102
+ </TableRow>
103
+ ) : null
104
+ }
@@ -0,0 +1,3 @@
1
+ export * from './PayloadTableColumnConfig'
2
+ export * from './Table'
3
+ export * from './TableRow'
@@ -0,0 +1,2 @@
1
+ export * from './Details'
2
+ export * from './Table'
@@ -0,0 +1,5 @@
1
+ import { createContextEx } from '@xyo-network/react-shared'
2
+
3
+ import { PayloadContextState } from './State'
4
+
5
+ export const PayloadContext = createContextEx<PayloadContextState>()
@@ -0,0 +1,31 @@
1
+ import { useAsyncEffect, WithChildren } from '@xylabs/react-shared'
2
+ import { XyoPayload } from '@xyo-network/payload'
3
+ import { useArchivist } from '@xyo-network/react-archivist'
4
+ import { useState } from 'react'
5
+
6
+ import { PayloadContext } from './Context'
7
+
8
+ export interface PayloadProviderProps {
9
+ required?: boolean
10
+ hash?: string
11
+ }
12
+
13
+ export const PayloadProvider: React.FC<WithChildren<PayloadProviderProps>> = ({ required = false, hash, children }) => {
14
+ const { archivist } = useArchivist()
15
+ const [payload, setPayload] = useState<XyoPayload | null>()
16
+
17
+ useAsyncEffect(
18
+ // eslint-disable-next-line react-hooks/exhaustive-deps
19
+ async (mounted) => {
20
+ if (payload === undefined && hash) {
21
+ const loadedPayload = (await archivist?.get([hash]))?.pop()
22
+ if (mounted()) {
23
+ setPayload(loadedPayload)
24
+ }
25
+ }
26
+ },
27
+ [archivist, payload, hash]
28
+ )
29
+
30
+ return <PayloadContext.Provider value={{ payload, provided: true, setPayload }}> {payload ? children : required ? null : children}</PayloadContext.Provider>
31
+ }
@@ -0,0 +1,8 @@
1
+ import { XyoPayload } from '@xyo-network/payload'
2
+ import { ContextExState } from '@xyo-network/react-shared'
3
+ import { Dispatch } from 'react'
4
+
5
+ export interface PayloadContextState extends ContextExState {
6
+ payload?: XyoPayload | null
7
+ setPayload?: Dispatch<XyoPayload>
8
+ }
@@ -0,0 +1,4 @@
1
+ export * from './Context'
2
+ export * from './Provider'
3
+ export * from './State'
4
+ export * from './use'
@@ -0,0 +1,7 @@
1
+ import { useContextEx } from '@xyo-network/react-shared'
2
+
3
+ import { PayloadContext } from './Context'
4
+
5
+ export const usePayload = (required = false) => {
6
+ return useContextEx(PayloadContext, 'Payload', required)
7
+ }
@@ -0,0 +1 @@
1
+ export * from './Payload'
@@ -0,0 +1,6 @@
1
+ import { XyoApiError } from '@xyo-network/api'
2
+ import { XyoPayload } from '@xyo-network/payload'
3
+
4
+ export type UsePayload = [XyoPayload?, boolean?, XyoApiError?]
5
+
6
+ export type UseHuriOrHash = [...UsePayload, boolean?]
@@ -0,0 +1,6 @@
1
+ export * from './lib'
2
+ export * from './ResolvePayloadArgs'
3
+ export * from './useGetSchema'
4
+ export * from './useHuriHash'
5
+ export * from './useLoadPayload'
6
+ export * from './useResolveHuri'
@@ -0,0 +1,3 @@
1
+ export interface FetchHuriHashOptions {
2
+ changeActiveNetwork?: boolean
3
+ }
@@ -0,0 +1,13 @@
1
+ import { XyoNetworkPayload } from '@xyo-network/network'
2
+ import { Huri } from '@xyo-network/payload'
3
+
4
+ export const findHuriNetwork = (huriInstance: Huri, networks?: XyoNetworkPayload[]) => {
5
+ // see if huri archivist matches any archivist in the network configs
6
+ return networks
7
+ ?.filter((network) =>
8
+ network.nodes?.find((node) => {
9
+ return node.type === 'archivist' && new URL(node.uri).hostname === huriInstance.archivist
10
+ })
11
+ )
12
+ .shift()
13
+ }
@@ -0,0 +1,2 @@
1
+ export * from './FetchHuriHashOptions'
2
+ export * from './findHuriNetwork'
@@ -0,0 +1,70 @@
1
+ import { FormControl, TextField, Typography } from '@mui/material'
2
+ import { ComponentStory, Meta } from '@storybook/react'
3
+ import { FlexCol, FlexRow } from '@xylabs/react-flexbox'
4
+ import { XyoSchemaCache } from '@xyo-network/utils'
5
+ import { lazy, Suspense, useEffect, useState } from 'react'
6
+
7
+ import { useGetSchemaPayload } from './useGetSchema'
8
+
9
+ const JsonView = lazy(() => import(/* webpackChunkName: "jsonView" */ 'react-json-view'))
10
+
11
+ XyoSchemaCache.instance.proxy = 'https://beta.api.archivist.xyo.network/domain'
12
+
13
+ const UseGetSchemaComponent: React.FC<{ schema: string }> = ({ schema }) => {
14
+ const exampleSchemas = ['network.xyo.domain', 'network.xyo.payload', 'network.xyo.schema']
15
+ const [schemaFieldValue, setSchemaFieldValue] = useState('')
16
+ const { schemaPayload } = useGetSchemaPayload(schemaFieldValue)
17
+
18
+ useEffect(() => {
19
+ if (schema) {
20
+ setSchemaFieldValue(schema)
21
+ }
22
+ }, [schema])
23
+
24
+ return (
25
+ <>
26
+ <Typography variant="body1" fontWeight="bold" mb={2}>
27
+ Example schemas to test:
28
+ {exampleSchemas.map((schema, index) => (
29
+ <Typography component="span" mx={1} key={index} onClick={() => setSchemaFieldValue(schema)} sx={{ cursor: 'pointer', textDecoration: 'underline' }}>
30
+ {schema}
31
+ </Typography>
32
+ ))}
33
+ </Typography>
34
+ <FormControl>
35
+ <TextField value={schemaFieldValue} label="Schema Name" onChange={(e) => setSchemaFieldValue(e.target.value)} />
36
+ </FormControl>
37
+ <FlexRow my={3} justifyContent="start">
38
+ <Suspense fallback={<FlexCol busy />}>
39
+ <JsonView src={schemaPayload || {}} />
40
+ </Suspense>
41
+ </FlexRow>
42
+ </>
43
+ )
44
+ }
45
+
46
+ const StorybookEntry: Meta = {
47
+ argTypes: {},
48
+ component: UseGetSchemaComponent,
49
+ parameters: {
50
+ docs: {
51
+ page: null,
52
+ },
53
+ },
54
+ title: 'payload/useGetSchema',
55
+ }
56
+
57
+ const Template: ComponentStory<typeof UseGetSchemaComponent> = ({ schema }) => {
58
+ return <UseGetSchemaComponent schema={schema} />
59
+ }
60
+
61
+ const Default = Template.bind({})
62
+ Default.args = { schema: 'network.xyo.schema' }
63
+
64
+ const Domain = Template.bind({})
65
+ Domain.args = { schema: 'network.xyo.domain' }
66
+
67
+ export { Default, Domain }
68
+
69
+ // eslint-disable-next-line import/no-default-export
70
+ export default StorybookEntry
@@ -0,0 +1,51 @@
1
+ import { useAsyncEffect } from '@xylabs/react-shared'
2
+ import { XyoApiError } from '@xyo-network/api'
3
+ import { XyoPayloadBuilder, XyoSchemaPayload } from '@xyo-network/payload'
4
+ import { XyoSchemaCache, XyoSchemaCacheEntry } from '@xyo-network/utils'
5
+ import { useState } from 'react'
6
+
7
+ /**
8
+ * Gets a Huri and schema payload from a schema string
9
+ */
10
+ const useGetSchemaPayload = (schema?: string) => {
11
+ const [notFound, setNotFound] = useState(false)
12
+ const [apiError, setApiError] = useState<XyoApiError>()
13
+ const [schemaCacheEntry, setSchemaCacheEntry] = useState<XyoSchemaCacheEntry | null | undefined>()
14
+ const [schemaLocal, setSchemaLocal] = useState<string>()
15
+
16
+ useAsyncEffect(
17
+ // eslint-disable-next-line react-hooks/exhaustive-deps
18
+ async (mounted) => {
19
+ const firstRequest = !notFound && !schemaCacheEntry && !apiError
20
+ const schemaChanged = schema !== schemaLocal
21
+
22
+ if ((schema && firstRequest) || (schema && schemaChanged)) {
23
+ try {
24
+ const schemaCacheEntry = await XyoSchemaCache.instance.get(schema)
25
+ if (mounted()) {
26
+ setSchemaCacheEntry(schemaCacheEntry)
27
+ setNotFound(schemaCacheEntry === null || schemaCacheEntry === undefined)
28
+ }
29
+ } catch (e) {
30
+ console.error(e)
31
+ if (mounted()) {
32
+ setApiError(e as XyoApiError)
33
+ }
34
+ }
35
+ }
36
+ if (schemaChanged) {
37
+ setSchemaLocal(schema)
38
+ }
39
+ },
40
+ [apiError, notFound, schema, schemaLocal, schemaCacheEntry]
41
+ )
42
+
43
+ return {
44
+ apiError,
45
+ notFound,
46
+ schemaHuri: schemaCacheEntry?.huri,
47
+ schemaPayload: schemaCacheEntry ? new XyoPayloadBuilder<XyoSchemaPayload>(schemaCacheEntry?.payload).fields(schemaCacheEntry.payload).build() : schemaCacheEntry,
48
+ }
49
+ }
50
+
51
+ export { useGetSchemaPayload }
@@ -0,0 +1,108 @@
1
+ /* eslint-disable import/no-internal-modules */
2
+ import { Alert, Typography } from '@mui/material'
3
+ import { ComponentStory, Meta } from '@storybook/react'
4
+ import { ButtonEx } from '@xylabs/react-button'
5
+ import { FlexCol, FlexRow } from '@xylabs/react-flexbox'
6
+ import { Huri } from '@xyo-network/payload'
7
+ import { ArchivistApiProvider } from '@xyo-network/react-archivist-api'
8
+ import { NetworkMemoryProvider } from '@xyo-network/react-network'
9
+ import { XyoSchemaCache } from '@xyo-network/utils'
10
+ import { lazy, Suspense, useState } from 'react'
11
+
12
+ import { FetchHuriHashOptions } from './lib'
13
+ import { useHuriHash } from './useHuriHash'
14
+
15
+ const JsonView = lazy(() => import(/* webpackChunkName: "jsonView" */ 'react-json-view'))
16
+
17
+ interface UseHuriHashComponentProps {
18
+ huriOrHash: string | Huri
19
+ huriUri?: string
20
+ options?: FetchHuriHashOptions
21
+ reTestable?: boolean
22
+ }
23
+
24
+ const apiDomain = 'https://beta.api.archivist.xyo.network'
25
+ const hash = '5605fabad11b10bb5fb86b309ca0970894eda8f22362dda1a489817723bca992'
26
+ XyoSchemaCache.instance.proxy = `${apiDomain}/domain`
27
+
28
+ const mainApiDomain = 'https://api.archivist.xyo.network'
29
+ const mainHash = 'd3a3936e31ba1d835c528784ab77c1eaaeedd6e16b7aad68a88241ce539853cb'
30
+
31
+ const Wrapper: React.FC<UseHuriHashComponentProps> = (props) => (
32
+ <NetworkMemoryProvider>
33
+ <ArchivistApiProvider apiDomain={apiDomain}>
34
+ <UseHuriHashComponent {...props} />
35
+ </ArchivistApiProvider>
36
+ </NetworkMemoryProvider>
37
+ )
38
+
39
+ const UseHuriHashComponent: React.FC<UseHuriHashComponentProps> = ({ huriOrHash, huriUri, options, reTestable }) => {
40
+ const [trigger, setTrigger] = useState<string | Huri>(huriOrHash)
41
+ const [payload, notFound, , networkNotFound] = useHuriHash(trigger, huriUri, options)
42
+
43
+ return (
44
+ <>
45
+ <Typography variant="body1" fontWeight="bold">
46
+ Fetches the payload for a huriOrHash.
47
+ </Typography>
48
+ {reTestable ? (
49
+ <FlexRow columnGap={2}>
50
+ <ButtonEx variant="contained" onClick={() => setTrigger(hash)}>
51
+ Fetch Valid Hash
52
+ </ButtonEx>
53
+ <ButtonEx variant="contained" onClick={() => setTrigger('foo')}>
54
+ Hash Not Found
55
+ </ButtonEx>
56
+ </FlexRow>
57
+ ) : null}
58
+ <FlexCol my={3}>
59
+ {notFound === undefined && networkNotFound === undefined ? 'Loading...' : null}
60
+ {notFound ? <Alert severity="warning">Not Found</Alert> : null}
61
+ {networkNotFound ? <Alert severity="warning">Network Not Found</Alert> : null}
62
+ <Suspense fallback={<FlexCol busy />}>
63
+ <JsonView src={payload || {}} />
64
+ </Suspense>
65
+ </FlexCol>
66
+ </>
67
+ )
68
+ }
69
+
70
+ const StorybookEntry: Meta = {
71
+ argTypes: {},
72
+ component: UseHuriHashComponent,
73
+ parameters: {
74
+ docs: {
75
+ page: null,
76
+ },
77
+ },
78
+ title: 'payload/useHuriHash',
79
+ }
80
+
81
+ const Template: ComponentStory<typeof UseHuriHashComponent> = (props) => {
82
+ return <Wrapper {...props} />
83
+ }
84
+
85
+ const Default = Template.bind({})
86
+ Default.args = { huriOrHash: hash }
87
+
88
+ const NotFound = Template.bind({})
89
+ NotFound.args = { huriOrHash: 'foo', reTestable: true }
90
+
91
+ const WithHuri = Template.bind({})
92
+ WithHuri.args = { huriOrHash: new Huri(`${apiDomain}/${hash}`) }
93
+
94
+ const WithHuriUri = Template.bind({})
95
+ WithHuriUri.args = { huriUri: `${mainApiDomain}/${mainHash}` }
96
+
97
+ const WithHuriUriNetworkNotFound = Template.bind({})
98
+ WithHuriUriNetworkNotFound.args = { huriUri: `http://badarchivisturl.com/${mainHash}` }
99
+
100
+ // Note - story will work correctly once main net return 200 instead of 404 when payloads aren't found
101
+ // Resolve huriUri when network is different from the current network
102
+ const WithHuriUriNotFound = Template.bind({})
103
+ WithHuriUriNotFound.args = { huriUri: `${mainApiDomain}/foo` }
104
+
105
+ export { Default, NotFound, WithHuri, WithHuriUri, WithHuriUriNetworkNotFound, WithHuriUriNotFound }
106
+
107
+ // eslint-disable-next-line import/no-default-export
108
+ export default StorybookEntry
@@ -0,0 +1,38 @@
1
+ import { Huri } from '@xyo-network/payload'
2
+ import { useCallback } from 'react'
3
+
4
+ import { FetchHuriHashOptions } from './lib'
5
+ import { UseHuriOrHash } from './ResolvePayloadArgs'
6
+ import { useLoadPayload } from './useLoadPayload'
7
+ import { useResolveHuri } from './useResolveHuri'
8
+
9
+ /**
10
+ * Resolve a hash or a huri regardless of network
11
+ */
12
+ const useHuriHash = (huriOrHash?: string | Huri, huriUri?: string, options?: FetchHuriHashOptions): UseHuriOrHash => {
13
+ const hash = useCallback((huriOrHash?: string | Huri) => {
14
+ if (huriOrHash) {
15
+ if (typeof huriOrHash === 'string') {
16
+ return huriOrHash
17
+ }
18
+ if (huriOrHash instanceof Huri) {
19
+ return huriOrHash.hash
20
+ }
21
+ }
22
+ }, [])
23
+
24
+ const providedHash = hash(huriOrHash)
25
+
26
+ // Optimistically try to grab the has from the current network and archive
27
+ const [payload, notFound, apiError] = useLoadPayload(providedHash)
28
+
29
+ // if a huriUri was passed, we can safely override the notFound from the hash only query
30
+ const notFoundOverride = huriUri ? true : notFound
31
+
32
+ // If payload isn't found, fallback to the huriUri
33
+ const [huriPayload, huriPayloadNotFound, huriApiError, networkNotFound] = useResolveHuri(huriUri, notFoundOverride, options)
34
+
35
+ return [payload ?? huriPayload, huriPayloadNotFound, apiError ?? huriApiError, networkNotFound]
36
+ }
37
+
38
+ export { useHuriHash }
@@ -0,0 +1,69 @@
1
+ /* eslint-disable import/no-internal-modules */
2
+ import { ComponentStory, Meta } from '@storybook/react'
3
+ import { ButtonEx } from '@xylabs/react-button'
4
+ import { FlexCol } from '@xylabs/react-flexbox'
5
+ import { ArchivistApiProvider, useArchivistApi } from '@xyo-network/react-archivist-api'
6
+ import { lazy, Suspense, useState } from 'react'
7
+
8
+ import { useLoadPayload } from './useLoadPayload'
9
+
10
+ const JsonView = lazy(() => import(/* webpackChunkName: "jsonView" */ 'react-json-view'))
11
+
12
+ const apiDomain = 'https://beta.api.archivist.xyo.network'
13
+ const hash = '5605fabad11b10bb5fb86b309ca0970894eda8f22362dda1a489817723bca992'
14
+
15
+ const Wrapper: React.FC<{ hash?: string }> = ({ hash }) => (
16
+ <ArchivistApiProvider apiDomain={apiDomain}>
17
+ <UsePayloadComponent hash={hash} />
18
+ </ArchivistApiProvider>
19
+ )
20
+
21
+ const UsePayloadComponent: React.FC<{ hash?: string }> = ({ hash }) => {
22
+ const { api } = useArchivistApi()
23
+ const [trigger, setTrigger] = useState<string>()
24
+ const [payload, notFound] = useLoadPayload(trigger)
25
+
26
+ return (
27
+ <>
28
+ {api ? (
29
+ <>
30
+ <ButtonEx variant="contained" marginBottom={2} onClick={() => setTrigger(hash)}>
31
+ Fetch Payload
32
+ </ButtonEx>
33
+ <ButtonEx variant="contained" onClick={() => setTrigger('foo')}>
34
+ Fetch Not Found
35
+ </ButtonEx>
36
+ </>
37
+ ) : null}
38
+ <FlexCol my={3}>
39
+ <Suspense fallback={<FlexCol busy />}>
40
+ {notFound ? 'Not Found' : null}
41
+ <JsonView src={payload || {}} />
42
+ </Suspense>
43
+ </FlexCol>
44
+ </>
45
+ )
46
+ }
47
+
48
+ const StorybookEntry: Meta = {
49
+ argTypes: {},
50
+ component: UsePayloadComponent,
51
+ parameters: {
52
+ docs: {
53
+ page: null,
54
+ },
55
+ },
56
+ title: 'payload/usePayload',
57
+ }
58
+
59
+ const Template: ComponentStory<typeof UsePayloadComponent> = (props) => {
60
+ return <Wrapper {...props} />
61
+ }
62
+
63
+ const Default = Template.bind({})
64
+ Default.args = { hash }
65
+
66
+ export { Default }
67
+
68
+ // eslint-disable-next-line import/no-default-export
69
+ export default StorybookEntry
@@ -0,0 +1,60 @@
1
+ import { useAsyncEffect } from '@xylabs/react-shared'
2
+ import { XyoApiError } from '@xyo-network/api'
3
+ import { XyoPayload } from '@xyo-network/payload'
4
+ import { useArchive } from '@xyo-network/react-archive'
5
+ import { useArchivistApi } from '@xyo-network/react-archivist-api'
6
+ import { useEffect, useState } from 'react'
7
+
8
+ import { UsePayload } from './ResolvePayloadArgs'
9
+
10
+ // I hate this name, would prefer loadPayload, but need use prefix to make it a 'hook'
11
+ export const useLoadPayload = (hash?: string): UsePayload => {
12
+ const { api } = useArchivistApi()
13
+ const { archive } = useArchive()
14
+ const [localHash, setLocalHash] = useState<string>()
15
+ const [notFound, setNotFound] = useState<boolean>()
16
+ const [apiError, setApiError] = useState<XyoApiError>()
17
+ const [payload, setPayload] = useState<XyoPayload>()
18
+
19
+ const reset = () => {
20
+ setPayload(undefined)
21
+ setApiError(undefined)
22
+ setNotFound(undefined)
23
+ }
24
+
25
+ // allow for hash changes to retrigger the api call
26
+ useEffect(() => {
27
+ if (hash !== localHash) {
28
+ setLocalHash(hash)
29
+ reset()
30
+ }
31
+ }, [hash, localHash])
32
+
33
+ useAsyncEffect(
34
+ // eslint-disable-next-line react-hooks/exhaustive-deps
35
+ async (mounted) => {
36
+ if (api && localHash && localHash.length > 0 && notFound === undefined) {
37
+ reset()
38
+ try {
39
+ const result = await api?.archive(archive).payload.hash(localHash).get()
40
+ if (mounted()) {
41
+ if (result?.length) {
42
+ setPayload(result[0])
43
+ setNotFound(false)
44
+ } else if (result?.length === 0) {
45
+ setNotFound(true)
46
+ setPayload(undefined)
47
+ }
48
+ }
49
+ } catch (e) {
50
+ reset()
51
+ setNotFound(false)
52
+ setApiError(e as XyoApiError)
53
+ console.error(e)
54
+ }
55
+ }
56
+ },
57
+ [hash, api, archive, payload, notFound, localHash]
58
+ )
59
+ return [payload, notFound, apiError]
60
+ }