Skip to content

Commit 66dfa12

Browse files
committed
fix(workflow): prevent duplicate lock notifications, no-op guard, fix orphaned JSDoc
1 parent 99d9239 commit 66dfa12

File tree

2 files changed

+40
-21
lines changed

2 files changed

+40
-21
lines changed

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/utils/block-protection-utils.ts

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -58,16 +58,32 @@ export function isEdgeProtected(
5858
* @param blocks - Record of all blocks in the workflow
5959
* @returns Result containing deletable IDs, protected IDs, and whether all are protected
6060
*/
61+
export function filterProtectedBlocks(
62+
blockIds: string[],
63+
blocks: Record<string, BlockState>
64+
): FilterProtectedBlocksResult {
65+
const protectedIds = blockIds.filter((id) => isBlockProtected(id, blocks))
66+
const deletableIds = blockIds.filter((id) => !protectedIds.includes(id))
67+
68+
return {
69+
deletableIds,
70+
protectedIds,
71+
allProtected: protectedIds.length === blockIds.length && blockIds.length > 0,
72+
}
73+
}
74+
6175
/**
6276
* Returns block IDs ordered so that `batchToggleLocked` will target the desired state.
6377
*
6478
* `batchToggleLocked` determines its target locked state from `!firstBlock.locked`.
6579
* When `targetLocked` is true (lock all), an unlocked block must come first.
6680
* When `targetLocked` is false (unlock all), a locked block must come first.
6781
*
82+
* Returns an empty array when there are no blocks or all blocks already match `targetLocked`.
83+
*
6884
* @param blocks - Record of all blocks in the workflow
6985
* @param targetLocked - The desired locked state for all blocks
70-
* @returns Sorted block IDs, or empty array if there are no blocks
86+
* @returns Sorted block IDs, or empty array if no toggle is needed
7187
*/
7288
export function getWorkflowLockToggleIds(
7389
blocks: Record<string, BlockState>,
@@ -76,6 +92,10 @@ export function getWorkflowLockToggleIds(
7692
const ids = Object.keys(blocks)
7793
if (ids.length === 0) return []
7894

95+
// No-op if all blocks already match the desired state
96+
const allMatch = Object.values(blocks).every((b) => Boolean(b.locked) === targetLocked)
97+
if (allMatch) return []
98+
7999
ids.sort((a, b) => {
80100
const aVal = blocks[a].locked ? 1 : 0
81101
const bVal = blocks[b].locked ? 1 : 0
@@ -86,17 +106,3 @@ export function getWorkflowLockToggleIds(
86106

87107
return ids
88108
}
89-
90-
export function filterProtectedBlocks(
91-
blockIds: string[],
92-
blocks: Record<string, BlockState>
93-
): FilterProtectedBlocksResult {
94-
const protectedIds = blockIds.filter((id) => isBlockProtected(id, blocks))
95-
const deletableIds = blockIds.filter((id) => !protectedIds.includes(id))
96-
97-
return {
98-
deletableIds,
99-
protectedIds,
100-
allProtected: protectedIds.length === blockIds.length && blockIds.length > 0,
101-
}
102-
}

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1200,19 +1200,32 @@ const WorkflowContent = React.memo(() => {
12001200
}
12011201
}, [])
12021202

1203-
// Reset notification when switching workflows so it recreates for the new workflow
1204-
const prevWorkflowIdRef = useRef(activeWorkflowId)
1203+
// Clear persisted lock notifications on mount/workflow change (prevents duplicates after reload)
1204+
useEffect(() => {
1205+
// Reset ref so the main effect creates a fresh notification for the new workflow
1206+
clearLockNotification()
1207+
1208+
if (!activeWorkflowId) return
1209+
const store = useNotificationStore.getState()
1210+
const stale = store.notifications.filter(
1211+
(n) =>
1212+
n.workflowId === activeWorkflowId &&
1213+
(n.action?.type === 'unlock-workflow' || n.message.startsWith('This workflow is locked'))
1214+
)
1215+
for (const n of stale) {
1216+
store.removeNotification(n.id)
1217+
}
1218+
}, [activeWorkflowId, clearLockNotification])
1219+
12051220
const prevCanAdminRef = useRef(effectivePermissions.canAdmin)
12061221
useEffect(() => {
12071222
if (!isWorkflowReady) return
12081223

1209-
const workflowChanged = prevWorkflowIdRef.current !== activeWorkflowId
12101224
const canAdminChanged = prevCanAdminRef.current !== effectivePermissions.canAdmin
1211-
prevWorkflowIdRef.current = activeWorkflowId
12121225
prevCanAdminRef.current = effectivePermissions.canAdmin
12131226

1214-
// Clear stale notification when workflow or admin status changes
1215-
if ((workflowChanged || canAdminChanged) && lockNotificationIdRef.current) {
1227+
// Clear stale notification when admin status changes so it recreates with correct message
1228+
if (canAdminChanged) {
12161229
clearLockNotification()
12171230
}
12181231

0 commit comments

Comments
 (0)