Skip to content

Commit a2d73db

Browse files
committed
Merge branch 'release50' into release51
2 parents 87573f0 + 17cec2b commit a2d73db

File tree

21 files changed

+656
-105
lines changed

21 files changed

+656
-105
lines changed

meteor/client/styles/shelf/externalFramePanel.scss

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,33 @@
2626
margin: 0.625rem;
2727
}
2828
}
29+
30+
.dropzone-panel {
31+
position: absolute;
32+
top: 0;
33+
left: 0;
34+
bottom: 0;
35+
right: 0;
36+
37+
.external-frame-panel__iframe {
38+
position: absolute;
39+
top: 0;
40+
left: 0;
41+
bottom: 0;
42+
right: 0;
43+
width: 100%;
44+
height: 100%;
45+
border: none;
46+
transform-origin: top left;
47+
z-index: 99;
48+
}
49+
.external-frame-panel__overlay {
50+
position: absolute;
51+
top: 0;
52+
left: 0;
53+
bottom: 0;
54+
right: 0;
55+
border: none;
56+
transform-origin: top left;
57+
}
58+
}

meteor/client/ui/SegmentTimeline/withMediaObjectStatus.tsx

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { deepFreeze } from '@sofie-automation/corelib/dist/lib'
1313
import _ from 'underscore'
1414
import { useTracker } from '../../lib/ReactMeteorData/ReactMeteorData'
1515
import { PieceInstance } from '@sofie-automation/corelib/dist/dataModel/PieceInstance'
16+
import { UIBucketContentStatus, UIPieceContentStatus } from '../../../lib/api/rundownNotifications'
1617

1718
type AnyPiece = {
1819
piece?: BucketAdLibUi | IAdLibListItem | AdLibPieceUi | PieceUi | BucketAdLibActionUi | undefined
@@ -51,6 +52,35 @@ function unwrapPieceInstance(piece: BucketAdLibUi | IAdLibListItem | AdLibPieceU
5152
}
5253
}
5354

