@ossy/resources 1.12.2 → 3.0.1

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 (89) hide show
  1. package/package.json +16 -4
  2. package/src/AudioResource.jsx +2 -2
  3. package/src/CreateDirectory.jsx +2 -10
  4. package/src/CreateDirectory.page.jsx +1 -1
  5. package/src/CreateDocument.jsx +2 -2
  6. package/src/CreateDocument.page.jsx +1 -1
  7. package/src/Definition.js +3 -2
  8. package/src/DocumentEdit.jsx +10 -38
  9. package/src/DocumentView.jsx +11 -22
  10. package/src/GenericResourceCard.jsx +25 -7
  11. package/src/GenericResourceDetail.jsx +6 -60
  12. package/src/GenericResourceForm.jsx +171 -37
  13. package/src/ImageResource.jsx +1 -1
  14. package/src/PDFResource.jsx +1 -1
  15. package/src/PlatformFileField.jsx +239 -0
  16. package/src/ResourceContentPage.jsx +5 -5
  17. package/src/ResourceDescription.jsx +1 -1
  18. package/src/ResourceDetails.jsx +3 -4
  19. package/src/ResourceDialogMove.jsx +3 -3
  20. package/src/ResourceFactory.jsx +11 -64
  21. package/src/ResourceGenericView.jsx +1 -1
  22. package/src/ResourceList.jsx +89 -106
  23. package/src/ResourcePanel.jsx +40 -57
  24. package/src/ResourceTags.jsx +1 -1
  25. package/src/ResourcesPage.jsx +26 -35
  26. package/src/SchemaDetailView.jsx +97 -0
  27. package/src/SchemaFormDefault.jsx +86 -0
  28. package/src/SchemaPresenter.jsx +207 -0
  29. package/src/Upload.jsx +6 -25
  30. package/src/Upload.page.jsx +1 -1
  31. package/src/UploadResources.jsx +2 -2
  32. package/src/VideoResource.jsx +1 -1
  33. package/src/access-filter.spec.js +1 -1
  34. package/src/create-page-view.action.js +1 -1
  35. package/src/create-page-view.task.js +10 -9
  36. package/src/create.action.js +1 -1
  37. package/src/create.task.js +27 -29
  38. package/src/delete.action.js +1 -1
  39. package/src/delete.task.js +9 -13
  40. package/src/en.translations.json +25 -24
  41. package/src/get-page-view-stats.action.js +1 -1
  42. package/src/get-page-view-stats.task.js +3 -4
  43. package/src/get-resource-alias.api.js +8 -0
  44. package/src/get-resource-info-alias.api.js +8 -0
  45. package/src/get-resource-info.api.js +36 -0
  46. package/src/get-resource-variant.api.js +8 -0
  47. package/src/get-resource.api.js +51 -0
  48. package/src/get.action.js +1 -1
  49. package/src/get.task.js +6 -6
  50. package/src/index.js +10 -0
  51. package/src/list.action.js +1 -1
  52. package/src/list.task.js +1 -1
  53. package/src/platform-file-field.component.jsx +5 -0
  54. package/src/platform-image-field.component.jsx +5 -0
  55. package/src/resource-create.page.jsx +5 -5
  56. package/src/resource-detail.page.jsx +1 -1
  57. package/src/resource-read.helpers.js +151 -0
  58. package/src/resource-stream.helpers.js +20 -0
  59. package/src/resource-stream.js +108 -0
  60. package/src/resource.helpers.js +9 -9
  61. package/src/resources.attach-media-urls.js +9 -3
  62. package/src/resources.events.js +42 -81
  63. package/src/{resources.spec.js → resources.integration.spec.js} +19 -19
  64. package/src/resources.queries.js +5 -2
  65. package/src/schema-detail.component.jsx +24 -0
  66. package/src/schema-form-default.component.jsx +14 -0
  67. package/src/schemaFormSlots.js +6 -0
  68. package/src/schemaViewSlots.js +6 -0
  69. package/src/search.action.js +1 -1
  70. package/src/search.task.js +1 -1
  71. package/src/server.js +2 -1
  72. package/src/sv.translations.json +25 -24
  73. package/src/update-access.action.js +1 -1
  74. package/src/update-access.task.js +9 -8
  75. package/src/update-content.action.js +1 -1
  76. package/src/update-content.task.js +25 -27
  77. package/src/update-location.action.js +1 -1
  78. package/src/update-location.task.js +9 -8
  79. package/src/update-name.action.js +1 -1
  80. package/src/update-name.task.js +9 -8
  81. package/src/upload-named-version.action.js +1 -1
  82. package/src/upload-named-version.task.js +16 -22
  83. package/src/useActivePath.jsx +3 -8
  84. package/src/useDocumentValidator.js +16 -0
  85. package/src/useSchemaEngine.js +9 -0
  86. package/src/useSchemas.js +29 -0
  87. package/src/useSelectedResourceId.jsx +36 -0
  88. package/src/ResourceContentPage.stories.jsx +0 -19
  89. package/src/resource.aggregate.js +0 -86
