@xyo-network/react-api 2.32.0-rc.11

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 (143) hide show
  1. package/LICENSE +165 -0
  2. package/README.md +70 -0
  3. package/dist/cjs/components/ApiErrorAlert.d.ts +9 -0
  4. package/dist/cjs/components/ApiErrorAlert.d.ts.map +1 -0
  5. package/dist/cjs/components/ApiErrorAlert.js +13 -0
  6. package/dist/cjs/components/ApiErrorAlert.js.map +1 -0
  7. package/dist/cjs/components/JsonRouteWrapper/JsonApiButton.d.ts +4 -0
  8. package/dist/cjs/components/JsonRouteWrapper/JsonApiButton.d.ts.map +1 -0
  9. package/dist/cjs/components/JsonRouteWrapper/JsonApiButton.js +15 -0
  10. package/dist/cjs/components/JsonRouteWrapper/JsonApiButton.js.map +1 -0
  11. package/dist/cjs/components/JsonRouteWrapper/JsonRouteWrapper.d.ts +11 -0
  12. package/dist/cjs/components/JsonRouteWrapper/JsonRouteWrapper.d.ts.map +1 -0
  13. package/dist/cjs/components/JsonRouteWrapper/JsonRouteWrapper.js +47 -0
  14. package/dist/cjs/components/JsonRouteWrapper/JsonRouteWrapper.js.map +1 -0
  15. package/dist/cjs/components/JsonRouteWrapper/index.d.ts +3 -0
  16. package/dist/cjs/components/JsonRouteWrapper/index.d.ts.map +1 -0
  17. package/dist/cjs/components/JsonRouteWrapper/index.js +6 -0
  18. package/dist/cjs/components/JsonRouteWrapper/index.js.map +1 -0
  19. package/dist/cjs/components/ResultLoader.d.ts +9 -0
  20. package/dist/cjs/components/ResultLoader.d.ts.map +1 -0
  21. package/dist/cjs/components/ResultLoader.js +24 -0
  22. package/dist/cjs/components/ResultLoader.js.map +1 -0
  23. package/dist/cjs/components/index.d.ts +4 -0
  24. package/dist/cjs/components/index.d.ts.map +1 -0
  25. package/dist/cjs/components/index.js +7 -0
  26. package/dist/cjs/components/index.js.map +1 -0
  27. package/dist/cjs/hooks/ResolvePayloadArgs.d.ts +5 -0
  28. package/dist/cjs/hooks/ResolvePayloadArgs.d.ts.map +1 -0
  29. package/dist/cjs/hooks/ResolvePayloadArgs.js +3 -0
  30. package/dist/cjs/hooks/ResolvePayloadArgs.js.map +1 -0
  31. package/dist/cjs/hooks/index.d.ts +6 -0
  32. package/dist/cjs/hooks/index.d.ts.map +1 -0
  33. package/dist/cjs/hooks/index.js +9 -0
  34. package/dist/cjs/hooks/index.js.map +1 -0
  35. package/dist/cjs/hooks/lib/FetchHuriHashOptions.d.ts +4 -0
  36. package/dist/cjs/hooks/lib/FetchHuriHashOptions.d.ts.map +1 -0
  37. package/dist/cjs/hooks/lib/FetchHuriHashOptions.js +3 -0
  38. package/dist/cjs/hooks/lib/FetchHuriHashOptions.js.map +1 -0
  39. package/dist/cjs/hooks/lib/findHuriNetwork.d.ts +4 -0
  40. package/dist/cjs/hooks/lib/findHuriNetwork.d.ts.map +1 -0
  41. package/dist/cjs/hooks/lib/findHuriNetwork.js +14 -0
  42. package/dist/cjs/hooks/lib/findHuriNetwork.js.map +1 -0
  43. package/dist/cjs/hooks/lib/index.d.ts +3 -0
  44. package/dist/cjs/hooks/lib/index.d.ts.map +1 -0
  45. package/dist/cjs/hooks/lib/index.js +6 -0
  46. package/dist/cjs/hooks/lib/index.js.map +1 -0
  47. package/dist/cjs/hooks/useHuriHash.d.ts +13 -0
  48. package/dist/cjs/hooks/useHuriHash.d.ts.map +1 -0
  49. package/dist/cjs/hooks/useHuriHash.js +33 -0
  50. package/dist/cjs/hooks/useHuriHash.js.map +1 -0
  51. package/dist/cjs/hooks/useLoadPayload.d.ts +3 -0
  52. package/dist/cjs/hooks/useLoadPayload.d.ts.map +1 -0
  53. package/dist/cjs/hooks/useLoadPayload.js +57 -0
  54. package/dist/cjs/hooks/useLoadPayload.js.map +1 -0
  55. package/dist/cjs/hooks/useResolveHuri.d.ts +10 -0
  56. package/dist/cjs/hooks/useResolveHuri.d.ts.map +1 -0
  57. package/dist/cjs/hooks/useResolveHuri.js +68 -0
  58. package/dist/cjs/hooks/useResolveHuri.js.map +1 -0
  59. package/dist/cjs/index.d.ts +2 -0
  60. package/dist/cjs/index.d.ts.map +1 -0
  61. package/dist/cjs/index.js +5 -0
  62. package/dist/cjs/index.js.map +1 -0
  63. package/dist/docs.json +19042 -0
  64. package/dist/esm/components/ApiErrorAlert.d.ts +9 -0
  65. package/dist/esm/components/ApiErrorAlert.d.ts.map +1 -0
  66. package/dist/esm/components/ApiErrorAlert.js +7 -0
  67. package/dist/esm/components/ApiErrorAlert.js.map +1 -0
  68. package/dist/esm/components/JsonRouteWrapper/JsonApiButton.d.ts +4 -0
  69. package/dist/esm/components/JsonRouteWrapper/JsonApiButton.d.ts.map +1 -0
  70. package/dist/esm/components/JsonRouteWrapper/JsonApiButton.js +11 -0
  71. package/dist/esm/components/JsonRouteWrapper/JsonApiButton.js.map +1 -0
  72. package/dist/esm/components/JsonRouteWrapper/JsonRouteWrapper.d.ts +11 -0
  73. package/dist/esm/components/JsonRouteWrapper/JsonRouteWrapper.d.ts.map +1 -0
  74. package/dist/esm/components/JsonRouteWrapper/JsonRouteWrapper.js +41 -0
  75. package/dist/esm/components/JsonRouteWrapper/JsonRouteWrapper.js.map +1 -0
  76. package/dist/esm/components/JsonRouteWrapper/index.d.ts +3 -0
  77. package/dist/esm/components/JsonRouteWrapper/index.d.ts.map +1 -0
  78. package/dist/esm/components/JsonRouteWrapper/index.js +3 -0
  79. package/dist/esm/components/JsonRouteWrapper/index.js.map +1 -0
  80. package/dist/esm/components/ResultLoader.d.ts +9 -0
  81. package/dist/esm/components/ResultLoader.d.ts.map +1 -0
  82. package/dist/esm/components/ResultLoader.js +20 -0
  83. package/dist/esm/components/ResultLoader.js.map +1 -0
  84. package/dist/esm/components/index.d.ts +4 -0
  85. package/dist/esm/components/index.d.ts.map +1 -0
  86. package/dist/esm/components/index.js +4 -0
  87. package/dist/esm/components/index.js.map +1 -0
  88. package/dist/esm/hooks/ResolvePayloadArgs.d.ts +5 -0
  89. package/dist/esm/hooks/ResolvePayloadArgs.d.ts.map +1 -0
  90. package/dist/esm/hooks/ResolvePayloadArgs.js +2 -0
  91. package/dist/esm/hooks/ResolvePayloadArgs.js.map +1 -0
  92. package/dist/esm/hooks/index.d.ts +6 -0
  93. package/dist/esm/hooks/index.d.ts.map +1 -0
  94. package/dist/esm/hooks/index.js +6 -0
  95. package/dist/esm/hooks/index.js.map +1 -0
  96. package/dist/esm/hooks/lib/FetchHuriHashOptions.d.ts +4 -0
  97. package/dist/esm/hooks/lib/FetchHuriHashOptions.d.ts.map +1 -0
  98. package/dist/esm/hooks/lib/FetchHuriHashOptions.js +2 -0
  99. package/dist/esm/hooks/lib/FetchHuriHashOptions.js.map +1 -0
  100. package/dist/esm/hooks/lib/findHuriNetwork.d.ts +4 -0
  101. package/dist/esm/hooks/lib/findHuriNetwork.d.ts.map +1 -0
  102. package/dist/esm/hooks/lib/findHuriNetwork.js +9 -0
  103. package/dist/esm/hooks/lib/findHuriNetwork.js.map +1 -0
  104. package/dist/esm/hooks/lib/index.d.ts +3 -0
  105. package/dist/esm/hooks/lib/index.d.ts.map +1 -0
  106. package/dist/esm/hooks/lib/index.js +3 -0
  107. package/dist/esm/hooks/lib/index.js.map +1 -0
  108. package/dist/esm/hooks/useHuriHash.d.ts +13 -0
  109. package/dist/esm/hooks/useHuriHash.d.ts.map +1 -0
  110. package/dist/esm/hooks/useHuriHash.js +30 -0
  111. package/dist/esm/hooks/useHuriHash.js.map +1 -0
  112. package/dist/esm/hooks/useLoadPayload.d.ts +3 -0
  113. package/dist/esm/hooks/useLoadPayload.d.ts.map +1 -0
  114. package/dist/esm/hooks/useLoadPayload.js +52 -0
  115. package/dist/esm/hooks/useLoadPayload.js.map +1 -0
  116. package/dist/esm/hooks/useResolveHuri.d.ts +10 -0
  117. package/dist/esm/hooks/useResolveHuri.d.ts.map +1 -0
  118. package/dist/esm/hooks/useResolveHuri.js +64 -0
  119. package/dist/esm/hooks/useResolveHuri.js.map +1 -0
  120. package/dist/esm/index.d.ts +2 -0
  121. package/dist/esm/index.d.ts.map +1 -0
  122. package/dist/esm/index.js +2 -0
  123. package/dist/esm/index.js.map +1 -0
  124. package/package.json +80 -0
  125. package/src/components/ApiErrorAlert.tsx +21 -0
  126. package/src/components/JsonRouteWrapper/JsonApiButton.tsx +19 -0
  127. package/src/components/JsonRouteWrapper/JsonRouteWrapper.stories.tsx +68 -0
  128. package/src/components/JsonRouteWrapper/JsonRouteWrapper.tsx +85 -0
  129. package/src/components/JsonRouteWrapper/index.ts +2 -0
  130. package/src/components/ResultLoader.stories.tsx +37 -0
  131. package/src/components/ResultLoader.tsx +26 -0
  132. package/src/components/index.ts +3 -0
  133. package/src/hooks/ResolvePayloadArgs.ts +6 -0
  134. package/src/hooks/index.ts +5 -0
  135. package/src/hooks/lib/FetchHuriHashOptions.ts +3 -0
  136. package/src/hooks/lib/findHuriNetwork.ts +13 -0
  137. package/src/hooks/lib/index.ts +2 -0
  138. package/src/hooks/useHuriHash.stories.tsx +108 -0
  139. package/src/hooks/useHuriHash.tsx +40 -0
  140. package/src/hooks/useLoadPayload.stories.tsx +69 -0
  141. package/src/hooks/useLoadPayload.tsx +59 -0
  142. package/src/hooks/useResolveHuri.tsx +75 -0
  143. package/src/index.ts +1 -0
