Skip to content

Commit ffdde73

Browse files
author
Mint de Wit
authored
Merge pull request #38 from bbc/chore/ui-useredit-properties
Refactor Segment/Part properties to be a property instead of an operation
2 parents 91dae54 + 53488d2 commit ffdde73

File tree

21 files changed

+728
-557
lines changed

21 files changed

+728
-557
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?: 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: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ export interface UserEditingSourceLayer {
7777
sourceLayerLabel: string
7878
sourceLayerType: SourceLayerType
7979
schema: JSONBlob<JSONSchema>
80+
defaultValue?: Record<string, any>
8081
}
8182

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

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, CoreUserEditingProperties } 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?: CoreUserEditingProperties
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, CoreUserEditingProperties } 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?: CoreUserEditingProperties
5460
}

packages/corelib/src/dataModel/UserEditingDefinitions.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,48 @@ export interface CoreUserEditingDefinitionSourceLayerForm {
6666
value: Record<string, any>
6767
}
6868
}
69+
70+
export interface CoreUserEditingProperties {
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?: CoreUserEditingDefinitionAction[]
110+
111+
/** Translation namespaces to use when rendering this form */
112+
translationNamespaces: string[]
113+
}

packages/documentation/docs/for-developers/json-config-schema.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,12 @@ If an integer property, whether to treat it as zero-based
4343
### `ui:displayType`
4444

4545
Override the presentation with a special mode.
46-
Currently only valid for string properties. Valid values are 'json'.
46+
47+
Currently only valid for:
48+
49+
- object properties. Valid values are 'json'.
50+
- string properties. Valid values are 'base64-image'.
51+
- boolean properties. Valid values are 'switch'.
4752

4853
### `tsEnumNames`
4954

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

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
CoreUserEditingDefinitionAction,
1515
CoreUserEditingDefinitionForm,
1616
CoreUserEditingDefinitionSourceLayerForm,
17+
CoreUserEditingProperties,
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'
@@ -58,6 +59,7 @@ import {
5859
UserEditingDefinitionAction,
5960
UserEditingDefinitionForm,
6061
UserEditingDefinitionSourceLayerForm,
62+
UserEditingProperties,
6163
UserEditingType,
6264
} from '@sofie-automation/blueprints-integration/dist/userEditing'
6365
import type { PlayoutMutatablePart } from '../../playout/model/PlayoutPartInstanceModel'
@@ -121,6 +123,7 @@ export const IBlueprintMutatablePartSampleKeys = allKeysOfObject<IBlueprintMutat
121123
identifier: true,
122124
hackListenToMediaObjectUpdates: true,
123125
userEditOperations: true,
126+
userEditProperties: true,
124127
})
125128

126129
/*
@@ -281,6 +284,7 @@ export function convertPartToBlueprints(part: ReadonlyDeep<DBPart>): IBlueprintP
281284
part.hackListenToMediaObjectUpdates
282285
),
283286
userEditOperations: translateUserEditsToBlueprint(part.userEditOperations),
287+
userEditProperties: translateUserEditPropertiesToBlueprint(part.userEditProperties),
284288
}
285289

286290
return obj
@@ -349,6 +353,7 @@ export function convertSegmentToBlueprints(segment: ReadonlyDeep<DBSegment>): IB
349353
showShelf: segment.showShelf,
350354
segmentTiming: segment.segmentTiming,
351355
userEditOperations: translateUserEditsToBlueprint(segment.userEditOperations),
356+
userEditProperties: translateUserEditPropertiesToBlueprint(segment.userEditProperties),
352357
}
353358

354359
return obj
@@ -540,6 +545,30 @@ function translateUserEditsToBlueprint(
540545
)
541546
}
542547

548+
function translateUserEditPropertiesToBlueprint(
549+
props: ReadonlyDeep<CoreUserEditingProperties> | undefined
550+
): UserEditingProperties | undefined {
551+
if (!props) return undefined
552+
553+
return {
554+
globalProperties: props.globalProperties,
555+
pieceTypeProperties: props.pieceTypeProperties,
556+
557+
operations: props.operations?.map(
558+
(userEdit) =>
559+
({
560+
type: UserEditingType.ACTION,
561+
id: userEdit.id,
562+
label: omit(userEdit.label, 'namespaces'),
563+
svgIcon: userEdit.svgIcon,
564+
svgIconInactive: userEdit.svgIconInactive,
565+
isActive: userEdit.isActive,
566+
buttonType: userEdit.buttonType,
567+
} satisfies Complete<UserEditingDefinitionAction>)
568+
),
569+
}
570+
}
571+
543572
export function translateUserEditsFromBlueprint(
544573
userEdits: UserEditingDefinition[] | undefined,
545574
blueprintIds: BlueprintId[]
@@ -585,6 +614,33 @@ export function translateUserEditsFromBlueprint(
585614
)
586615
}
587616

617+
export function translateUserEditPropertiesFromBlueprint(
618+
props: UserEditingProperties | undefined,
619+
blueprintIds: BlueprintId[]
620+
): CoreUserEditingProperties | undefined {
621+
if (!props) return undefined
622+
623+
return {
624+
globalProperties: clone(props.globalProperties),
625+
pieceTypeProperties: clone(props.pieceTypeProperties),
626+
627+
operations: props.operations?.map(
628+
(userEdit) =>
629+
({
630+
type: UserEditingType.ACTION,
631+
id: userEdit.id,
632+
label: wrapTranslatableMessageFromBlueprints(userEdit.label, blueprintIds),
633+
svgIcon: userEdit.svgIcon,
634+
svgIconInactive: userEdit.svgIconInactive,
635+
isActive: userEdit.isActive,
636+
buttonType: userEdit.buttonType,
637+
} satisfies Complete<UserEditingDefinitionAction>)
638+
),
639+
640+
translationNamespaces: blueprintIds.map((id) => `blueprint_${id}`),
641+
}
642+
}
643+
588644
export function convertPartialBlueprintMutablePartToCore(
589645
updatePart: Partial<IBlueprintMutatablePart>,
590646
blueprintId: BlueprintId
@@ -602,5 +658,13 @@ export function convertPartialBlueprintMutablePartToCore(
602658
delete playoutUpdatePart.userEditOperations
603659
}
604660

661+
if ('userEditProperties' in updatePart) {
662+
playoutUpdatePart.userEditProperties = translateUserEditPropertiesFromBlueprint(updatePart.userEditProperties, [
663+
blueprintId,
664+
])
665+
} else {
666+
delete playoutUpdatePart.userEditOperations
667+
}
668+
605669
return playoutUpdatePart
606670
}

packages/job-worker/src/blueprints/context/services/PartAndPieceInstanceActionService.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,7 @@ export class PartAndPieceInstanceActionService {
388388
floated: false,
389389
expectedDurationWithTransition: undefined, // Filled in later
390390
userEditOperations: [], // Adlibbed parts can't be edited by ingest
391+
userEditProperties: undefined,
391392
}
392393

393394
const pieces = postProcessPieces(

0 commit comments

Comments
 (0)