Skip to content

Commit 77bf227

Browse files
author
Mint de Wit
committed
feat: refactor form action to properties field
1 parent 91dae54 commit 77bf227

File tree

10 files changed

+377
-415
lines changed

10 files changed

+377
-415
lines changed

packages/blueprints-integration/src/documents/part.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { UserEditingDefinition } from '../userEditing'
1+
import { UserEditingDefinition, UserEditingProperties } from '../userEditing'
22
import type { NoteSeverity } from '../lib'
33
import type { ITranslatableMessage } from '../translations'
44

@@ -88,6 +88,12 @@ export interface IBlueprintMutatablePart<TPrivateData = unknown, TPublicData = u
8888
* User editing definitions for this part
8989
*/
9090
userEditOperations?: UserEditingDefinition[]
91+
92+
/**
93+
* Properties that are user editable from the properties panel in the Sofie UI, if the user saves changes to these
94+
* it will trigger a user edit operation of type DefaultUserOperationEditProperties
95+
*/
96+
userEditProperties?: Readonly<UserEditingProperties>
9197
}
9298

9399
export interface HackPartMediaObjectSubscription {

packages/blueprints-integration/src/documents/segment.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { UserEditingDefinition } from '../userEditing'
1+
import { UserEditingDefinition, UserEditingProperties } from '../userEditing'
22

33
export enum SegmentDisplayMode {
44
Timeline = 'timeline',
@@ -52,6 +52,12 @@ export interface IBlueprintSegment<TPrivateData = unknown, TPublicData = unknown
5252
* User editing definitions for this segment
5353
*/
5454
userEditOperations?: UserEditingDefinition[]
55+
56+
/**
57+
* Properties that are user editable from the properties panel in the Sofie UI, if the user saves changes to these
58+
* it will trigger a user edit operation of type DefaultUserOperationEditProperties
59+
*/
60+
userEditProperties?: UserEditingProperties
5561
}
5662
/** The Segment sent from Core */
5763
export interface IBlueprintSegmentDB<TPrivateData = unknown, TPublicData = unknown>

packages/blueprints-integration/src/ingest.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,10 @@ export interface UserOperationTarget {
125125
}
126126

127127
export enum DefaultUserOperationsTypes {
128-
REVERT_SEGMENT = 'revert-segment',
129-
REVERT_PART = 'revert-part',
130-
REVERT_RUNDOWN = 'revert-rundown',
128+
REVERT_SEGMENT = '__sofie-revert-segment',
129+
REVERT_PART = '__sofie-revert-part',
130+
REVERT_RUNDOWN = '__sofie-revert-rundown',
131+
UPDATE_PROPS = '__sofie-update-props',
131132
}
132133

133134
export interface DefaultUserOperationRevertRundown {
@@ -144,14 +145,19 @@ export interface DefaultUserOperationRevertPart {
144145
id: DefaultUserOperationsTypes.REVERT_PART
145146
}
146147

148+
export interface DefaultUserOperationEditProperties {
149+
id: DefaultUserOperationsTypes.UPDATE_PROPS
150+
payload: {
151+
pieceTypeProperties: { type: string; value: Record<string, any> }
152+
globalProperties: Record<string, any>
153+
}
154+
}
155+
147156
export type DefaultUserOperations =
148-
| {
149-
id: '__sofie-move-segment' // Future: define properly
150-
payload: Record<string, never>
151-
}
152157
| DefaultUserOperationRevertRundown
153158
| DefaultUserOperationRevertSegment
154159
| DefaultUserOperationRevertPart
160+
| DefaultUserOperationEditProperties
155161

156162
export interface UserOperationChange<TCustomBlueprintOperations extends { id: string } = never> {
157163
/** Indicate that this change is from user operations */

packages/blueprints-integration/src/userEditing.ts

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ export enum UserEditingType {
7474
}
7575

7676
export interface UserEditingSourceLayer {
77-
sourceLayerLabel: string
77+
sourceLayerLabel: string // translate? take from type?
7878
sourceLayerType: SourceLayerType
7979
schema: JSONBlob<JSONSchema>
8080
}
@@ -87,3 +87,45 @@ export enum UserEditingButtonType {
8787
/** Hidden */
8888
HIDDEN = 'hidden',
8989
}
90+
91+
export interface UserEditingProperties {
92+
/**
93+
* These properties are dependent on the (primary) piece type, the user will get the option
94+
* to select the type of piece (from the SourceLayerTypes i.e. Camera or Split etc.) and then
95+
* be presented the corresponding form
96+
*
97+
* example:
98+
* {
99+
* schema: {
100+
* camera: '{ "type": "object", "properties": { "input": { "type": "number" } } }',
101+
* split: '{ "type": "object", ... }',
102+
* },
103+
* currentValue: {
104+
* type: 'camera',
105+
* value: {
106+
* input: 3
107+
* },
108+
* }
109+
* }
110+
*/
111+
pieceTypeProperties?: {
112+
schema: Record<string, UserEditingSourceLayer>
113+
currentValue: { type: string; value: Record<string, any> }
114+
}
115+
116+
/**
117+
* These are properties that are available to edit regardless of the piece type, examples
118+
* could be whether it an element is locked from NRCS updates
119+
*
120+
* if you do not want the piece type to be changed, then use only this field.
121+
*/
122+
globalProperties?: { schema: JSONBlob<JSONSchema>; currentValue: Record<string, any> }
123+
124+
/**
125+
* A list of id's of operations to be exposed on the properties panel as buttons. These operations
126+
* must be available on the element
127+
*
128+
* note - perhaps these should have their own full definitions?
129+
*/
130+
operations?: string[]
131+
}

packages/corelib/src/dataModel/Part.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { ITranslatableMessage } from '../TranslatableMessage'
33
import { PartId, RundownId, SegmentId } from './Ids'
44
import { PartNote } from './Notes'
55
import { ReadonlyDeep } from 'type-fest'
6-
import { CoreUserEditingDefinition } from './UserEditingDefinitions'
6+
import { CoreUserEditingDefinition, UserEditingProperties } from './UserEditingDefinitions'
77

88
export interface PartInvalidReason {
99
message: ITranslatableMessage
@@ -41,6 +41,12 @@ export interface DBPart extends Omit<IBlueprintPart, 'userEditOperations'> {
4141
* User editing definitions for this part
4242
*/
4343
userEditOperations?: CoreUserEditingDefinition[]
44+
45+
/**
46+
* Properties that are user editable from the properties panel in the Sofie UI, if the user saves changes to these
47+
* it will trigger a user edit operation of type DefaultUserOperationEditProperties
48+
*/
49+
userEditProperties?: UserEditingProperties
4450
}
4551

4652
export function isPartPlayable(part: Pick<ReadonlyDeep<DBPart>, 'invalid' | 'floated'>): boolean {

packages/corelib/src/dataModel/Segment.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { SegmentDisplayMode, SegmentTimingInfo } from '@sofie-automation/blueprints-integration'
22
import { SegmentId, RundownId } from './Ids'
33
import { SegmentNote } from './Notes'
4-
import { CoreUserEditingDefinition } from './UserEditingDefinitions'
4+
import { CoreUserEditingDefinition, UserEditingProperties } from './UserEditingDefinitions'
55

66
export enum SegmentOrphanedReason {
77
/** Segment is deleted from the NRCS but we still need it */
@@ -51,4 +51,10 @@ export interface DBSegment {
5151
* User editing definitions for this segment
5252
*/
5353
userEditOperations?: CoreUserEditingDefinition[]
54+
55+
/**
56+
* Properties that are user editable from the properties panel in the Sofie UI, if the user saves changes to these
57+
* it will trigger a user edit operation of type DefaultUserOperationEditProperties
58+
*/
59+
userEditProperties?: UserEditingProperties
5460
}

packages/corelib/src/dataModel/UserEditingDefinitions.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,45 @@ export interface CoreUserEditingDefinitionSourceLayerForm {
6666
value: Record<string, any>
6767
}
6868
}
69+
70+
export interface UserEditingProperties {
71+
/**
72+
* These properties are dependent on the (primary) piece type, the user will get the option
73+
* to select the type of piece (from the SourceLayerTypes i.e. Camera or Split etc.) and then
74+
* be presented the corresponding form
75+
*
76+
* example:
77+
* {
78+
* schema: {
79+
* camera: '{ "type": "object", "properties": { "input": { "type": "number" } } }',
80+
* split: '{ "type": "object", ... }',
81+
* },
82+
* currentValue: {
83+
* type: 'camera',
84+
* value: {
85+
* input: 3
86+
* },
87+
* }
88+
* }
89+
*/
90+
pieceTypeProperties?: {
91+
schema: Record<string, UserEditingSourceLayer>
92+
currentValue: { type: string; value: Record<string, any> }
93+
}
94+
95+
/**
96+
* These are properties that are available to edit regardless of the piece type, examples
97+
* could be whether it an element is locked from NRCS updates
98+
*
99+
* if you do not want the piece type to be changed, then use only this field.
100+
*/
101+
globalProperties?: { schema: JSONBlob<JSONSchema>; currentValue: Record<string, any> }
102+
103+
/**
104+
* A list of id's of operations to be exposed on the properties panel as buttons. These operations
105+
* must be available on the element
106+
*
107+
* note - perhaps these should have their own full definitions?
108+
*/
109+
operations?: string[]
110+
}

packages/job-worker/src/blueprints/context/lib.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
CoreUserEditingDefinitionAction,
1515
CoreUserEditingDefinitionForm,
1616
CoreUserEditingDefinitionSourceLayerForm,
17+
UserEditingProperties,
1718
} from '@sofie-automation/corelib/dist/dataModel/UserEditingDefinitions'
1819
import { DBSegment } from '@sofie-automation/corelib/dist/dataModel/Segment'
1920
import { assertNever, clone, Complete, literal, omit } from '@sofie-automation/corelib/dist/lib'
@@ -121,6 +122,7 @@ export const IBlueprintMutatablePartSampleKeys = allKeysOfObject<IBlueprintMutat
121122
identifier: true,
122123
hackListenToMediaObjectUpdates: true,
123124
userEditOperations: true,
125+
userEditProperties: true,
124126
})
125127

126128
/*
@@ -281,6 +283,7 @@ export function convertPartToBlueprints(part: ReadonlyDeep<DBPart>): IBlueprintP
281283
part.hackListenToMediaObjectUpdates
282284
),
283285
userEditOperations: translateUserEditsToBlueprint(part.userEditOperations),
286+
userEditProperties: clone<UserEditingProperties | undefined>(part.userEditProperties),
284287
}
285288

286289
return obj
@@ -349,6 +352,7 @@ export function convertSegmentToBlueprints(segment: ReadonlyDeep<DBSegment>): IB
349352
showShelf: segment.showShelf,
350353
segmentTiming: segment.segmentTiming,
351354
userEditOperations: translateUserEditsToBlueprint(segment.userEditOperations),
355+
userEditProperties: clone<UserEditingProperties | undefined>(segment.userEditProperties),
352356
}
353357

354358
return obj
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { useCallback, useMemo, useState } from 'react'
2+
import {
3+
OverrideOpHelperForItemContentsBatcher,
4+
WrappedOverridableItemNormal,
5+
} from '../../ui/Settings/util/OverrideOpHelper'
6+
import { SchemaFormCommonProps } from './schemaFormUtil'
7+
import { SchemaFormWithOverrides } from './SchemaFormWithOverrides'
8+
import { literal, objectPathSet } from '@sofie-automation/corelib/dist/lib'
9+
import { AnyARecord } from 'dns'
10+
11+
interface SchemaFormWithStateProps extends Omit<SchemaFormCommonProps, 'isRequired'> {
12+
object: any
13+
14+
onUpdate: (object: any) => void
15+
}
16+
17+
export function SchemaFormWithState({
18+
object,
19+
onUpdate,
20+
...commonProps
21+
}: Readonly<SchemaFormWithStateProps>): JSX.Element {
22+
const helper = useCallback(
23+
() =>
24+
new OverrideOpHelperWithState(object, (object) => {
25+
onUpdate(object)
26+
}),
27+
[object, onUpdate]
28+
)
29+
30+
const wrappedItem = useMemo(
31+
() =>
32+
literal<WrappedOverridableItemNormal<any>>({
33+
type: 'normal',
34+
id: 'not-used',
35+
computed: object,
36+
defaults: undefined,
37+
overrideOps: [],
38+
}),
39+
[object]
40+
)
41+
42+
return <SchemaFormWithOverrides {...commonProps} attr="" item={wrappedItem} overrideHelper={helper} isRequired />
43+
}
44+
45+
/**
46+
* An alternate OverrideOpHelper designed to directly mutate an object, instead of using the `ObjectWithOverrides` system.
47+
* This allows us to have one SchemaForm implementation that can handle working with `ObjectWithOverrides`, and simpler options
48+
*/
49+
class OverrideOpHelperWithState implements OverrideOpHelperForItemContentsBatcher {
50+
readonly #object: any
51+
readonly #onUpdate: (object: any) => void
52+
53+
constructor(object: AnyARecord, onUpdate: (object: any) => void) {
54+
this.#object = object
55+
this.#onUpdate = onUpdate
56+
}
57+
58+
clearItemOverrides(_itemId: string, _subPath: string): this {
59+
// Not supported as this is faking an item with overrides
60+
61+
return this
62+
}
63+
setItemValue(_itemId: string, subPath: string, value: any): this {
64+
objectPathSet(this.#object, subPath, value)
65+
66+
return this
67+
}
68+
69+
commit(): void {
70+
this.#onUpdate(this.#object)
71+
}
72+
}

0 commit comments

Comments
 (0)