Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 54 additions & 20 deletions packages/webui/src/client/ui/SegmentTimeline/SegmentContextMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,17 @@ import { IContextMenuContext } from '../RundownView.js'
import { PartInstanceId, SegmentId } from '@sofie-automation/corelib/dist/dataModel/Ids'
import { SegmentOrphanedReason } from '@sofie-automation/corelib/dist/dataModel/Segment'
import { UserEditOperationMenuItems } from '../UserEditOperations/RenderUserEditOperations.js'
import { CoreUserEditingDefinition } from '@sofie-automation/corelib/dist/dataModel/UserEditingDefinitions'
import {
CoreUserEditingDefinition,
CoreUserEditingDefinitionAction,
CoreUserEditingDefinitionForm,
CoreUserEditingDefinitionSofie,
} from '@sofie-automation/corelib/dist/dataModel/UserEditingDefinitions'
import * as RundownResolver from '../../lib/RundownResolver.js'
import { SelectedElement } from '../RundownView/SelectedElementsContext.js'
import { DBPartInstance } from '@sofie-automation/corelib/dist/dataModel/PartInstance.js'
import { ReadonlyObjectDeep } from 'type-fest/source/readonly-deep.js'
import { UserEditingType, DefaultUserOperationsTypes } from '@sofie-automation/blueprints-integration'

