@contember/react-client 1.0.0-alpha.8 → 1.0.0-rc.4

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 (224) hide show
  1. package/dist/types/ClientError.d.ts +3 -0
  2. package/dist/types/ClientError.d.ts.map +1 -0
  3. package/dist/types/ContemberClient.d.ts +12 -0
  4. package/dist/types/ContemberClient.d.ts.map +1 -0
  5. package/dist/types/apiRequest/ApiRequestAction.d.ts +13 -0
  6. package/dist/types/apiRequest/ApiRequestAction.d.ts.map +1 -0
  7. package/dist/types/apiRequest/ApiRequestState.d.ts +21 -0
  8. package/dist/types/apiRequest/ApiRequestState.d.ts.map +1 -0
  9. package/dist/types/apiRequest/apiRequestReducer.d.ts +5 -0
  10. package/dist/types/apiRequest/apiRequestReducer.d.ts.map +1 -0
  11. package/dist/types/apiRequest/index.d.ts +5 -0
  12. package/dist/types/apiRequest/index.d.ts.map +1 -0
  13. package/dist/types/apiRequest/useApiRequest.d.ts +8 -0
  14. package/dist/types/apiRequest/useApiRequest.d.ts.map +1 -0
  15. package/dist/types/auth/SessionTokenContext.d.ts +3 -0
  16. package/dist/types/auth/SessionTokenContext.d.ts.map +1 -0
  17. package/dist/types/auth/SetSessionTokenContext.d.ts +3 -0
  18. package/dist/types/auth/SetSessionTokenContext.d.ts.map +1 -0
  19. package/dist/types/auth/index.d.ts +5 -0
  20. package/dist/types/auth/index.d.ts.map +1 -0
  21. package/dist/types/auth/useSessionToken.d.ts +2 -0
  22. package/dist/types/auth/useSessionToken.d.ts.map +1 -0
  23. package/dist/types/auth/useSetSessionToken.d.ts +2 -0
  24. package/dist/types/auth/useSetSessionToken.d.ts.map +1 -0
  25. package/dist/types/components/RichTextRenderer/BuiltinElements.d.ts +49 -0
  26. package/dist/types/components/RichTextRenderer/BuiltinElements.d.ts.map +1 -0
  27. package/dist/types/components/RichTextRenderer/BuiltinLeaves.d.ts +21 -0
  28. package/dist/types/components/RichTextRenderer/BuiltinLeaves.d.ts.map +1 -0
  29. package/dist/types/components/RichTextRenderer/ElementRenderer.d.ts +22 -0
  30. package/dist/types/components/RichTextRenderer/ElementRenderer.d.ts.map +1 -0
  31. package/dist/types/components/RichTextRenderer/LeafRenderer.d.ts +14 -0
  32. package/dist/types/components/RichTextRenderer/LeafRenderer.d.ts.map +1 -0
  33. package/dist/types/components/RichTextRenderer/NormalizedRichTextBlock.d.ts +14 -0
  34. package/dist/types/components/RichTextRenderer/NormalizedRichTextBlock.d.ts.map +1 -0
  35. package/dist/types/components/RichTextRenderer/ReferenceRenderer.d.ts +14 -0
  36. package/dist/types/components/RichTextRenderer/ReferenceRenderer.d.ts.map +1 -0
  37. package/dist/types/components/RichTextRenderer/RenderElementFallback.d.ts +12 -0
  38. package/dist/types/components/RichTextRenderer/RenderElementFallback.d.ts.map +1 -0
  39. package/dist/types/components/RichTextRenderer/RenderLeafFallback.d.ts +7 -0
  40. package/dist/types/components/RichTextRenderer/RenderLeafFallback.d.ts.map +1 -0
  41. package/dist/types/components/RichTextRenderer/RichTextBlock.d.ts +2 -0
  42. package/dist/types/components/RichTextRenderer/RichTextBlock.d.ts.map +1 -0
  43. package/dist/types/components/RichTextRenderer/RichTextElement.d.ts +7 -0
  44. package/dist/types/components/RichTextRenderer/RichTextElement.d.ts.map +1 -0
  45. package/dist/types/components/RichTextRenderer/RichTextElementMetadata.d.ts +11 -0
  46. package/dist/types/components/RichTextRenderer/RichTextElementMetadata.d.ts.map +1 -0
  47. package/dist/types/components/RichTextRenderer/RichTextLeaf.d.ts +4 -0
  48. package/dist/types/components/RichTextRenderer/RichTextLeaf.d.ts.map +1 -0
  49. package/dist/types/components/RichTextRenderer/RichTextLeafMetadata.d.ts +4 -0
  50. package/dist/types/components/RichTextRenderer/RichTextLeafMetadata.d.ts.map +1 -0
  51. package/dist/types/components/RichTextRenderer/RichTextReference.d.ts +4 -0
  52. package/dist/types/components/RichTextRenderer/RichTextReference.d.ts.map +1 -0
  53. package/dist/types/components/RichTextRenderer/RichTextRenderMetadata.d.ts +12 -0
  54. package/dist/types/components/RichTextRenderer/RichTextRenderMetadata.d.ts.map +1 -0
  55. package/dist/types/components/RichTextRenderer/RichTextRenderMetadataContext.d.ts +8 -0
  56. package/dist/types/components/RichTextRenderer/RichTextRenderMetadataContext.d.ts.map +1 -0
  57. package/dist/types/components/RichTextRenderer/RichTextRenderer.d.ts +29 -0
  58. package/dist/types/components/RichTextRenderer/RichTextRenderer.d.ts.map +1 -0
  59. package/dist/types/components/RichTextRenderer/RichTextRendererError.d.ts +3 -0
  60. package/dist/types/components/RichTextRenderer/RichTextRendererError.d.ts.map +1 -0
  61. package/dist/types/components/RichTextRenderer/RootEditorNode.d.ts +9 -0
  62. package/dist/types/components/RichTextRenderer/RootEditorNode.d.ts.map +1 -0
  63. package/dist/types/components/RichTextRenderer/defaultDeserialize.d.ts +5 -0
  64. package/dist/types/components/RichTextRenderer/defaultDeserialize.d.ts.map +1 -0
  65. package/dist/types/components/RichTextRenderer/index.d.ts +16 -0
  66. package/dist/types/components/RichTextRenderer/index.d.ts.map +1 -0
  67. package/dist/types/components/RichTextRenderer/renderChildren.d.ts +14 -0
  68. package/dist/types/components/RichTextRenderer/renderChildren.d.ts.map +1 -0
  69. package/dist/types/components/RichTextRenderer/resolveRichTextElementMetadata.d.ts +8 -0
  70. package/dist/types/components/RichTextRenderer/resolveRichTextElementMetadata.d.ts.map +1 -0
  71. package/dist/types/components/index.d.ts +2 -0
  72. package/dist/types/components/index.d.ts.map +1 -0
  73. package/dist/types/config/ApiBaseUrlContext.d.ts +3 -0
  74. package/dist/types/config/ApiBaseUrlContext.d.ts.map +1 -0
  75. package/dist/types/config/LoginTokenContext.d.ts +3 -0
  76. package/dist/types/config/LoginTokenContext.d.ts.map +1 -0
  77. package/dist/types/config/index.d.ts +5 -0
  78. package/dist/types/config/index.d.ts.map +1 -0
  79. package/dist/types/config/useApiBaseUrl.d.ts +2 -0
  80. package/dist/types/config/useApiBaseUrl.d.ts.map +1 -0
  81. package/dist/types/config/useLoginToken.d.ts +2 -0
  82. package/dist/types/config/useLoginToken.d.ts.map +1 -0
  83. package/dist/types/content/index.d.ts +4 -0
  84. package/dist/types/content/index.d.ts.map +1 -0
  85. package/dist/types/content/useContentApiRequest.d.ts +3 -0
  86. package/dist/types/content/useContentApiRequest.d.ts.map +1 -0
  87. package/dist/types/content/useContentGraphQlClient.d.ts +3 -0
  88. package/dist/types/content/useContentGraphQlClient.d.ts.map +1 -0
  89. package/dist/types/content/useCurrentContentGraphQlClient.d.ts +3 -0
  90. package/dist/types/content/useCurrentContentGraphQlClient.d.ts.map +1 -0
  91. package/dist/types/index.d.ts +14 -0
  92. package/dist/types/index.d.ts.map +1 -0
  93. package/dist/types/project/ProjectSlugContext.d.ts +3 -0
  94. package/dist/types/project/ProjectSlugContext.d.ts.map +1 -0
  95. package/dist/types/project/StageSlugContext.d.ts +3 -0
  96. package/dist/types/project/StageSlugContext.d.ts.map +1 -0
  97. package/dist/types/project/index.d.ts +5 -0
  98. package/dist/types/project/index.d.ts.map +1 -0
  99. package/dist/types/project/useProjectSlug.d.ts +2 -0
  100. package/dist/types/project/useProjectSlug.d.ts.map +1 -0
  101. package/dist/types/project/useStageSlug.d.ts +2 -0
  102. package/dist/types/project/useStageSlug.d.ts.map +1 -0
  103. package/dist/types/system/index.d.ts +4 -0
  104. package/dist/types/system/index.d.ts.map +1 -0
  105. package/dist/types/system/useCurrentSystemGraphQlClient.d.ts +3 -0
  106. package/dist/types/system/useCurrentSystemGraphQlClient.d.ts.map +1 -0
  107. package/dist/types/system/useSystemApiRequest.d.ts +3 -0
  108. package/dist/types/system/useSystemApiRequest.d.ts.map +1 -0
  109. package/dist/types/system/useSystemGraphQlClient.d.ts +2 -0
  110. package/dist/types/system/useSystemGraphQlClient.d.ts.map +1 -0
  111. package/dist/types/tenant/index.d.ts +3 -0
  112. package/dist/types/tenant/index.d.ts.map +1 -0
  113. package/dist/types/tenant/useTenantApiRequest.d.ts +3 -0
  114. package/dist/types/tenant/useTenantApiRequest.d.ts.map +1 -0
  115. package/dist/types/tenant/useTenantGraphQlClient.d.ts +2 -0
  116. package/dist/types/tenant/useTenantGraphQlClient.d.ts.map +1 -0
  117. package/dist/types/tsconfig.tsbuildinfo +1 -0
  118. package/dist/types/upload/FileId.d.ts +2 -0
  119. package/dist/types/upload/FileId.d.ts.map +1 -0
  120. package/dist/types/upload/FileUploadAction.d.ts +26 -0
  121. package/dist/types/upload/FileUploadAction.d.ts.map +1 -0
  122. package/dist/types/upload/FileUploadCompoundState.d.ts +4 -0
  123. package/dist/types/upload/FileUploadCompoundState.d.ts.map +1 -0
  124. package/dist/types/upload/FileUploadMetadata.d.ts +6 -0
  125. package/dist/types/upload/FileUploadMetadata.d.ts.map +1 -0
  126. package/dist/types/upload/FileUploadMultiTemporalState.d.ts +10 -0
  127. package/dist/types/upload/FileUploadMultiTemporalState.d.ts.map +1 -0
  128. package/dist/types/upload/FileUploadOperations.d.ts +18 -0
  129. package/dist/types/upload/FileUploadOperations.d.ts.map +1 -0
  130. package/dist/types/upload/FileWithMetadata.d.ts +8 -0
  131. package/dist/types/upload/FileWithMetadata.d.ts.map +1 -0
  132. package/dist/types/upload/SingleFileUploadState.d.ts +31 -0
  133. package/dist/types/upload/SingleFileUploadState.d.ts.map +1 -0
  134. package/dist/types/upload/fileUploadReducer.d.ts +5 -0
  135. package/dist/types/upload/fileUploadReducer.d.ts.map +1 -0
  136. package/dist/types/upload/index.d.ts +8 -0
  137. package/dist/types/upload/index.d.ts.map +1 -0
  138. package/dist/types/upload/toFileId.d.ts +4 -0
  139. package/dist/types/upload/toFileId.d.ts.map +1 -0
  140. package/dist/types/upload/useFileUpload.d.ts +11 -0
  141. package/dist/types/upload/useFileUpload.d.ts.map +1 -0
  142. package/dist/types/useGraphQlClient.d.ts +3 -0
  143. package/dist/types/useGraphQlClient.d.ts.map +1 -0
  144. package/dist/types/utils/assertNever.d.ts +2 -0
  145. package/dist/types/utils/assertNever.d.ts.map +1 -0
  146. package/dist/types/utils/index.d.ts +2 -0
  147. package/dist/types/utils/index.d.ts.map +1 -0
  148. package/package.json +6 -7
  149. package/src/ClientError.ts +1 -0
  150. package/src/ContemberClient.tsx +48 -0
  151. package/src/ambient.d.ts +1 -0
  152. package/src/apiRequest/ApiRequestAction.ts +17 -0
  153. package/src/apiRequest/ApiRequestState.ts +25 -0
  154. package/src/apiRequest/apiRequestReducer.ts +41 -0
  155. package/src/apiRequest/index.ts +4 -0
  156. package/src/apiRequest/useApiRequest.ts +73 -0
  157. package/src/auth/SessionTokenContext.ts +3 -0
  158. package/src/auth/SetSessionTokenContext.ts +5 -0
  159. package/src/auth/index.ts +4 -0
  160. package/src/auth/useSessionToken.ts +4 -0
  161. package/src/auth/useSetSessionToken.ts +4 -0
  162. package/src/components/RichTextRenderer/BuiltinElements.ts +112 -0
  163. package/src/components/RichTextRenderer/BuiltinLeaves.ts +32 -0
  164. package/src/components/RichTextRenderer/ElementRenderer.tsx +71 -0
  165. package/src/components/RichTextRenderer/LeafRenderer.tsx +39 -0
  166. package/src/components/RichTextRenderer/NormalizedRichTextBlock.ts +17 -0
  167. package/src/components/RichTextRenderer/ReferenceRenderer.ts +26 -0
  168. package/src/components/RichTextRenderer/RenderElementFallback.tsx +147 -0
  169. package/src/components/RichTextRenderer/RenderLeafFallback.tsx +43 -0
  170. package/src/components/RichTextRenderer/RichTextBlock.ts +1 -0
  171. package/src/components/RichTextRenderer/RichTextElement.ts +10 -0
  172. package/src/components/RichTextRenderer/RichTextElementMetadata.ts +15 -0
  173. package/src/components/RichTextRenderer/RichTextLeaf.ts +3 -0
  174. package/src/components/RichTextRenderer/RichTextLeafMetadata.ts +3 -0
  175. package/src/components/RichTextRenderer/RichTextReference.ts +3 -0
  176. package/src/components/RichTextRenderer/RichTextRenderMetadata.ts +16 -0
  177. package/src/components/RichTextRenderer/RichTextRenderMetadataContext.ts +20 -0
  178. package/src/components/RichTextRenderer/RichTextRenderer.tsx +165 -0
  179. package/src/components/RichTextRenderer/RichTextRendererError.ts +1 -0
  180. package/src/components/RichTextRenderer/RootEditorNode.ts +12 -0
  181. package/src/components/RichTextRenderer/defaultDeserialize.ts +19 -0
  182. package/src/components/RichTextRenderer/index.ts +15 -0
  183. package/src/components/RichTextRenderer/renderChildren.tsx +50 -0
  184. package/src/components/RichTextRenderer/resolveRichTextElementMetadata.ts +50 -0
  185. package/src/components/index.ts +1 -0
  186. package/src/config/ApiBaseUrlContext.ts +3 -0
  187. package/src/config/LoginTokenContext.ts +3 -0
  188. package/src/config/index.ts +4 -0
  189. package/src/config/useApiBaseUrl.ts +4 -0
  190. package/src/config/useLoginToken.ts +4 -0
  191. package/src/content/index.ts +3 -0
  192. package/src/content/useContentApiRequest.ts +6 -0
  193. package/src/content/useContentGraphQlClient.ts +6 -0
  194. package/src/content/useCurrentContentGraphQlClient.ts +17 -0
  195. package/src/index.ts +15 -0
  196. package/src/project/ProjectSlugContext.ts +3 -0
  197. package/src/project/StageSlugContext.ts +3 -0
  198. package/src/project/index.ts +4 -0
  199. package/src/project/useProjectSlug.ts +4 -0
  200. package/src/project/useStageSlug.ts +4 -0
  201. package/src/system/index.ts +3 -0
  202. package/src/system/useCurrentSystemGraphQlClient.ts +13 -0
  203. package/src/system/useSystemApiRequest.ts +6 -0
  204. package/src/system/useSystemGraphQlClient.ts +4 -0
  205. package/src/tenant/index.ts +2 -0
  206. package/src/tenant/useTenantApiRequest.ts +6 -0
  207. package/src/tenant/useTenantGraphQlClient.ts +4 -0
  208. package/src/tsconfig.json +9 -0
  209. package/src/upload/FileId.ts +1 -0
  210. package/src/upload/FileUploadAction.ts +33 -0
  211. package/src/upload/FileUploadCompoundState.ts +7 -0
  212. package/src/upload/FileUploadMetadata.ts +6 -0
  213. package/src/upload/FileUploadMultiTemporalState.ts +10 -0
  214. package/src/upload/FileUploadOperations.ts +22 -0
  215. package/src/upload/FileWithMetadata.ts +8 -0
  216. package/src/upload/SingleFileUploadState.ts +35 -0
  217. package/src/upload/fileUploadReducer.ts +185 -0
  218. package/src/upload/index.ts +15 -0
  219. package/src/upload/toFileId.ts +12 -0
  220. package/src/upload/useFileUpload.ts +262 -0
  221. package/src/useGraphQlClient.ts +10 -0
  222. package/src/utils/assertNever.ts +3 -0
  223. package/src/utils/index.ts +1 -0
  224. package/dist/react-client.d.ts +0 -367