@@ -0,0 +1,21 @@
1
+ import { Alert, AlertProps, AlertTitle, Typography } from '@mui/material'
2
+ import { XyoApiError } from '@xyo-network/api'
3
+
4
+ export interface ApiErrorAlertProps extends AlertProps {
5
+ call?: XyoApiError
6
+ }
7
+
8
+ const ApiErrorAlert: React.FC<ApiErrorAlertProps> = ({ call, ...props }) => {
9
+ return (
10
+ <Alert severity="error" {...props}>
11
+ <AlertTitle>Whoops! Something went wrong</AlertTitle>
12
+ <Typography sx={{ wordBreak: 'break-all' }}>{call?.config?.url}</Typography>
13
+ <Typography variant="caption" mr={0.5} fontWeight="bold">
14
+ Error:
15
+ </Typography>
16
+ <Typography variant="caption">{call?.message}</Typography>
17
+ </Alert>
18
+ )
19
+ }
20
+
21
+ export { ApiErrorAlert }
@@ -0,0 +1,19 @@
1
+ import { ButtonEx, ButtonExProps } from '@xylabs/react-button'
2
+ import { useSearchParams } from 'react-router-dom'
3
+
4
+ export const JsonApiButton: React.FC<ButtonExProps> = (props) => {
5
+ const [searchParams, setSearchParams] = useSearchParams()
6
+ return (
7
+ <ButtonEx
8
+ marginX={2}
9
+ variant="outlined"
10
+ onClick={() => {
11
+ searchParams.set('json', 'true')
12
+ setSearchParams(searchParams)
13
+ }}
14
+ {...props}
15
+ >
16
+ JSON
17
+ </ButtonEx>
18
+ )
19
+ }
@@ -0,0 +1,68 @@
1
+ import { ComponentMeta, ComponentStory, DecoratorFn } from '@storybook/react'
2
+ import { ButtonEx } from '@xylabs/react-button'
3
+ import { FlexCol } from '@xylabs/react-flexbox'
4
+ import { XyoArchivistApi } from '@xyo-network/api'
5
+ import { BrowserRouter, useSearchParams } from 'react-router-dom'
6
+
7
+ import { JsonRouteWrapper } from './JsonRouteWrapper'
8
+
9
+ const JsonDecorator: DecoratorFn = (Story) => (
10
+ <BrowserRouter>
11
+ <Story />
12
+ </BrowserRouter>
13
+ )
14
+
15
+ const StorybookEntry = {
16
+ argTypes: {
17
+ apiDomain: 'https://beta.api.archivist.xyo.network',
18
+ },
19
+ component: JsonRouteWrapper,
20
+ decorators: [JsonDecorator],
21
+ parameters: {
22
+ docs: {
23
+ page: null,
24
+ },
25
+ },
26
+ title: 'shared/JsonRouteWrapper',
27
+ } as ComponentMeta<typeof JsonRouteWrapper>
28
+
29
+ const Template: ComponentStory<typeof JsonRouteWrapper> = (props) => {
30
+ const [searchParams, setSearchParams] = useSearchParams()
31
+ const activeJson = searchParams.get('json')
32
+ return (
33
+ <FlexCol>
34
+ <ButtonEx
35
+ marginY={3}
36
+ onClick={() => {
37
+ activeJson === 'true' ? searchParams.delete('json') : searchParams.set('json', 'true')
38
+ setSearchParams(searchParams)
39
+ }}
40
+ variant="outlined"
41
+ >
42
+ Toggle JSON Page
43
+ </ButtonEx>
44
+ <JsonRouteWrapper {...props}></JsonRouteWrapper>
45
+ </FlexCol>
46
+ )
47
+ }
48
+
49
+ const Default = Template.bind({})
50
+ Default.args = {
51
+ callback: () => new XyoArchivistApi({ apiDomain: 'https://beta.api.archivist.xyo.network' }).archive('temp').block.stats.get(),
52
+ }
53
+
54
+ const HideBackButton = Template.bind({})
55
+ HideBackButton.args = {
56
+ callback: () =>
57
+ new XyoArchivistApi({
58
+ apiDomain: 'https://beta.api.archivist.xyo.network',
59
+ })
60
+ .archive('temp')
61
+ .block.stats.get(),
62
+ noBackButton: true,
63
+ }
64
+
65
+ export { Default, HideBackButton }
66
+
67
+ // eslint-disable-next-line import/no-default-export
68
+ export default StorybookEntry
@@ -0,0 +1,85 @@
1
+ import { ButtonEx } from '@xylabs/react-button'
2
+ import { ErrorDialog } from '@xylabs/react-dialogs'
3
+ import { FlexBoxProps, FlexCol, FlexRow } from '@xylabs/react-flexbox'
4
+ import { useAsyncEffect } from '@xylabs/react-shared'
5
+ import { XyoApiError } from '@xyo-network/api'
6
+ import { lazy, Suspense, useState } from 'react'
7
+ import { ReactJsonViewProps } from 'react-json-view'
8
+ import { useSearchParams } from 'react-router-dom'
9
+
10
+ import { JsonApiButton } from './JsonApiButton'
11
+
12
+ const JsonView = lazy(() => import(/* webpackChunkName: "jsonView" */ 'react-json-view'))
13
+
14
+ export interface JsonFromPromiseProps extends FlexBoxProps {
15
+ callback: () => Promise<object | undefined>
16
+ noBackButton?: boolean
17
+ noJsonButton?: boolean
18
+ jsonViewProps?: ReactJsonViewProps
19
+ }
20
+
21
+ export const JsonRouteWrapper: React.FC<JsonFromPromiseProps> = ({
22
+ callback,
23
+ children,
24
+ noBackButton = false,
25
+ noJsonButton = false,
26
+ jsonViewProps,
27
+ ...props
28
+ }) => {
29
+ const [apiResponse, setApiResponse] = useState<object>()
30
+ const [apiError, setApiError] = useState<XyoApiError>()
31
+ const [searchParams, setSearchParams] = useSearchParams()
32
+ const active = !!searchParams.get('json')
33
+
34
+ useAsyncEffect(
35
+ // eslint-disable-next-line react-hooks/exhaustive-deps
36
+ async (mounted) => {
37
+ try {
38
+ const response = await callback()
39
+ if (mounted()) {
40
+ setApiResponse(response)
41
+ }
42
+ } catch (ex) {
43
+ if (mounted()) {
44
+ const error = ex as XyoApiError
45
+ if (error.isXyoError) {
46
+ setApiError(error)
47
+ } else {
48
+ throw ex
49
+ }
50
+ }
51
+ }
52
+ },
53
+ [callback],
54
+ )
55
+
56
+ return (
57
+ <FlexCol {...props}>
58
+ {active ? (
59
+ <>
60
+ <Suspense fallback={null}>{apiResponse && <JsonView src={apiResponse} collapseStringsAfterLength={64} {...jsonViewProps} />}</Suspense>
61
+ {!noBackButton && (
62
+ <FlexRow marginY={3}>
63
+ <ButtonEx
64
+ flexDirection="row"
65
+ variant="outlined"
66
+ onClick={() => {
67
+ searchParams.delete('json')
68
+ setSearchParams(searchParams)
69
+ }}
70
+ >
71
+ Back
72
+ </ButtonEx>
73
+ </FlexRow>
74
+ )}
75
+ <ErrorDialog title="Error Fetching JSON" error={apiError} open={!!apiError} onAction={() => setApiError(undefined)} />
76
+ </>
77
+ ) : (
78
+ <>
79
+ {children}
80
+ {!noJsonButton && <JsonApiButton marginTop={2} />}
81
+ </>
82
+ )}
83
+ </FlexCol>
84
+ )
85
+ }
@@ -0,0 +1,2 @@
1
+ export * from './JsonApiButton'
2
+ export * from './JsonRouteWrapper'
@@ -0,0 +1,37 @@
1
+ /* eslint-disable import/no-internal-modules */
2
+ import { ComponentStory, Meta } from '@storybook/react'
3
+ import { AxiosError } from 'axios'
4
+
5
+ import { ResultLoader } from './ResultLoader'
6
+
7
+ const StorybookEntry: Meta = {
8
+ argTypes: {},
9
+ component: ResultLoader,
10
+ parameters: {
11
+ docs: {
12
+ page: null,
13
+ },
14
+ },
15
+ title: 'webapp/ResultLoader',
16
+ }
17
+
18
+ const Template: ComponentStory<typeof ResultLoader> = (props) => {
19
+ return <ResultLoader {...props} />
20
+ }
21
+
22
+ const Default = Template.bind({})
23
+ Default.args = {}
24
+
25
+ const NotFound = Template.bind({})
26
+ NotFound.args = { notFound: true }
27
+
28
+ const ApiError = Template.bind({})
29
+ ApiError.args = { apiError: new AxiosError(), children: <h1>Shown in case of error</h1> }
30
+
31
+ const SearchResult = Template.bind({})
32
+ SearchResult.args = { children: <h1>Shown when there is a valid result</h1>, searchResult: 'foo' }
33
+
34
+ export { ApiError, Default, NotFound, SearchResult }
35
+
36
+ // eslint-disable-next-line import/no-default-export
37
+ export default StorybookEntry
@@ -0,0 +1,26 @@
1
+ import { FlexGrowRow } from '@xylabs/react-flexbox'
2
+ import { XyoApiError } from '@xyo-network/api'
3
+ import { NotFound } from '@xyo-network/react-shared'
4
+ import { PropsWithChildren } from 'react'
5
+
6
+ export interface HandleItemDetailLoadingProps<T> {
7
+ apiError: Error | XyoApiError | undefined
8
+ notFound: boolean
9
+ searchResult: T | undefined
10
+ }
11
+
12
+ export function ResultLoader<T>(props: PropsWithChildren<HandleItemDetailLoadingProps<T>>) {
13
+ const { notFound, apiError, searchResult, children } = props
14
+ if (notFound) {
15
+ return <NotFound />
16
+ }
17
+ // Defer error handling to the children
18
+ if (apiError) {
19
+ return <>{children}</>
20
+ }
21
+ if (searchResult === undefined) {
22
+ return <FlexGrowRow busy minHeight="50px" />
23
+ } else {
24
+ return <>{children}</>
25
+ }
26
+ }
@@ -0,0 +1,3 @@
1
+ export * from './ApiErrorAlert'
2
+ export * from './JsonRouteWrapper'
3
+ export * from './ResultLoader'
@@ -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,5 @@
1
+ export * from './lib'
2
+ export * from './ResolvePayloadArgs'
3
+ export * from './useHuriHash'
4
+ export * from './useLoadPayload'
5
+ 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,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,40 @@
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
+ //AT: TODO -> Talk about this pattern
27
+
28
+ // Optimistically try to grab the has from the current network and archive
29
+ const [payload, notFound, apiError] = useLoadPayload(providedHash)
30
+
31
+ // if a huriUri was passed, we can safely override the notFound from the hash only query
32
+ const notFoundOverride = huriUri ? true : notFound
33
+
34
+ // If payload isn't found, fallback to the huriUri
35
+ const [huriPayload, huriPayloadNotFound, huriApiError, networkNotFound] = useResolveHuri(huriUri, notFoundOverride, options)
36
+
37
+ return [payload ?? huriPayload, huriPayloadNotFound, apiError ?? huriApiError, networkNotFound]
38
+ }
39
+
40
+ 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,59 @@
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
+ export const useLoadPayload = (hash?: string): UsePayload => {
11
+ const { api } = useArchivistApi()
12
+ const { archive } = useArchive()
13
+ const [localHash, setLocalHash] = useState<string>()
14
+ const [notFound, setNotFound] = useState<boolean>()
15
+ const [apiError, setApiError] = useState<XyoApiError>()
16
+ const [payload, setPayload] = useState<XyoPayload>()
17
+
18
+ const reset = () => {
19
+ setPayload(undefined)
20
+ setApiError(undefined)
21
+ setNotFound(undefined)
22
+ }
23
+
24
+ // allow for hash changes to retrigger the api call
25
+ useEffect(() => {
26
+ if (hash !== localHash) {
27
+ setLocalHash(hash)
28
+ reset()
29
+ }
30
+ }, [hash, localHash])
31
+
32
+ useAsyncEffect(
33
+ // eslint-disable-next-line react-hooks/exhaustive-deps
34
+ async (mounted) => {
35
+ if (api && localHash && localHash.length > 0 && notFound === undefined) {
36
+ reset()
37
+ try {
38
+ const result = await api?.archive(archive).payload.hash(localHash).get()
39
+ if (mounted()) {
40
+ if (result?.length) {
41
+ setPayload(result[0])
42
+ setNotFound(false)
43
+ } else if (result?.length === 0) {
44
+ setNotFound(true)
45
+ setPayload(undefined)
46
+ }
47
+ }
48
+ } catch (e) {
49
+ reset()
50
+ setNotFound(false)
51
+ setApiError(e as XyoApiError)
52
+ console.error(e)
53
+ }
54
+ }
55
+ },
56
+ [hash, api, archive, payload, notFound, localHash],
57
+ )
58
+ return [payload, notFound, apiError]
59
+ }
@@ -0,0 +1,75 @@
1
+ import { useAsyncEffect } from '@xylabs/react-shared'
2
+ import { XyoApiError } from '@xyo-network/api'
3
+ import { Huri, XyoPayload } from '@xyo-network/payload'
4
+ import { useNetwork } from '@xyo-network/react-network'
5
+ import { useEffect, useState } from 'react'
6
+
7
+ import { FetchHuriHashOptions, findHuriNetwork } from './lib'
8
+ import { UseHuriOrHash } from './ResolvePayloadArgs'
9
+
10
+ const useResolveHuri = (huriUri?: string, dependentNotFound?: boolean, options?: FetchHuriHashOptions): UseHuriOrHash => {
11
+ const { network, networks, setNetwork } = useNetwork()
12
+ const [huriPayload, setHuriPayload] = useState<XyoPayload>()
13
+ const [huriPayloadNotFound, setHuriPayloadNotFound] = useState<boolean>()
14
+ const [huriNetworkNotFound, setHuriNetworkNotFound] = useState<boolean>()
15
+ const [huriApiError, setHuriApiError] = useState<XyoApiError>()
16
+
17
+ const { changeActiveNetwork } = options ?? {}
18
+
19
+ //AT: TODO -> Talk about this pattern
20
+ const reset = () => {
21
+ setHuriPayload(undefined)
22
+ setHuriPayloadNotFound(undefined)
23
+ setHuriApiError(undefined)
24
+ }
25
+
26
+ useEffect(() => {
27
+ // Initially, sync local not found with dependent's status
28
+ setHuriPayloadNotFound(dependentNotFound)
29
+ }, [dependentNotFound])
30
+
31
+ useAsyncEffect(
32
+ // eslint-disable-next-line react-hooks/exhaustive-deps
33
+ async (mounted) => {
34
+ // if dependent value is resolved, don't do anything, if not resolved, try to resolve huriUri
35
+ if (dependentNotFound && huriUri) {
36
+ reset()
37
+ const huriInstance = new Huri(huriUri)
38
+
39
+ const foundHuriNetwork = findHuriNetwork(huriInstance, networks)
40
+
41
+ if (foundHuriNetwork && mounted()) {
42
+ if (network !== foundHuriNetwork && changeActiveNetwork) {
43
+ setNetwork?.(foundHuriNetwork)
44
+ return
45
+ }
46
+
47
+ try {
48
+ const huriPayload = await huriInstance.fetch()
49
+ if (mounted()) {
50
+ if (huriPayload) {
51
+ setHuriPayload(huriPayload)
52
+ setHuriPayloadNotFound(false)
53
+ } else {
54
+ setHuriPayloadNotFound(true)
55
+ }
56
+ }
57
+ } catch (e) {
58
+ if (mounted()) {
59
+ setHuriPayloadNotFound(false)
60
+ setHuriPayload(undefined)
61
+ setHuriApiError(e as XyoApiError)
62
+ }
63
+ }
64
+ } else {
65
+ setHuriNetworkNotFound(true)
66
+ }
67
+ }
68
+ },
69
+ [huriUri, network, networks, dependentNotFound, setNetwork, changeActiveNetwork],
70
+ )
71
+
72
+ return [huriPayload, huriPayloadNotFound, huriApiError, huriNetworkNotFound]
73
+ }
74
+
75
+ export { useResolveHuri }
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './components'