-
Notifications
You must be signed in to change notification settings - Fork 1
fix(visual-builder): claim the field lock from the empty-block add #641
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -7,6 +7,9 @@ import { ISchemaFieldMap } from "../utils/types/index.types"; | |||||||||||||||||||
| import { VisualBuilderPostMessageEvents } from "../utils/types/postMessage.types"; | ||||||||||||||||||||
| import React from "preact/compat"; | ||||||||||||||||||||
| import { startCase, toLower } from "lodash-es"; | ||||||||||||||||||||
| import { getDOMEditStack } from "../utils/getCsDataOfElement"; | ||||||||||||||||||||
| import { getPeerLockForField } from "../utils/fieldLockIndicator"; | ||||||||||||||||||||
| import { DATA_CSLP_ATTR_SELECTOR } from "../utils/constants"; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| interface EmptyBlockProps { | ||||||||||||||||||||
| details: { | ||||||||||||||||||||
|
|
@@ -20,14 +23,44 @@ export function EmptyBlock(props: EmptyBlockProps): JSX.Element { | |||||||||||||||||||
|
|
||||||||||||||||||||
| const blockParentName = details.fieldSchema.display_name; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| async function sendAddInstanceEvent() { | ||||||||||||||||||||
| await visualBuilderPostMessage?.send( | ||||||||||||||||||||
| VisualBuilderPostMessageEvents.ADD_INSTANCE, | ||||||||||||||||||||
| { | ||||||||||||||||||||
| fieldMetadata: details.fieldMetadata, | ||||||||||||||||||||
| index: 0, | ||||||||||||||||||||
| } | ||||||||||||||||||||
| ); | ||||||||||||||||||||
| async function sendAddInstanceEvent( | ||||||||||||||||||||
| event: JSX.TargetedMouseEvent<HTMLButtonElement> | ||||||||||||||||||||
| ) { | ||||||||||||||||||||
| // A peer holds this field: adding would edit through their lock, the same | ||||||||||||||||||||
| // no-op a click on a peer-locked field gets in the click listener. | ||||||||||||||||||||
| if (getPeerLockForField(details.fieldMetadata)) return; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // Resolve the field by its cslp, not the button's DOM position: a portal | ||||||||||||||||||||
| // render would yield an empty stack and silently skip the lock claim. | ||||||||||||||||||||
| const fieldElement = | ||||||||||||||||||||
| document.querySelector( | ||||||||||||||||||||
| `[${DATA_CSLP_ATTR_SELECTOR}="${details.fieldMetadata.cslpValue}"]` | ||||||||||||||||||||
| ) ?? event.currentTarget; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // The empty-state add never selects the field, so nothing else claims the | ||||||||||||||||||||
| // lock. Fire and forget: the parent does not await the claim either. | ||||||||||||||||||||
| const DOMEditStack = getDOMEditStack(fieldElement); | ||||||||||||||||||||
| // An empty stack reads as a deselect on the parent and would RELEASE the lock. | ||||||||||||||||||||
| if (DOMEditStack.length) { | ||||||||||||||||||||
| visualBuilderPostMessage?.send( | ||||||||||||||||||||
| VisualBuilderPostMessageEvents.FOCUS_FIELD, | ||||||||||||||||||||
| { DOMEditStack } | ||||||||||||||||||||
| ); | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| try { | ||||||||||||||||||||
| await visualBuilderPostMessage?.send( | ||||||||||||||||||||
| VisualBuilderPostMessageEvents.ADD_INSTANCE, | ||||||||||||||||||||
| { | ||||||||||||||||||||
| fieldMetadata: details.fieldMetadata, | ||||||||||||||||||||
| index: 0, | ||||||||||||||||||||
| } | ||||||||||||||||||||
| ); | ||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||
| console.error("Visual Builder: Failed to add instance", error); | ||||||||||||||||||||
| return; | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| observeParentAndFocusNewInstance({ | ||||||||||||||||||||
|
Comment on lines
+59
to
64
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Catch swallows failure but control falls through to Either
Suggested change
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch, fixed in 07a1c65. The catch returns now, so a failed add no longer leaves an observer waiting on an instance that never arrives. I went with the return rather than moving the observe inside the try, so the success path still reads top to bottom. |
||||||||||||||||||||
| parentCslp: details.fieldMetadata.cslpValue, | ||||||||||||||||||||
| index: 0, | ||||||||||||||||||||
|
|
@@ -67,7 +100,7 @@ export function EmptyBlock(props: EmptyBlockProps): JSX.Element { | |||||||||||||||||||
| "visual-builder__empty-block-add-button" | ||||||||||||||||||||
| ] | ||||||||||||||||||||
| )} | ||||||||||||||||||||
| onClick={() => sendAddInstanceEvent()} | ||||||||||||||||||||
| onClick={sendAddInstanceEvent} | ||||||||||||||||||||
| type="button" | ||||||||||||||||||||
| data-testid="visual-builder__empty-block-add-button" | ||||||||||||||||||||
| > | ||||||||||||||||||||
|
|
||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.