forked from nrkno/sofie-core
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
f7a4fe7
commit 1284f9f
Showing
10 changed files
with
312 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import { PartInstanceId, PieceInstanceId, SegmentId } from '@sofie-automation/corelib/dist/dataModel/Ids' | ||
import { createContext } from 'react' | ||
import { PieceUi } from '../SegmentContainer/withResolvedSegment' | ||
|
||
export interface IDragContext { | ||
/** | ||
* Indicate a drag operation on a piece has started | ||
* @param piece The piece that is being dragged | ||
* @param timeScale The current TimeScale of the segment | ||
* @param position The position of the mouse | ||
* @param elementOffset The x-coordinate of the element relative to the mouse position | ||
* @param limitToSegment Whether the piece can be dragged to other segments (note: if the other segment does not have the right source layer the piece will look to have disappeared... consider omitting this is a todo) | ||
*/ | ||
startDrag: ( | ||
piece: PieceUi, | ||
timeScale: number, | ||
position: { x: number; y: number }, | ||
elementOffset?: number, | ||
limitToSegment?: SegmentId | ||
) => void | ||
/** | ||
* Indicate the part the mouse is on has changed | ||
* @param partId The part id that the mouse is currently hovering on | ||
* @param segmentId The segment the part currenly hover is in | ||
* @param position The position of the part in absolute coords to the screen | ||
*/ | ||
setHoveredPart: (partId: PartInstanceId, segmentId: SegmentId, position: { x: number; y: number }) => void | ||
|
||
/** | ||
* PieceId of the piece that is being dragged | ||
*/ | ||
pieceId: undefined | PieceInstanceId | ||
/** | ||
* The piece with any local overrides coming from dragging it around (i.e. changed renderedInPoint) | ||
*/ | ||
piece: undefined | PieceUi | ||
} | ||
|
||
export const dragContext = createContext<IDragContext | undefined>(undefined) // slay. |
145 changes: 145 additions & 0 deletions
145
packages/webui/src/client/ui/RundownView/DragContextProvider.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
import { PartInstanceId, PieceInstanceId, SegmentId } from '@sofie-automation/corelib/dist/dataModel/Ids' | ||
import { PropsWithChildren, useRef, useState } from 'react' | ||
import { dragContext, IDragContext } from './DragContext' | ||
import { PieceUi } from '../SegmentContainer/withResolvedSegment' | ||
import { doUserAction, UserAction } from '../../lib/clientUserAction' | ||
import { MeteorCall } from '../../lib/meteorApi' | ||
import { TFunction } from 'i18next' | ||
import { UIParts } from '../Collections' | ||
import { Segments } from '../../collections' | ||
import { literal } from '../../lib/tempLib' | ||
import { DefaultUserOperationRetimePiece, DefaultUserOperationsTypes } from '@sofie-automation/blueprints-integration' | ||
|
||
const DRAG_TIMEOUT = 10000 | ||
|
||
interface Props { | ||
t: TFunction | ||
} | ||
|
||
// notes: this doesn't limit dragging between rundowns right now but I'm not sure if the ingest stage will be happy with that - mint | ||
export function DragContextProvider({ t, children }: PropsWithChildren<Props>): JSX.Element { | ||
const [pieceId, setPieceId] = useState<undefined | PieceInstanceId>(undefined) | ||
const [piece, setPiece] = useState<undefined | PieceUi>(undefined) | ||
|
||
const partIdRef = useRef<undefined | PartInstanceId>(undefined) | ||
const positionRef = useRef({ x: 0, y: 0 }) | ||
const segmentIdRef = useRef<undefined | SegmentId>(undefined) | ||
|
||
const startDrag = ( | ||
ogPiece: PieceUi, | ||
timeScale: number, | ||
pos: { x: number; y: number }, | ||
elementOffset?: number, | ||
limitToSegment?: SegmentId | ||
) => { | ||
if (pieceId) return // a drag is currently in progress.... | ||
|
||
const inPoint = ogPiece.renderedInPoint ?? 0 | ||
segmentIdRef.current = limitToSegment | ||
positionRef.current = pos | ||
setPieceId(ogPiece.instance._id) | ||
|
||
let localPiece = ogPiece // keep a copy of the overriden piece because react does not let us access the state of the context easily | ||
|
||
const onMove = (e: MouseEvent) => { | ||
const newInPoint = | ||
(!partIdRef.current ? inPoint : (elementOffset ?? 0) / timeScale) + | ||
(e.clientX - positionRef.current.x) / timeScale | ||
|
||
localPiece = { | ||
...ogPiece, | ||
instance: { ...ogPiece.instance, partInstanceId: partIdRef.current ?? ogPiece.instance.partInstanceId }, | ||
renderedInPoint: newInPoint, | ||
} | ||
setPiece(localPiece) | ||
} | ||
|
||
const cleanup = () => { | ||
// unset state - note: for ux reasons this runs after the backend operation has returned a result | ||
setPieceId(undefined) | ||
setPiece(undefined) | ||
partIdRef.current = undefined | ||
segmentIdRef.current = undefined | ||
} | ||
|
||
const onMouseUp = (e: MouseEvent) => { | ||
// detach from the mouse | ||
document.removeEventListener('mousemove', onMove) | ||
document.removeEventListener('mouseup', onMouseUp) | ||
|
||
// process the drag | ||
if (!localPiece || localPiece.renderedInPoint === ogPiece.renderedInPoint) return cleanup() | ||
|
||
// find the parts so we can get their externalId | ||
const startPartId = localPiece.instance.piece.startPartId // this could become a funny thing with infinites | ||
const part = UIParts.findOne(startPartId) | ||
const oldPart = | ||
startPartId === ogPiece.instance.piece.startPartId ? part : UIParts.findOne(ogPiece.instance.piece.startPartId) | ||
if (!part) return cleanup() // tough to continue without a parent for the piece | ||
|
||
// find the Segment's External ID | ||
const segment = Segments.findOne(part?.segmentId) | ||
const oldSegment = part?.segmentId === oldPart?.segmentId ? segment : Segments.findOne(oldPart?.segmentId) | ||
if (!segment) return | ||
|
||
const operationTarget = { | ||
segmentExternalId: oldSegment?.externalId, | ||
partExternalId: oldPart?.externalId, | ||
pieceExternalId: ogPiece.instance.piece.externalId, | ||
} | ||
doUserAction( | ||
t, | ||
e, | ||
UserAction.EXECUTE_USER_OPERATION, | ||
(e, ts) => | ||
MeteorCall.userAction.executeUserChangeOperation( | ||
e, | ||
ts, | ||
part.rundownId, | ||
operationTarget, | ||
literal<DefaultUserOperationRetimePiece>({ | ||
id: DefaultUserOperationsTypes.RETIME_PIECE, | ||
payload: { | ||
segmentExternalId: segment.externalId, | ||
partExternalId: part.externalId, | ||
|
||
inPoint: localPiece.renderedInPoint ?? inPoint, | ||
}, | ||
}) | ||
), | ||
() => { | ||
cleanup() | ||
} | ||
) | ||
} | ||
|
||
document.addEventListener('mousemove', onMove) | ||
document.addEventListener('mouseup', onMouseUp) | ||
|
||
setTimeout(() => { | ||
// after the timeout we want to bail out in case something went wrong | ||
document.removeEventListener('mousemove', onMove) | ||
document.removeEventListener('mouseup', onMouseUp) | ||
|
||
cleanup() | ||
}, DRAG_TIMEOUT) | ||
} | ||
const setHoveredPart = (updatedPartId: PartInstanceId, segmentId: SegmentId, pos: { x: number; y: number }) => { | ||
if (!pieceId) return | ||
if (updatedPartId === piece?.instance.partInstanceId) return | ||
if (segmentIdRef.current && segmentIdRef.current !== segmentId) return | ||
|
||
partIdRef.current = updatedPartId | ||
positionRef.current = pos | ||
} | ||
|
||
const ctx = literal<IDragContext>({ | ||
pieceId, | ||
piece, | ||
|
||
startDrag, | ||
setHoveredPart, | ||
}) | ||
|
||
return <dragContext.Provider value={ctx}>{children}</dragContext.Provider> | ||
} |
Oops, something went wrong.