@@ -1,17 +1,14 @@
1
- import { Aggregate } from '@ossy/event-store'
2
- import { Resource } from './resource.aggregate.js'
3
1
  import { ResourcesEvents } from './resources.events.js'
4
- import { Workspace } from '@ossy/workspaces/server'
2
+ import { mutateResource } from './resource-stream.helpers.js'
3
+ import { resolveResourceId } from './resource-read.helpers.js'
4
+ import { derivativeObjectKey } from '@ossy/platform/storage-keys'
5
5
  import { mediaContextFromRun, withResourceMedia } from './resources.action-media.js'
6
6
 
7
- export const metadata = { id: 'resources/upload-named-version' }
7
+ export const metadata = { id: '@ossy/resources/tasks/upload-named-version' }
8
8
 
9
- // TODO: remove named version concept
10
-
11
- export async function run({ payload, integrations, req }) {
9
+ export async function run ({ payload, integrations, req }) {
12
10
  const createdBy = payload?.userId ?? req?.userId
13
- const workspaceId = payload?.workspaceId ?? req?.workspaceId
14
- const resourceId = payload?.resourceId ?? req?.params?.resourceId
11
+ const resourceId = resolveResourceId({ payload, req })
15
12
  const namedVersion = payload?.namedVersion ?? req?.params?.namedVersion
16
13
  const type = payload?.type
17
14
  const size = payload?.size
@@ -19,26 +16,23 @@ export async function run({ payload, integrations, req }) {
19
16
  if (!resourceId) throw Object.assign(new Error('resourceId is required'), { status: 400 })
20
17
  if (!namedVersion) throw Object.assign(new Error('namedVersion is required'), { status: 400 })
21
18
 
22
- const workspace = await Aggregate.Of(Workspace, workspaceId).then(Aggregate.View())
23
-
24
- const event = ResourcesEvents.NamedVersionUploaded({
19
+ const event = ResourcesEvents.Patched({
25
20
  createdBy,
26
- namedVersion,
27
- Key: `media/${workspace.id}/${resourceId}/${namedVersion}.${type.replace('image/', '')}`,
28
- ContentLength: size,
29
- ContentType: type,
21
+ content: {
22
+ sizes: { [namedVersion]: derivativeObjectKey(resourceId, namedVersion) },
23
+ ContentLength: size,
24
+ ContentType: type,
25
+ },
30
26
  })
31
27
 
32
28
  const storageClient = integrations?.get?.('storage')
33
29
  const uploadHeaders = {
34
- ContentLength: event.payload.ContentLength,
35
- ContentType: event.payload.ContentType,
36
- Key: event.payload.Key,
30
+ ContentLength: size,
31
+ ContentType: type,
32
+ Key: derivativeObjectKey(resourceId, namedVersion),
37
33
  }
38
34
  const uploadUrl = storageClient ? await storageClient.createUploadUrl(uploadHeaders) : null
39
- const resource = await Aggregate.Of(Resource, resourceId)
40
- .then(Aggregate.Add(event))
41
- .then(Aggregate.View())
35
+ const resource = await mutateResource(resourceId, event)
42
36
  const withUpload = uploadUrl
43
37
  ? { ...resource, content: { ...resource.content, uploadUrl } }
44
38
  : resource
@@ -4,20 +4,15 @@ import {
4
4
  o,
5
5
  endsWith
6
6
  } from 'ramda'
7
- import { useRouter } from '@ossy/router-react'
8
- import React, { useEffect } from 'react'
7
+ import { useSearchParams } from '@ossy/router-react'
9
8
 
10
9
  const prependSlash = unless(startsWith('/'), path => `/${path}`)
11
10
  const appendSlash = unless(endsWith('/'), path => `${path}/`)
12
11
  const addSlashes = o(prependSlash, appendSlash)
13
12
 
14
13
  export const useActivePath = () => {
15
- const router = useRouter()
16
- const activePath = addSlashes(router.searchParams.location || '/')
17
-
18
- // useEffect(() => {
19
- // console.log('useActivePath', activePath)
20
- // }, [activePath])
14
+ const searchParams = useSearchParams()
15
+ const activePath = addSlashes(searchParams.location || '/')
21
16
 
22
17
  return activePath
23
18
  }
@@ -0,0 +1,16 @@
1
+ import { useMemo } from 'react'
2
+ import { contentValidator } from '@ossy/schema'
3
+ import { useSchemaEngine } from './useSchemaEngine.js'
4
+
5
+ /**
6
+ * Returns a Form-compatible validator for a document schema (merged system + workspace).
7
+ *
8
+ * @param {string | object | undefined} schema
9
+ */
10
+ export function useDocumentValidator (schema) {
11
+ const engine = useSchemaEngine()
12
+ return useMemo(() => {
13
+ if (!schema) return undefined
14
+ return contentValidator(engine, schema)
15
+ }, [engine, schema])
16
+ }
@@ -0,0 +1,9 @@
1
+ import { useMemo } from 'react'
2
+ import { Schema } from '@ossy/schema'
3
+ import { useSchemas } from './useSchemas.js'
4
+
5
+ /** Merged system + workspace schema engine for client-side validation. */
6
+ export function useSchemaEngine () {
7
+ const schemas = useSchemas()
8
+ return useMemo(() => Schema.of({ schemas }), [schemas])
9
+ }
@@ -0,0 +1,29 @@
1
+ import { useMemo } from 'react'
2
+ import { GetSchemas } from '@ossy/workspaces'
3
+ import { useSdk } from '@ossy/sdk-react'
4
+
5
+ /** Merged system + workspace schemas (see workspaces/actions/get-schemas). */
6
+ export function useSchemas () {
7
+ const sdk = useSdk()
8
+ const { data: schemas = [] } = sdk.read(GetSchemas)
9
+ return schemas
10
+ }
11
+
12
+ export function useSchemasById () {
13
+ const schemas = useSchemas()
14
+ return useMemo(
15
+ () => schemas.reduce((acc, schema) => {
16
+ acc[schema.id] = schema
17
+ return acc
18
+ }, {}),
19
+ [schemas],
20
+ )
21
+ }
22
+
23
+ export function useSchema (schemaId) {
24
+ const schemas = useSchemas()
25
+ return useMemo(
26
+ () => schemas.find(s => s.id === schemaId),
27
+ [schemas, schemaId],
28
+ )
29
+ }
@@ -0,0 +1,36 @@
1
+ import { useCallback, useMemo } from 'react'
2
+ import { useSearchParams } from '@ossy/router-react'
3
+
4
+ export const RESOURCE_QUERY_PARAM = 'r'
5
+ export const RESOURCE_QUERY_PARAM_ALT = 'resourceId'
6
+
7
+ export const useSelectedResourceId = () => {
8
+ const searchParams = useSearchParams()
9
+
10
+ const selectedResourceId =
11
+ searchParams[RESOURCE_QUERY_PARAM] ||
12
+ searchParams[RESOURCE_QUERY_PARAM_ALT] ||
13
+ undefined
14
+
15
+ const setSelectedResourceId = useCallback((resourceId) => {
16
+ if (resourceId) {
17
+ searchParams.set(RESOURCE_QUERY_PARAM, resourceId)
18
+ searchParams.delete(RESOURCE_QUERY_PARAM_ALT)
19
+ return
20
+ }
21
+
22
+ searchParams.delete(RESOURCE_QUERY_PARAM)
23
+ searchParams.delete(RESOURCE_QUERY_PARAM_ALT)
24
+ }, [searchParams.set, searchParams.delete])
25
+
26
+ const clearSelectedResourceId = useCallback(
27
+ () => setSelectedResourceId(undefined),
28
+ [setSelectedResourceId],
29
+ )
30
+
31
+ return useMemo(() => ({
32
+ selectedResourceId,
33
+ setSelectedResourceId,
34
+ clearSelectedResourceId,
35
+ }), [selectedResourceId, setSelectedResourceId, clearSelectedResourceId])
36
+ }
@@ -1,19 +0,0 @@
1
- import React from 'react'
2
- import { ResourceContentPage } from './ResourceContentPage.jsx'
3
-
4
- export default {
5
- title: 'Websites/Ossy/ResourceContentPage',
6
- component: ResourceContentPage,
7
- parameters: {
8
- layout: 'fullscreen',
9
- workspaceId: '36zDqF0TKZZ5KkJdyg7NH',
10
- params: {
11
- resourceId: 'yBa6GdMeHg2EQr-u5xDd3'
12
- }
13
- },
14
- }
15
-
16
- const Story = props => <ResourceContentPage {...props}/>
17
-
18
- export const Default = Story.bind({})
19
- Default.args = { }
@@ -1,86 +0,0 @@
1
- export class Resource {
2
-
3
- static AggregateType = 'Resource'
4
-
5
- static View(events, savedState = {}) {
6
- return events.reduce((resource, event) => {
7
- switch (event.type) {
8
-
9
- case 'Created': {
10
- const content = event?.payload?.content || {}
11
-
12
- return {
13
- ...resource,
14
- id: event.aggregateId,
15
- name: event.payload.name,
16
- location: event.payload.location,
17
- belongsTo: event.payload.belongsTo,
18
- type: event.payload.type,
19
- createdBy: event.createdBy,
20
- created: event.created,
21
- lastUpdated: event.created,
22
- content: content,
23
- access: event.payload.access || 'restricted',
24
- }
25
- }
26
-
27
- case 'NameUpdated':
28
- return {
29
- ...resource,
30
- lastUpdated: event.created,
31
- name: event.payload.name
32
- }
33
-
34
- case 'LocationUpdated':
35
- return {
36
- ...resource,
37
- lastUpdated: event.created,
38
- location: event.payload.location
39
- }
40
-
41
- case 'ContentUpdated':
42
- return {
43
- ...resource,
44
- lastUpdated: event.created,
45
- content: event.payload.content
46
- }
47
-
48
- case 'AccessUpdated':
49
- return {
50
- ...resource,
51
- lastUpdated: event.created,
52
- access: event.payload.access
53
- }
54
-
55
- case 'NamedVersionUploaded': {
56
- const mediaKey = event.payload.Key
57
-
58
- return {
59
- ...resource,
60
- lastUpdated: event.created,
61
- content: {
62
- ...resource.content,
63
- sizes: {
64
- ...(resource.content.sizes || {}),
65
- [event.payload.namedVersion]: mediaKey
66
- }
67
- },
68
- }
69
- }
70
-
71
- case 'Deleted':
72
- return {
73
- status: ['removed'],
74
- }
75
-
76
- default:
77
- return resource
78
-
79
- }
80
- }, savedState)
81
-
82
- }
83
- }
84
-
85
- export { Resource as Aggregate }
86
- export const id = 'Resource'