55+
function getStatusDocForPiece(
56+
piece: BucketAdLibUi | IAdLibListItem | AdLibPieceUi | PieceUi | BucketAdLibActionUi
57+
): UIPieceContentStatus | UIBucketContentStatus | undefined {
58+
const pieceUnwrapped = unwrapPieceInstance(piece)
59+
60+
// Bucket items use a different collection
61+
if (RundownUtils.isBucketAdLibItem(piece)) {
62+
return UIBucketContentStatuses.findOne({
63+
bucketId: piece.bucketId,
64+
docId: pieceUnwrapped._id,
65+
})
66+
}
67+
68+
// PieceInstance's might have a dedicated status
69+
if (RundownUtils.isPieceInstance(piece)) {
70+
const status = UIPieceContentStatuses.findOne({
71+
// Future: It would be good for this to be stricter.
72+
pieceId: piece.instance._id,
73+
})
74+
if (status) return status
75+
}
76+
77+
// Fallback to using the one from the source piece
78+
return UIPieceContentStatuses.findOne({
79+
// Future: It would be good for this to be stricter.
80+
pieceId: pieceUnwrapped._id,
81+
})
82+
}
83+
5484
/**
5585
* @deprecated This can now be achieved by a simple minimongo query against either UIPieceContentStatuses or UIBucketContentStatuses
5686
*/
@@ -81,19 +111,8 @@ export function withMediaObjectStatus<IProps extends AnyPiece, IState>(): (
81111

82112
// Check item status
83113
if (piece && (piece.sourceLayer || layer) && studio) {
84-
const pieceUnwrapped = unwrapPieceInstance(piece)
85-
const statusDoc = RundownUtils.isBucketAdLibItem(piece)
86-
? UIBucketContentStatuses.findOne({
87-
bucketId: piece.bucketId,
88-
docId: pieceUnwrapped._id,
89-
})
90-
: UIPieceContentStatuses.findOne({
91-
// Future: It would be good for this to be stricter.
92-
pieceId: pieceUnwrapped._id,
93-
})
94-
95114
// Extract the status or populate some default values
96-
const statusObj = statusDoc?.status ?? DEFAULT_STATUS
115+
const statusObj = getStatusDocForPiece(piece)?.status ?? DEFAULT_STATUS
97116

98117
if (RundownUtils.isAdLibPieceOrAdLibListItem(piece)) {
99118
if (!overrides.piece || !_.isEqual(statusObj, (overrides.piece as AdLibPieceUi).contentStatus)) {

meteor/client/ui/Settings/components/FilterEditor.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,18 @@ export default withTranslation()(
535535
/>
536536
</label>
537537

538+
<label className="field">
539+
<LabelActual label={t('Dropzone URL')} />
540+
<EditAttribute
541+
modifiedClassName="bghl"
542+
attribute={`filters.${index}.dropzoneUrl`}
543+
obj={item}
544+
type="text"
545+
collection={RundownLayouts}
546+
className="input text-input input-l"
547+
/>
548+
</label>
549+
538550
<label className="field">
539551
<LabelActual label={t('Display Rank')} />
540552
<EditAttribute

meteor/client/ui/Shelf/BucketPanel.tsx

Lines changed: 112 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import { PieceDisplayStyle } from '../../../lib/collections/RundownLayouts'
4545
import RundownViewEventBus, {
4646
RundownViewEvents,
4747
RevealInShelfEvent,
48+
ToggleShelfDropzoneEvent,
4849
} from '../../../lib/api/triggers/RundownViewEventBus'
4950
import { setShelfContextMenuContext, ContextType } from './ShelfContextMenu'
5051
import { translateMessage } from '@sofie-automation/corelib/dist/TranslatableMessage'
@@ -180,6 +181,7 @@ const bucketTarget = {
180181

181182
interface IState {
182183
dropActive: boolean
184+
dropFrameActive: string | null
183185
bucketName: string
184186
adLibPieces: BucketAdLibItem[]
185187
singleClickMode: boolean
@@ -246,6 +248,7 @@ export interface IBucketPanelProps {
246248
onSelectAdlib: (piece: any) => void // TODO - fix this
247249
onAdLibContext: (args: { contextBucket: Bucket; contextBucketAdLib: BucketAdLibItem }, callback: () => void) => void
248250
onPieceNameRename: () => void
251+
extFrameDropZones: { _id: string; url: string }[]
249252
}
250253

251254
export interface IBucketPanelTrackedProps extends IDashboardPanelTrackedProps {
@@ -408,6 +411,7 @@ const BucketPanelContent = withTranslation()(
408411

409412
this.state = {
410413
dropActive: false,
414+
dropFrameActive: null,
411415
bucketName: props.bucket.name,
412416
adLibPieces: props.adLibPieces.slice(),
413417
singleClickMode: false,
@@ -419,6 +423,7 @@ const BucketPanelContent = withTranslation()(
419423
window.addEventListener(MOSEvents.dragleave, this.onDragLeave)
420424

421425
RundownViewEventBus.on(RundownViewEvents.REVEAL_IN_SHELF, this.onRevealInShelf)
426+
RundownViewEventBus.on(RundownViewEvents.TOGGLE_SHELF_DROPZONE, this.onToggleDropFrame)
422427
}
423428

424429
componentDidUpdate(prevProps: IBucketPanelProps & IBucketPanelTrackedProps) {
@@ -434,6 +439,7 @@ const BucketPanelContent = withTranslation()(
434439
componentWillUnmount(): void {
435440
window.removeEventListener(MOSEvents.dragenter, this.onDragEnter)
436441
window.removeEventListener(MOSEvents.dragleave, this.onDragLeave)
442+
RundownViewEventBus.removeListener(RundownViewEvents.TOGGLE_SHELF_DROPZONE, this.onToggleDropFrame)
437443
}
438444

439445
onRevealInShelf = (e: RevealInShelfEvent) => {
@@ -789,6 +795,13 @@ const BucketPanelContent = withTranslation()(
789795
)
790796
}
791797

798+
private onToggleDropFrame = (e: ToggleShelfDropzoneEvent) => {
799+
this.setState({
800+
// dropActive: e.display,
801+
dropFrameActive: e.display ? e.id : null,
802+
})
803+
}
804+
792805
render(): JSX.Element | null {
793806
const { connectDragSource, connectDragPreview, connectDropTarget } = this.props
794807

@@ -828,7 +841,8 @@ const BucketPanelContent = withTranslation()(
828841
<div
829842
className={ClassNames('dashboard-panel', 'dashboard-panel__panel--bucket', {
830843
'dashboard-panel__panel--bucket-active': this.state.dropActive,
831-
'dashboard-panel__panel--sort-dragging': this.props.isDragging,
844+
'dashboard-panel__panel--sort-dragging':
845+
(this.props.isDragging || this.state.dropFrameActive) && !this.state.dropActive,
832846
})}
833847
data-bucket-id={this.props.bucket._id}
834848
ref={this.setRef}
@@ -906,6 +920,18 @@ const BucketPanelContent = withTranslation()(
906920
</BucketPieceButton>
907921
</ContextMenuTrigger>
908922
))}
923+
{this.props.extFrameDropZones.map((dropZone) => (
924+
<DropzoneHolder
925+
key={dropZone._id}
926+
bucketId={this.props.bucket._id}
927+
id={dropZone._id}
928+
url={dropZone.url}
929+
hidden={this.state.dropFrameActive !== dropZone._id}
930+
showStyleBaseId={this.props.showStyleBaseId}
931+
onDragEnter={this.onDragEnter}
932+
onDragLeave={this.onDragLeave}
933+
/>
934+
))}
909935
</div>
910936
</div>
911937
)
@@ -917,3 +943,88 @@ const BucketPanelContent = withTranslation()(
917943
)
918944
)
919945
)
946+
947+
interface DropzoneHolderProps {
948+
id: string
949+
bucketId: BucketId
950+
url: string
951+
hidden: boolean
952+
showStyleBaseId: ShowStyleBaseId
953+
954+
onDragEnter?: () => void
955+
onDragLeave?: () => void
956+
}
957+
const DropzoneHolder = (props: DropzoneHolderProps) => {
958+
const [dropzoneElementRef, setDropzoneElementRef] = React.useState<HTMLIFrameElement | null>(null)
959+
960+
const onMessage = React.useCallback(
961+
(event: MessageEvent) => {
962+
// filter out messages from this panel
963+
if (event.source !== dropzoneElementRef?.contentWindow) return
964+
965+
switch (event.data?.event) {
966+
case 'drop':
967+
RundownViewEventBus.emit(RundownViewEvents.ITEM_DROPPED, {
968+
id: props.id,
969+
bucketId: props.bucketId,
970+
ev: event,
971+
})
972+
if (props.onDragLeave) props.onDragLeave()
973+
break
974+
case 'data':
975+
if (event.data.data.trim().endsWith('</mos>')) {
976+
RundownViewEventBus.emit(RundownViewEvents.ITEM_DROPPED, {
977+
id: props.id,
978+
bucketId: props.bucketId,
979+
message: event.data.data,
980+
ev: event,
981+
})
982+
}
983+
break
984+
case 'error':
985+
RundownViewEventBus.emit(RundownViewEvents.ITEM_DROPPED, {
986+
id: props.id,
987+
bucketId: props.bucketId,
988+
error: event.data.message,
989+
ev: event,
990+
})
991+
break
992+
case 'dragEnter':
993+
if (props.onDragEnter) props.onDragEnter()
994+
break
995+
case 'dragLeave':
996+
if (props.onDragLeave) props.onDragLeave()
997+
break
998+
}
999+
},
1000+
[dropzoneElementRef, props.onDragEnter, props.onDragLeave]
1001+
)
1002+
1003+
React.useEffect(() => {
1004+
if (!dropzoneElementRef) return
1005+
1006+
const registerHandlers = () => {
1007+
window.addEventListener('message', onMessage)
1008+
}
1009+
const unregisterHandlers = () => {
1010+
window.removeEventListener('message', onMessage)
1011+
}
1012+
1013+
registerHandlers()
1014+
1015+
return () => {
1016+
unregisterHandlers()
1017+
}
1018+
}, [dropzoneElementRef, onMessage])
1019+
1020+
return (
1021+
<div className="dropzone-panel" style={{ visibility: props.hidden ? 'hidden' : 'visible' }}>
1022+
<iframe
1023+
ref={setDropzoneElementRef}
1024+
className="external-frame-panel__iframe"
1025+
src={props.url}
1026+
sandbox="allow-forms allow-popups allow-scripts allow-same-origin"
1027+
></iframe>
1028+
</div>
1029+
)
1030+
}

0 commit comments

Comments
 (0)