@@ -0,0 +1,4 @@
1
+ import { useContext } from 'react'
2
+ import { LoginTokenContext } from './LoginTokenContext'
3
+
4
+ export const useLoginToken = () => useContext(LoginTokenContext)
@@ -0,0 +1,3 @@
1
+ export * from './useContentApiRequest'
2
+ export * from './useContentGraphQlClient'
3
+ export * from './useCurrentContentGraphQlClient'
@@ -0,0 +1,6 @@
1
+ import type { UseApiRequestResult } from '../apiRequest'
2
+ import { useApiRequest } from '../apiRequest'
3
+ import { useCurrentContentGraphQlClient } from './useCurrentContentGraphQlClient'
4
+
5
+ export const useContentApiRequest = <SuccessData>(): UseApiRequestResult<SuccessData> =>
6
+ useApiRequest<SuccessData>(useCurrentContentGraphQlClient())
@@ -0,0 +1,6 @@
1
+ import type { GraphQlClient } from '@contember/client'
2
+ import { formatContentApiRelativeUrl } from '@contember/client'
3
+ import { useGraphQlClient } from '../useGraphQlClient'
4
+
5
+ export const useContentGraphQlClient = (projectSlug: string, stageSlug: string): GraphQlClient =>
6
+ useGraphQlClient(formatContentApiRelativeUrl(projectSlug, stageSlug))
@@ -0,0 +1,17 @@
1
+ import type { GraphQlClient } from '@contember/client'
2
+ import { ClientError } from '../ClientError'
3
+ import { useProjectSlug, useStageSlug } from '../project'
4
+ import { useContentGraphQlClient } from './useContentGraphQlClient'
5
+
6
+ export const useCurrentContentGraphQlClient = (): GraphQlClient => {
7
+ const projectSlug = useProjectSlug()
8
+ const stageSlug = useStageSlug()
9
+
10
+ if (projectSlug === undefined) {
11
+ throw new ClientError(`Cannot contact the content API: undefined project slug.`)
12
+ }
13
+ if (stageSlug === undefined) {
14
+ throw new ClientError(`Cannot contact the content API: undefined stage slug.`)
15
+ }
16
+ return useContentGraphQlClient(projectSlug, stageSlug)
17
+ }
package/src/index.ts ADDED
@@ -0,0 +1,15 @@
1
+ export * from './apiRequest'
2
+ export * from './auth'
3
+ export * from './components'
4
+ export * from './config'
5
+ export * from './content'
6
+ export * from './project'
7
+ export * from './system'
8
+ export * from './tenant'
9
+ export * from './upload'
10
+
11
+ export * from './ContemberClient'
12
+ export * from './ClientError'
13
+ export * from './useGraphQlClient'
14
+
15
+ export * from '@contember/client'
@@ -0,0 +1,3 @@
1
+ import { createContext } from 'react'
2
+
3
+ export const ProjectSlugContext = createContext<string | undefined>(undefined)
@@ -0,0 +1,3 @@
1
+ import { createContext } from 'react'
2
+
3
+ export const StageSlugContext = createContext<string | undefined>(undefined)
@@ -0,0 +1,4 @@
1
+ export * from './ProjectSlugContext'
2
+ export * from './StageSlugContext'
3
+ export * from './useProjectSlug'
4
+ export * from './useStageSlug'
@@ -0,0 +1,4 @@
1
+ import { useContext } from 'react'
2
+ import { ProjectSlugContext } from './ProjectSlugContext'
3
+
4
+ export const useProjectSlug = () => useContext(ProjectSlugContext)
@@ -0,0 +1,4 @@
1
+ import { useContext } from 'react'
2
+ import { StageSlugContext } from './StageSlugContext'
3
+
4
+ export const useStageSlug = () => useContext(StageSlugContext)
@@ -0,0 +1,3 @@
1
+ export * from './useCurrentSystemGraphQlClient'
2
+ export * from './useSystemApiRequest'
3
+ export * from './useSystemGraphQlClient'
@@ -0,0 +1,13 @@
1
+ import type { GraphQlClient } from '@contember/client'
2
+ import { ClientError } from '../ClientError'
3
+ import { useProjectSlug } from '../project'
4
+ import { useSystemGraphQlClient } from './useSystemGraphQlClient'
5
+
6
+ export const useCurrentSystemGraphQlClient = (): GraphQlClient => {
7
+ const projectSlug = useProjectSlug()
8
+
9
+ if (projectSlug === undefined) {
10
+ throw new ClientError(`Cannot contact the system API: undefined project slug.`)
11
+ }
12
+ return useSystemGraphQlClient(projectSlug)
13
+ }
@@ -0,0 +1,6 @@
1
+ import type { UseApiRequestResult } from '../apiRequest'
2
+ import { useApiRequest } from '../apiRequest'
3
+ import { useCurrentSystemGraphQlClient } from './useCurrentSystemGraphQlClient'
4
+
5
+ export const useSystemApiRequest = <SuccessData>(): UseApiRequestResult<SuccessData> =>
6
+ useApiRequest<SuccessData>(useCurrentSystemGraphQlClient())
@@ -0,0 +1,4 @@
1
+ import { formatSystemApiRelativeUrl } from '@contember/client'
2
+ import { useGraphQlClient } from '../useGraphQlClient'
3
+
4
+ export const useSystemGraphQlClient = (projectSlug: string) => useGraphQlClient(formatSystemApiRelativeUrl(projectSlug))
@@ -0,0 +1,2 @@
1
+ export * from './useTenantApiRequest'
2
+ export * from './useTenantGraphQlClient'
@@ -0,0 +1,6 @@
1
+ import type { UseApiRequestResult } from '../apiRequest'
2
+ import { useApiRequest } from '../apiRequest'
3
+ import { useTenantGraphQlClient } from './useTenantGraphQlClient'
4
+
5
+ export const useTenantApiRequest = <SuccessData>(): UseApiRequestResult<SuccessData> =>
6
+ useApiRequest<SuccessData>(useTenantGraphQlClient())
@@ -0,0 +1,4 @@
1
+ import { tenantApiRelativeUrl } from '@contember/client'
2
+ import { useGraphQlClient } from '../useGraphQlClient'
3
+
4
+ export const useTenantGraphQlClient = () => useGraphQlClient(tenantApiRelativeUrl)
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "../tsconfig.settings.json",
3
+ "compilerOptions": {
4
+ "outDir": "../dist/types"
5
+ },
6
+ "references": [
7
+ { "path": "../../client/src" }
8
+ ]
9
+ }
@@ -0,0 +1 @@
1
+ export type FileId = string | number
@@ -0,0 +1,33 @@
1
+ import type { FileUploadProgress } from '@contember/client'
2
+ import type { FileId } from './FileId'
3
+ import type { FileUploadMetadata } from './FileUploadMetadata'
4
+ import type { FileWithMetadata } from './FileWithMetadata'
5
+
6
+ export type FileUploadAction<Result = unknown, Metadata = undefined> =
7
+ | {
8
+ type: 'publishNewestState'
9
+ }
10
+ | {
11
+ type: 'initialize'
12
+ files: Iterable<[FileId, FileWithMetadata]>
13
+ }
14
+ | {
15
+ type: 'startUploading'
16
+ files: Iterable<[File | FileId, FileUploadMetadata<Metadata>]>
17
+ }
18
+ | {
19
+ type: 'updateUploadProgress'
20
+ progress: Iterable<[File | FileId, FileUploadProgress]>
21
+ }
22
+ | {
23
+ type: 'finishSuccessfully'
24
+ result: Iterable<[File | FileId, Result]>
25
+ }
26
+ | {
27
+ type: 'finishWithError'
28
+ error: Iterable<(File | FileId) | [File | FileId, any]>
29
+ }
30
+ | {
31
+ type: 'purge'
32
+ files: Iterable<File | FileId>
33
+ }
@@ -0,0 +1,7 @@
1
+ import type { FileId } from './FileId'
2
+ import type { SingleFileUploadState } from './SingleFileUploadState'
3
+
4
+ export type FileUploadCompoundState<Result = unknown, Metadata = undefined> = Map<
5
+ FileId,
6
+ SingleFileUploadState<Result, Metadata>
7
+ >
@@ -0,0 +1,6 @@
1
+ import type { FileUploader } from '@contember/client'
2
+
3
+ export interface FileUploadMetadata<Metadata = undefined> {
4
+ uploader: FileUploader
5
+ metadata: Metadata | undefined
6
+ }
@@ -0,0 +1,10 @@
1
+ import type { FileId } from './FileId'
2
+ import type { FileUploadCompoundState } from './FileUploadCompoundState'
3
+
4
+ export interface FileUploadMultiTemporalState<Result = unknown, Metadata = undefined> {
5
+ lastUpdateTime: number
6
+ isLiveStateDirty: boolean
7
+ fileIdByFile: WeakMap<File, FileId>
8
+ publicState: FileUploadCompoundState<Result, Metadata>
9
+ liveState: FileUploadCompoundState<Result, Metadata>
10
+ }
@@ -0,0 +1,22 @@
1
+ import type { FileUploader } from '@contember/client'
2
+ import type { FileId } from './FileId'
3
+ import type { FileWithMetadata } from './FileWithMetadata'
4
+
5
+ export interface StartUploadFileOptions<Metadata = undefined> {
6
+ metadata?: Metadata
7
+ uploader?: FileUploader
8
+ }
9
+
10
+ export type InitializeUpload = (files: Iterable<[FileId, File] | File>) => Map<FileId, FileWithMetadata>
11
+ export type StartUpload<Metadata = undefined> = (
12
+ files: Iterable<FileId | File | [FileId | File, StartUploadFileOptions<Metadata>]>,
13
+ ) => void
14
+ export type PurgeUpload = (files: Iterable<FileId | File>) => void
15
+ export type FailUpload = (files: Iterable<FileId | File | [FileId | File, any]>) => void
16
+
17
+ export interface FileUploadOperations<Metadata = undefined> {
18
+ initializeUpload: InitializeUpload
19
+ startUpload: StartUpload<Metadata>
20
+ purgeUpload: PurgeUpload
21
+ failUpload: FailUpload
22
+ }
@@ -0,0 +1,8 @@
1
+ import type { FileId } from './FileId'
2
+
3
+ export interface FileWithMetadata {
4
+ file: File
5
+ fileId: FileId
6
+ abortController: AbortController
7
+ previewUrl: string
8
+ }
@@ -0,0 +1,35 @@
1
+ import type { FileUploader, FileUploadError } from '@contember/client'
2
+
3
+ export type SingleFileUploadState<Result = unknown, Metadata = undefined> =
4
+ | {
5
+ readyState: 'initializing'
6
+ abortController: AbortController
7
+ file: File
8
+ previewUrl: string
9
+ }
10
+ | {
11
+ readyState: 'uploading'
12
+ abortController: AbortController
13
+ file: File
14
+ metadata: Metadata
15
+ previewUrl: string
16
+ progress: number | undefined
17
+ uploader: FileUploader
18
+ }
19
+ | {
20
+ readyState: 'success'
21
+ file: File
22
+ metadata: Metadata
23
+ previewUrl: string
24
+ result: Result
25
+ uploader: FileUploader
26
+ }
27
+ | {
28
+ readyState: 'error'
29
+ errors: FileUploadError[] | undefined
30
+ rawError: any
31
+ file: File
32
+ metadata: Metadata | undefined
33
+ previewUrl: string
34
+ uploader: FileUploader | undefined
35
+ }
@@ -0,0 +1,185 @@
1
+ import { FileUploadError } from '@contember/client'
2
+ import { assertNever } from '../utils'
3
+ import type { FileId } from './FileId'
4
+ import type { FileUploadAction } from './FileUploadAction'
5
+ import type { FileUploadMultiTemporalState } from './FileUploadMultiTemporalState'
6
+ import { toFileId } from './toFileId'
7
+
8
+ export const initializeFileUploadState = <Result = unknown, Metadata = undefined>(): FileUploadMultiTemporalState<
9
+ Result,
10
+ Metadata
11
+ > => ({
12
+ lastUpdateTime: 0,
13
+ fileIdByFile: new WeakMap<File, FileId>(),
14
+ liveState: new Map(),
15
+ publicState: new Map(),
16
+ isLiveStateDirty: false,
17
+ })
18
+
19
+ export const fileUploadReducer = <Result = unknown, Metadata = undefined>(
20
+ previousState: FileUploadMultiTemporalState<Result, Metadata>,
21
+ action: FileUploadAction<Result, Metadata>,
22
+ ): FileUploadMultiTemporalState<Result, Metadata> => {
23
+ const publishNewState = (): FileUploadMultiTemporalState<Result, Metadata> => ({
24
+ publicState: previousState.liveState,
25
+ fileIdByFile: previousState.fileIdByFile,
26
+ lastUpdateTime: previousState.lastUpdateTime,
27
+ isLiveStateDirty: false,
28
+
29
+ // Immediately make the live state referentially unequal so that this doesn't have to happen during each action
30
+ // several times between two publishes.
31
+ liveState: new Map(previousState.liveState),
32
+ })
33
+ const getNewDirtyState = (): FileUploadMultiTemporalState<Result, Metadata> => ({
34
+ fileIdByFile: previousState.fileIdByFile,
35
+ liveState: previousState.liveState,
36
+ publicState: previousState.publicState,
37
+ isLiveStateDirty: true,
38
+ lastUpdateTime: Date.now(),
39
+ })
40
+ switch (action.type) {
41
+ case 'publishNewestState': {
42
+ return publishNewState()
43
+ }
44
+ case 'initialize': {
45
+ for (const [fileId, metadata] of action.files) {
46
+ // Deliberately allowing starting a new upload with the same id
47
+
48
+ previousState.liveState.set(fileId, {
49
+ readyState: 'initializing',
50
+ abortController: metadata.abortController,
51
+ previewUrl: metadata.previewUrl,
52
+ file: metadata.file,
53
+ })
54
+ previousState.fileIdByFile.set(metadata.file, fileId)
55
+ }
56
+ return publishNewState() /* Making the feedback about new files immediate*/
57
+ }
58
+ case 'startUploading': {
59
+ for (const [fileOrId, metadata] of action.files) {
60
+ const fileId = toFileId(previousState, fileOrId)
61
+ const fileState = previousState.liveState.get(fileId)
62
+
63
+ if (fileState === undefined || fileState.readyState !== 'initializing') {
64
+ continue
65
+ }
66
+
67
+ previousState.liveState.set(fileId, {
68
+ readyState: 'uploading',
69
+ abortController: fileState.abortController,
70
+ file: fileState.file,
71
+ metadata: metadata.metadata!, // If the user didn't supply the metadata, then that's on them.
72
+ previewUrl: fileState.previewUrl,
73
+ progress: undefined,
74
+ uploader: metadata.uploader,
75
+ })
76
+ }
77
+ return publishNewState() /* Making the feedback about started upload immediate*/
78
+ }
79
+ case 'finishSuccessfully': {
80
+ for (const [fileOrId, result] of action.result) {
81
+ const fileId = toFileId(previousState, fileOrId)
82
+ const previousFileState = previousState.liveState.get(fileId)
83
+ if (previousFileState === undefined || previousFileState.readyState !== 'uploading') {
84
+ continue
85
+ }
86
+ previousState.liveState.set(fileId, {
87
+ readyState: 'success',
88
+ file: previousFileState.file,
89
+ metadata: previousFileState.metadata,
90
+ previewUrl: previousFileState.previewUrl,
91
+ result,
92
+ uploader: previousFileState.uploader,
93
+ })
94
+ }
95
+ // return publishNewState() // Making the feedback about successful upload immediate.
96
+ return getNewDirtyState()
97
+ }
98
+ case 'finishWithError': {
99
+ for (const errorSpec of action.error) {
100
+ let fileOrId: File | FileId
101
+ let rawError: unknown
102
+
103
+ if (Array.isArray(errorSpec)) {
104
+ [fileOrId, rawError] = errorSpec
105
+ } else {
106
+ fileOrId = errorSpec
107
+ rawError = undefined
108
+ }
109
+
110
+ const errors: FileUploadError[] = []
111
+
112
+ if (rawError instanceof FileUploadError) {
113
+ errors.push(rawError)
114
+ } else if (Array.isArray(rawError)) {
115
+ for (const error of rawError) {
116
+ if (error instanceof FileUploadError) {
117
+ errors.push(error)
118
+ }
119
+ }
120
+ }
121
+
122
+ const fileId = toFileId(previousState, fileOrId)
123
+ const previousFileState = previousState.liveState.get(fileId)
124
+ if (
125
+ previousFileState === undefined ||
126
+ (previousFileState.readyState !== 'initializing' && previousFileState.readyState !== 'uploading')
127
+ ) {
128
+ continue
129
+ }
130
+ const uploader = previousFileState.readyState === 'uploading' ? previousFileState.uploader : undefined
131
+ const metadata = previousFileState.readyState === 'uploading' ? previousFileState.metadata : undefined
132
+ previousState.liveState.set(fileId, {
133
+ readyState: 'error',
134
+ errors: errors.length ? errors : undefined,
135
+ rawError,
136
+ file: previousFileState.file,
137
+ metadata,
138
+ previewUrl: previousFileState.previewUrl,
139
+ uploader,
140
+ })
141
+ }
142
+ return getNewDirtyState() // Bad news can wait.
143
+ }
144
+ case 'purge': {
145
+ let atLeastOnePurged = false
146
+ for (const fileOrId of action.files) {
147
+ const fileId = toFileId(previousState, fileOrId)
148
+ const fileState = previousState.liveState.get(fileId)
149
+
150
+ if (fileState === undefined) {
151
+ continue
152
+ }
153
+ atLeastOnePurged = true
154
+ if (fileState.readyState === 'initializing' || fileState.readyState === 'uploading') {
155
+ fileState.abortController.abort() // This is a bit naughty… We shouldn't do this from here.
156
+ }
157
+ previousState.liveState.delete(fileId)
158
+ }
159
+ if (atLeastOnePurged) {
160
+ return getNewDirtyState()
161
+ }
162
+ return previousState
163
+ }
164
+ case 'updateUploadProgress': {
165
+ for (const [fileOrId, { progress }] of action.progress) {
166
+ const fileId = toFileId(previousState, fileOrId)
167
+ const previousFileState = previousState.liveState.get(fileId)
168
+ if (progress === undefined || previousFileState === undefined || previousFileState.readyState !== 'uploading') {
169
+ continue
170
+ }
171
+ previousState.liveState.set(fileId, {
172
+ readyState: 'uploading',
173
+ file: previousFileState.file,
174
+ uploader: previousFileState.uploader,
175
+ abortController: previousFileState.abortController,
176
+ previewUrl: previousFileState.previewUrl,
177
+ metadata: previousFileState.metadata,
178
+ progress,
179
+ })
180
+ }
181
+ return getNewDirtyState()
182
+ }
183
+ }
184
+ assertNever(action)
185
+ }
@@ -0,0 +1,15 @@
1
+ export * from './useFileUpload'
2
+
3
+ export type { FileId } from './FileId'
4
+ export type { FileUploadCompoundState } from './FileUploadCompoundState'
5
+ export type { FileUploadMetadata } from './FileUploadMetadata'
6
+ export type {
7
+ StartUploadFileOptions,
8
+ StartUpload,
9
+ FileUploadOperations,
10
+ InitializeUpload,
11
+ PurgeUpload,
12
+ FailUpload,
13
+ } from './FileUploadOperations'
14
+ export type { FileWithMetadata } from './FileWithMetadata'
15
+ export type { SingleFileUploadState } from './SingleFileUploadState'
@@ -0,0 +1,12 @@
1
+ import type { FileId } from './FileId'
2
+ import type { FileUploadMultiTemporalState } from './FileUploadMultiTemporalState'
3
+
4
+ export const toFileId = <Result = unknown, Metadata = undefined>(
5
+ state: FileUploadMultiTemporalState<Result, Metadata>,
6
+ fileOrId: File | FileId,
7
+ ) => {
8
+ if (fileOrId instanceof File) {
9
+ return state.fileIdByFile.get(fileOrId)! // Should we throw instead of the yolo "!"?
10
+ }
11
+ return fileOrId
12
+ }