interface IProps {
onSetNext: (partInstance: DBPartInstance | DBPart | undefined, e: any, offset?: number, take?: boolean) => void
Expand Down Expand Up @@ -84,6 +91,20 @@ export function SegmentContextMenu({
return false
}

const doesItemSupportUserEditUpdateProps = (
userEditOperations:
| readonly (
| ReadonlyObjectDeep<CoreUserEditingDefinitionAction>
| ReadonlyObjectDeep<CoreUserEditingDefinitionForm>
| ReadonlyObjectDeep<CoreUserEditingDefinitionSofie>
)[]
| undefined
) => {
return userEditOperations?.find(
(op) => op.type === UserEditingType.SOFIE && op.id === DefaultUserOperationsTypes.UPDATE_PROPS
)
}

const onSetAsNextFromHere = (
partInstance: DBPartInstance,
nextPartInstanceId: PartInstanceId | null,
Expand Down Expand Up @@ -159,14 +180,17 @@ export function SegmentContextMenu({
isFormEditable={isSegmentEditAble}
/>
)}
{enableUserEdits && (
<>
<hr />
<MenuItem onClick={() => onEditProps({ type: 'segment', elementId: part.instance.segmentId })}>
<span>{t('Edit Segment Properties')}</span>
</MenuItem>
</>
)}
{enableUserEdits &&
segment &&
segment.userEditProperties &&
doesItemSupportUserEditUpdateProps(segment.userEditOperations) && (
<>
<hr />
<MenuItem onClick={() => onEditProps({ type: 'segment', elementId: part.instance.segmentId })}>
<span>{t('Edit Segment Properties')}</span>
</MenuItem>
</>
)}
<hr />
</>
)}
Expand Down Expand Up @@ -289,17 +313,27 @@ export function SegmentContextMenu({
{enableUserEdits && (
<>
<hr />
<MenuItem onClick={() => onEditProps({ type: 'segment', elementId: part.instance.segmentId })}>
<span>{t('Edit Segment Properties')}</span>
</MenuItem>
<MenuItem onClick={() => onEditProps({ type: 'part', elementId: part.instance.part._id })}>
<span>{t('Edit Part Properties')}</span>
</MenuItem>
{piece && piece.instance.piece.userEditProperties && (
<MenuItem onClick={() => onEditProps({ type: 'piece', elementId: piece.instance.piece._id })}>
<span>{t('Edit Piece Properties')}</span>
</MenuItem>
)}
{segment &&
segment.userEditProperties &&
doesItemSupportUserEditUpdateProps(segment.userEditOperations) && (
<MenuItem onClick={() => onEditProps({ type: 'segment', elementId: part.instance.segmentId })}>
<span>{t('Edit Segment Properties')}</span>
</MenuItem>
)}
{part &&
part.instance.part.userEditProperties &&
doesItemSupportUserEditUpdateProps(part.instance.part.userEditOperations) && (
<MenuItem onClick={() => onEditProps({ type: 'part', elementId: part.instance.part._id })}>
<span>{t('Edit Part Properties')}</span>
</MenuItem>
)}
{piece &&
piece.instance.piece.userEditProperties &&
doesItemSupportUserEditUpdateProps(piece.instance.piece.userEditOperations) && (
<MenuItem onClick={() => onEditProps({ type: 'piece', elementId: piece.instance.piece._id })}>
<span>{t('Edit Piece Properties')}</span>
</MenuItem>
)}
</>
Comment on lines 313 to 337
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Potential orphan <hr /> when no edit menu items render.

If enableUserEdits is true but none of segment, part, or piece satisfy their conditions (i.e., none have both userEditProperties and UPDATE_PROPS support), the block will render only the <hr /> on line 315 with no menu items following it, leaving a visual artifact at the end of the context menu.

🛠️ Proposed fix

Move the <hr /> inside a condition that checks if at least one item will render:

 {enableUserEdits && (
 	<>
-		<hr />
+		{(segment?.userEditProperties && doesItemSupportUserEditUpdateProps(segment.userEditOperations)) ||
+		 (part?.instance.part.userEditProperties && doesItemSupportUserEditUpdateProps(part.instance.part.userEditOperations)) ||
+		 (piece?.instance.piece.userEditProperties && doesItemSupportUserEditUpdateProps(piece.instance.piece.userEditOperations)) ? (
+			<hr />
+		) : null}
 		{segment &&
 			segment.userEditProperties &&
 			doesItemSupportUserEditUpdateProps(segment.userEditOperations) && (

Alternatively, compute a boolean flag earlier:

const hasAnyEditPropsItem =
	(segment?.userEditProperties && doesItemSupportUserEditUpdateProps(segment.userEditOperations)) ||
	(part?.instance.part.userEditProperties && doesItemSupportUserEditUpdateProps(part.instance.part.userEditOperations)) ||
	(piece?.instance.piece.userEditProperties && doesItemSupportUserEditUpdateProps(piece.instance.piece.userEditOperations))

Then use {enableUserEdits && hasAnyEditPropsItem && <hr />} before the menu items.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{enableUserEdits && (
<>
<hr />
<MenuItem onClick={() => onEditProps({ type: 'segment', elementId: part.instance.segmentId })}>
<span>{t('Edit Segment Properties')}</span>
</MenuItem>
<MenuItem onClick={() => onEditProps({ type: 'part', elementId: part.instance.part._id })}>
<span>{t('Edit Part Properties')}</span>
</MenuItem>
{piece && piece.instance.piece.userEditProperties && (
<MenuItem onClick={() => onEditProps({ type: 'piece', elementId: piece.instance.piece._id })}>
<span>{t('Edit Piece Properties')}</span>
</MenuItem>
)}
{segment &&
segment.userEditProperties &&
doesItemSupportUserEditUpdateProps(segment.userEditOperations) && (
<MenuItem onClick={() => onEditProps({ type: 'segment', elementId: part.instance.segmentId })}>
<span>{t('Edit Segment Properties')}</span>
</MenuItem>
)}
{part &&
part.instance.part.userEditProperties &&
doesItemSupportUserEditUpdateProps(part.instance.part.userEditOperations) && (
<MenuItem onClick={() => onEditProps({ type: 'part', elementId: part.instance.part._id })}>
<span>{t('Edit Part Properties')}</span>
</MenuItem>
)}
{piece &&
piece.instance.piece.userEditProperties &&
doesItemSupportUserEditUpdateProps(piece.instance.piece.userEditOperations) && (
<MenuItem onClick={() => onEditProps({ type: 'piece', elementId: piece.instance.piece._id })}>
<span>{t('Edit Piece Properties')}</span>
</MenuItem>
)}
</>
{enableUserEdits && (
<>
{(segment?.userEditProperties && doesItemSupportUserEditUpdateProps(segment.userEditOperations)) ||
(part?.instance.part.userEditProperties && doesItemSupportUserEditUpdateProps(part.instance.part.userEditOperations)) ||
(piece?.instance.piece.userEditProperties && doesItemSupportUserEditUpdateProps(piece.instance.piece.userEditOperations)) ? (
<hr />
) : null}
{segment &&
segment.userEditProperties &&
doesItemSupportUserEditUpdateProps(segment.userEditOperations) && (
<MenuItem onClick={() => onEditProps({ type: 'segment', elementId: part.instance.segmentId })}>
<span>{t('Edit Segment Properties')}</span>
</MenuItem>
)}
{part &&
part.instance.part.userEditProperties &&
doesItemSupportUserEditUpdateProps(part.instance.part.userEditOperations) && (
<MenuItem onClick={() => onEditProps({ type: 'part', elementId: part.instance.part._id })}>
<span>{t('Edit Part Properties')}</span>
</MenuItem>
)}
{piece &&
piece.instance.piece.userEditProperties &&
doesItemSupportUserEditUpdateProps(piece.instance.piece.userEditOperations) && (
<MenuItem onClick={() => onEditProps({ type: 'piece', elementId: piece.instance.piece._id })}>
<span>{t('Edit Piece Properties')}</span>
</MenuItem>
)}
</>
)}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/webui/src/client/ui/SegmentTimeline/SegmentContextMenu.tsx` around
lines 313 - 337, The <hr /> can render alone when enableUserEdits is true but
none of the edit menu items are eligible; update the SegmentContextMenu render
so the divider is only output when at least one edit item will render by either
moving the <hr /> inside the same conditional checks that render the MenuItem(s)
or by computing a boolean like hasAnyEditPropsItem using segment, part, piece
and doesItemSupportUserEditUpdateProps (checking the same properties used for
the MenuItem rendering) and then rendering {enableUserEdits &&
hasAnyEditPropsItem && <hr />} before the MenuItem blocks; keep existing
onEditProps calls and the checks on segment.userEditProperties,
part.instance.part.userEditProperties, piece.instance.piece.userEditProperties
and their corresponding doesItemSupportUserEditUpdateProps calls.

)}
</>
Expand Down
Loading