@atlaskit/editor-plugin-synced-block 10.4.7 → 10.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -13,6 +13,7 @@ import { convertPMNodesToSyncBlockNodes, rebaseTransaction } from '@atlaskit/edi
13
13
  import { getSourceProductFromResourceIdSafe } from '@atlaskit/editor-synced-block-provider/utils';
14
14
  import { fg } from '@atlaskit/platform-feature-flags';
15
15
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
16
+ import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
16
17
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
17
18
  import { bodiedSyncBlockNodeView, bodiedSyncBlockNodeViewOld } from '../nodeviews/bodiedSyncedBlock';
18
19
  import { SyncBlock as SyncBlockView } from '../nodeviews/syncedBlock';
@@ -37,6 +38,7 @@ export const syncedBlockPluginKey = new PluginKey('syncedBlockPlugin');
37
38
  * `platform_editor_blocks_patch_4`.
38
39
  */
39
40
  export const deleteMechanismMetaKey = new PluginKey('syncedBlockDeleteMechanism');
41
+ const syncedBlockPromptedFeedbackMetaKey = new PluginKey('syncedBlockPromptedFeedback');
40
42
 
41
43
  /**
42
44
  * Creation-type signals set by {@link createSyncedBlock} on the creating
@@ -121,7 +123,7 @@ const getDeleteReason = tr => {
121
123
  };
122
124
 
123
125
  /** Narrows the history plugin meta, matching the editor-plugin-card pattern. */
124
- const isHistoryMeta = meta => typeof meta === 'object' && meta !== null && 'redo' in meta;
126
+ const isHistoryMeta = meta => typeof meta === 'object' && meta !== null && 'redo' in meta && typeof meta.redo === 'boolean';
125
127
 
126
128
  /**
127
129
  * Derive how a source bodiedSyncBlock removal was performed, for the `mechanism`
@@ -145,9 +147,16 @@ const isHistoryMeta = meta => typeof meta === 'object' && meta !== null && 'redo
145
147
  * selected when the edit was made.
146
148
  */
147
149
  export const getDeleteMechanism = (tr, state) => {
148
- var _selection$node;
150
+ var _selection$node, _selection$node2;
151
+ // Keep the legacy analytics classification unchanged while activation is off.
152
+ // The stricter history, structural-step, and reference-selection handling is
153
+ // only needed by the prompted feedback rollout.
154
+ const isSyncBlockActivationEnabled = expValEqualsNoExposure('platform_editor_sync_block_activation', 'isEnabled', true);
149
155
  const historyMeta = tr.getMeta(pmHistoryPluginKey);
150
156
  if (historyMeta) {
157
+ if (isSyncBlockActivationEnabled && !isHistoryMeta(historyMeta)) {
158
+ return 'other';
159
+ }
151
160
  return isHistoryMeta(historyMeta) && historyMeta.redo ? 'redo' : 'undo';
152
161
  }
153
162
 
@@ -156,16 +165,26 @@ export const getDeleteMechanism = (tr, state) => {
156
165
  if (tr.getMeta(deleteMechanismMetaKey) === 'deleteButton') {
157
166
  return 'deleteButton';
158
167
  }
168
+ const hasStructuralStep = isSyncBlockActivationEnabled && tr.steps.some(step => step instanceof ReplaceAroundStep);
159
169
  const hasReplaceStep = tr.steps.some(step => step instanceof ReplaceStep);
160
- if (!hasReplaceStep) {
170
+ if (hasStructuralStep || !hasReplaceStep) {
161
171
  return 'other';
162
172
  }
163
173
  const {
164
174
  selection
165
175
  } = state;
166
- const isNodeSelected = selection instanceof NodeSelection && ((_selection$node = selection.node) === null || _selection$node === void 0 ? void 0 : _selection$node.type.name) === 'bodiedSyncBlock';
176
+ const isNodeSelected = selection instanceof NodeSelection && (((_selection$node = selection.node) === null || _selection$node === void 0 ? void 0 : _selection$node.type.name) === 'bodiedSyncBlock' || isSyncBlockActivationEnabled && ((_selection$node2 = selection.node) === null || _selection$node2 === void 0 ? void 0 : _selection$node2.type.name) === 'syncBlock');
167
177
  return isNodeSelected ? 'selectionReplaced' : 'keyboardDelete';
168
178
  };
179
+ export const getPromptedFeedbackEntryPoint = mechanism => {
180
+ if (mechanism === 'undo') {
181
+ return 'prompted-undo';
182
+ }
183
+ if (mechanism === 'deleteButton' || mechanism === 'keyboardDelete' || mechanism === 'selectionReplaced') {
184
+ return 'prompted-delete';
185
+ }
186
+ return undefined;
187
+ };
169
188
  /**
170
189
  * True when a source `bodiedSyncBlock` holds user content — i.e. not just the
171
190
  * single empty paragraph it is created with (used to detect empty→content).
@@ -211,6 +230,7 @@ const filterTransactionOnline = ({
211
230
  syncBlockStore,
212
231
  api,
213
232
  confirmationTransactionRef,
233
+ ctx,
214
234
  bodiedSyncBlockRemoved,
215
235
  bodiedSyncBlockAdded,
216
236
  extensionFlagShown
@@ -241,6 +261,19 @@ const filterTransactionOnline = ({
241
261
  // dedicated `cross_product_paste` event subject.
242
262
  const isPaste = Boolean((_tr$getMeta = tr.getMeta('paste')) !== null && _tr$getMeta !== void 0 ? _tr$getMeta : tr.getMeta('uiEvent') === 'paste');
243
263
  const isPasteOrDrop = isPaste || tr.getMeta('uiEvent') === 'drop';
264
+ const hasRemovedSyncBlock = syncBlockRemoved.length > 0 || bodiedSyncBlockRemoved.length > 0;
265
+ const isCutRemoval = hasRemovedSyncBlock && ctx.consumeCutRemoval();
266
+ const canPromptForRemoval = Boolean(ctx.onGiveFeedback) && hasRemovedSyncBlock && !isDirtyTransaction(tr) && !isCutRemoval && !isPasteOrDrop;
267
+ const promptedFeedbackEntryPoint = canPromptForRemoval ? getPromptedFeedbackEntryPoint(getDeleteMechanism(tr, state)) : undefined;
268
+ const entryPoint = promptedFeedbackEntryPoint && expValEqualsNoExposure('platform_editor_sync_block_activation', 'isEnabled', true) ? promptedFeedbackEntryPoint : undefined;
269
+ if (entryPoint && syncBlockRemoved.length > 0 && bodiedSyncBlockRemoved.length === 0) {
270
+ tr.setMeta(syncedBlockPromptedFeedbackMetaKey, {
271
+ context: {
272
+ blockType: 'reference',
273
+ entryPoint
274
+ }
275
+ });
276
+ }
244
277
  syncBlockAdded.forEach(syncBlock => {
245
278
  var _api$analytics2, _api$analytics2$actio;
246
279
  api === null || api === void 0 ? void 0 : (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 ? void 0 : (_api$analytics2$actio = _api$analytics2.actions) === null || _api$analytics2$actio === void 0 ? void 0 : _api$analytics2$actio.fireAnalyticsEvent({
@@ -268,7 +301,22 @@ const filterTransactionOnline = ({
268
301
  }
269
302
  // Only derive the mechanism behind the gate — discarded gate-off.
270
303
  const mechanism = fg('platform_editor_blocks_patch_4') ? getDeleteMechanism(tr, state) : undefined;
271
- return handleBodiedSyncBlockRemoval(bodiedSyncBlockRemoved, syncBlockStore, api, confirmationTransactionRef, getDeleteReason(tr), mechanism);
304
+ const feedbackContext = entryPoint && getDeleteReason(tr) !== 'source-block-unsynced' ? {
305
+ blockType: 'source',
306
+ entryPoint
307
+ } : undefined;
308
+ if (feedbackContext) {
309
+ const sourceAttempt = ctx.queueSourceFeedback(feedbackContext);
310
+ return handleBodiedSyncBlockRemoval(bodiedSyncBlockRemoved, syncBlockStore, api, confirmationTransactionRef, getDeleteReason(tr), mechanism, {
311
+ onDeleteCompleted: success => ctx.completeSourceDeletion(sourceAttempt, success),
312
+ onDeleteTransaction: deleteTr => deleteTr.setMeta(syncedBlockPromptedFeedbackMetaKey, {
313
+ context: feedbackContext,
314
+ sourceAttempt
315
+ }),
316
+ onDestroy: () => ctx.clearSourceFeedback(sourceAttempt)
317
+ });
318
+ }
319
+ return handleBodiedSyncBlockRemoval(bodiedSyncBlockRemoved, syncBlockStore, api, confirmationTransactionRef, getDeleteReason(tr), mechanism, undefined);
272
320
  }
273
321
  if (bodiedSyncBlockAdded.length > 0) {
274
322
  if (tr.getMeta(pmHistoryPluginKey)) {
@@ -391,14 +439,17 @@ const buildStatusDecorations = (doc, syncBlockStore, isOffline, isViewMode, isDr
391
439
  * is explicitly scoped to a single plugin instance.
392
440
  */
393
441
  class SyncedBlockPluginContext {
394
- constructor() {
442
+ constructor(onGiveFeedback) {
395
443
  _defineProperty(this, "confirmationTransactionRef", {
396
444
  current: undefined
397
445
  });
446
+ _defineProperty(this, "_cutRemovalPending", false);
447
+ _defineProperty(this, "cutRemovalGeneration", 0);
398
448
  _defineProperty(this, "_isCopyEvent", false);
399
449
  _defineProperty(this, "_isCutEvent", false);
400
450
  _defineProperty(this, "unpublishedFlagShown", new Set());
401
451
  _defineProperty(this, "extensionFlagShown", new Set());
452
+ this.onGiveFeedback = onGiveFeedback;
402
453
  }
403
454
  get isCopyEvent() {
404
455
  return this._isCopyEvent;
@@ -419,6 +470,86 @@ class SyncedBlockPluginContext {
419
470
  this._isCutEvent = false;
420
471
  return was;
421
472
  }
473
+ markCutRemovalPending() {
474
+ this._cutRemovalPending = true;
475
+ const generation = ++this.cutRemovalGeneration;
476
+ queueMicrotask(() => {
477
+ if (this.cutRemovalGeneration === generation) {
478
+ this._cutRemovalPending = false;
479
+ }
480
+ });
481
+ }
482
+ consumeCutRemoval() {
483
+ const wasPending = this._cutRemovalPending;
484
+ this._cutRemovalPending = false;
485
+ this.cutRemovalGeneration += 1;
486
+ return wasPending;
487
+ }
488
+ queueSourceFeedback(context) {
489
+ const sourceAttempt = Symbol('syncedBlockSourceFeedbackAttempt');
490
+ this.pendingSourceFeedback = {
491
+ context,
492
+ deletionSucceeded: false,
493
+ sourceAttempt,
494
+ transactionApplied: false
495
+ };
496
+ return sourceAttempt;
497
+ }
498
+ completeSourceDeletion(sourceAttempt, success) {
499
+ var _this$pendingSourceFe;
500
+ if (((_this$pendingSourceFe = this.pendingSourceFeedback) === null || _this$pendingSourceFe === void 0 ? void 0 : _this$pendingSourceFe.sourceAttempt) !== sourceAttempt) {
501
+ return;
502
+ }
503
+ if (!success) {
504
+ // Keep the attempt pending because retryDeletion reuses these callbacks.
505
+ // Dismissal or cancellation clears it through the destroy callback.
506
+ return;
507
+ }
508
+ this.pendingSourceFeedback.deletionSucceeded = true;
509
+ this.flushSourceFeedback(sourceAttempt);
510
+ }
511
+ clearSourceFeedback(sourceAttempt) {
512
+ var _this$pendingSourceFe2;
513
+ if (((_this$pendingSourceFe2 = this.pendingSourceFeedback) === null || _this$pendingSourceFe2 === void 0 ? void 0 : _this$pendingSourceFe2.sourceAttempt) === sourceAttempt) {
514
+ this.pendingSourceFeedback = undefined;
515
+ }
516
+ }
517
+ handleAppliedFeedback({
518
+ context,
519
+ sourceAttempt
520
+ }) {
521
+ var _this$pendingSourceFe3;
522
+ if (context.blockType === 'source' && sourceAttempt && ((_this$pendingSourceFe3 = this.pendingSourceFeedback) === null || _this$pendingSourceFe3 === void 0 ? void 0 : _this$pendingSourceFe3.sourceAttempt) === sourceAttempt) {
523
+ this.pendingSourceFeedback.transactionApplied = true;
524
+ this.flushSourceFeedback(sourceAttempt);
525
+ return;
526
+ }
527
+ if (context.blockType === 'reference') {
528
+ this.invokeFeedback(context);
529
+ }
530
+ }
531
+ flushSourceFeedback(sourceAttempt) {
532
+ var _this$pendingSourceFe4, _this$pendingSourceFe5;
533
+ if (((_this$pendingSourceFe4 = this.pendingSourceFeedback) === null || _this$pendingSourceFe4 === void 0 ? void 0 : _this$pendingSourceFe4.sourceAttempt) === sourceAttempt && (_this$pendingSourceFe5 = this.pendingSourceFeedback) !== null && _this$pendingSourceFe5 !== void 0 && _this$pendingSourceFe5.deletionSucceeded && this.pendingSourceFeedback.transactionApplied) {
534
+ const {
535
+ context
536
+ } = this.pendingSourceFeedback;
537
+ this.pendingSourceFeedback = undefined;
538
+ this.invokeFeedback(context);
539
+ }
540
+ }
541
+ invokeFeedback(context) {
542
+ deferDispatch(() => {
543
+ try {
544
+ var _this$onGiveFeedback;
545
+ const feedbackResult = (_this$onGiveFeedback = this.onGiveFeedback) === null || _this$onGiveFeedback === void 0 ? void 0 : _this$onGiveFeedback.call(this, context);
546
+ void Promise.resolve(feedbackResult).catch(() => {});
547
+ } catch {
548
+ // Product feedback collectors are optional UI. Their failures must
549
+ // never affect the already-applied editor transaction.
550
+ }
551
+ });
552
+ }
422
553
  }
423
554
  export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api) => {
424
555
  const {
@@ -430,7 +561,7 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
430
561
  // and avoids ~10 redundant Statsig SDK evaluations per keystroke in hot paths
431
562
  // (apply, filterTransaction, appendTransaction, decorations).
432
563
  const isPerfExperimentOn = expValEquals('editor_synced_block_perf', 'isEnabled', true);
433
- const ctx = new SyncedBlockPluginContext();
564
+ const ctx = new SyncedBlockPluginContext(options === null || options === void 0 ? void 0 : options.onGiveFeedback);
434
565
  const confirmationTransactionRef = ctx.confirmationTransactionRef;
435
566
  const unpublishedFlagShown = ctx.unpublishedFlagShown;
436
567
  const extensionFlagShown = ctx.extensionFlagShown;
@@ -781,7 +912,7 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
781
912
  return false;
782
913
  },
783
914
  cut: () => {
784
- if (fg('platform_synced_block_patch_13')) {
915
+ if (fg('platform_synced_block_patch_13') || Boolean(options === null || options === void 0 ? void 0 : options.onGiveFeedback) && expValEqualsNoExposure('platform_editor_sync_block_activation', 'isEnabled', true)) {
785
916
  ctx.consumeCutEvent();
786
917
  ctx.markCutEvent();
787
918
  }
@@ -822,20 +953,40 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
822
953
  } = state;
823
954
  const isCopy = ctx.consumeCopyEvent();
824
955
  const isSyncedBlockPatch13Enabled = fg('platform_synced_block_patch_13');
825
- const isCut = isSyncedBlockPatch13Enabled && ctx.consumeCutEvent();
826
- if (!syncBlockStore || !isCopy && !isCut) {
956
+ const wasCut = ctx.consumeCutEvent();
957
+ const isCut = isSyncedBlockPatch13Enabled && wasCut;
958
+ const isPromptedFeedbackCut = Boolean(syncBlockStore && (options === null || options === void 0 ? void 0 : options.onGiveFeedback) && wasCut) && expValEqualsNoExposure('platform_editor_sync_block_activation', 'isEnabled', true);
959
+ if (!syncBlockStore || !isCopy && !isCut && !isPromptedFeedbackCut) {
827
960
  return slice;
828
961
  }
829
- return mapSlice(slice, node => {
962
+ if (isPromptedFeedbackCut && !isCut) {
963
+ let containsCutSyncBlock = false;
964
+ mapSlice(slice, node => {
965
+ if (syncBlockStore.referenceManager.isReferenceBlock(node) || node.type.name === 'bodiedSyncBlock' && sliceFullyContainsNode(slice, node)) {
966
+ containsCutSyncBlock = true;
967
+ }
968
+ return node;
969
+ });
970
+ if (containsCutSyncBlock) {
971
+ ctx.markCutRemovalPending();
972
+ }
973
+ return slice;
974
+ }
975
+ let containsCutSyncBlock = false;
976
+ const transformedSlice = mapSlice(slice, node => {
830
977
  if (syncBlockStore.referenceManager.isReferenceBlock(node)) {
831
978
  var _syncBlockStore$refer, _syncBlockStore$refer2;
832
979
  if (isCut) {
980
+ containsCutSyncBlock = isPromptedFeedbackCut;
833
981
  return node;
834
982
  }
835
983
  showCopiedFlag(api, options === null || options === void 0 ? void 0 : options.__livePage, ((_syncBlockStore$refer = syncBlockStore.referenceManager.getFromCache(node.attrs.resourceId)) === null || _syncBlockStore$refer === void 0 ? void 0 : (_syncBlockStore$refer2 = _syncBlockStore$refer.data) === null || _syncBlockStore$refer2 === void 0 ? void 0 : _syncBlockStore$refer2.status) === 'unpublished', getSourceProductFromResourceIdSafe(node.attrs.resourceId));
836
984
  return node;
837
985
  }
838
986
  if (node.type.name === 'bodiedSyncBlock' && node.attrs.resourceId) {
987
+ if (isPromptedFeedbackCut && isCut && sliceFullyContainsNode(slice, node)) {
988
+ containsCutSyncBlock = true;
989
+ }
839
990
  // if we only selected part of the bodied sync block content,
840
991
  // remove the sync block node and only keep the content
841
992
  if (!sliceFullyContainsNode(slice, node)) {
@@ -860,6 +1011,10 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
860
1011
  }
861
1012
  return node;
862
1013
  });
1014
+ if (containsCutSyncBlock) {
1015
+ ctx.markCutRemovalPending();
1016
+ }
1017
+ return transformedSlice;
863
1018
  }
864
1019
  },
865
1020
  filterTransaction: (tr, state) => {
@@ -939,6 +1094,7 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
939
1094
  syncBlockStore,
940
1095
  api,
941
1096
  confirmationTransactionRef,
1097
+ ctx,
942
1098
  bodiedSyncBlockRemoved,
943
1099
  bodiedSyncBlockAdded,
944
1100
  extensionFlagShown
@@ -962,6 +1118,17 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
962
1118
  if (viewMode === 'view') {
963
1119
  return null;
964
1120
  }
1121
+ if (options !== null && options !== void 0 && options.onGiveFeedback) {
1122
+ const feedbackContexts = trs.flatMap(tr => {
1123
+ const feedbackContext = tr.getMeta(syncedBlockPromptedFeedbackMetaKey);
1124
+ return feedbackContext ? [feedbackContext] : [];
1125
+ });
1126
+ if (feedbackContexts.length > 0 && expValEqualsNoExposure('platform_editor_sync_block_activation', 'isEnabled', true)) {
1127
+ feedbackContexts.forEach(feedbackContext => {
1128
+ ctx.handleAppliedFeedback(feedbackContext);
1129
+ });
1130
+ }
1131
+ }
965
1132
 
966
1133
  // Update source sync block cache for user-initiated changes only.
967
1134
  // Cache updates are handled here instead of in the nodeview update() so we
@@ -21,7 +21,7 @@ const onDismissed = syncBlockStore => tr => {
21
21
  bodiedSyncBlockDeletionStatus: 'none'
22
22
  });
23
23
  };
24
- export const handleBodiedSyncBlockRemoval = (bodiedSyncBlockRemoved, syncBlockStore, api, confirmationTransactionRef, deletionReason, mechanism) => {
24
+ export const handleBodiedSyncBlockRemoval = (bodiedSyncBlockRemoved, syncBlockStore, api, confirmationTransactionRef, deletionReason, mechanism, feedbackCallbacks) => {
25
25
  // Clear potential old pending deletion to retreat the deletion as first attempt
26
26
  syncBlockStore.sourceManager.clearPendingDeletion();
27
27
 
@@ -54,6 +54,7 @@ export const handleBodiedSyncBlockRemoval = (bodiedSyncBlockRemoved, syncBlockSt
54
54
  return null;
55
55
  }
56
56
  recomputedTr.setMeta('isConfirmedSyncBlockDeletion', true);
57
+ feedbackCallbacks === null || feedbackCallbacks === void 0 ? void 0 : feedbackCallbacks.onDeleteTransaction(recomputedTr);
57
58
  if (!recomputedTr.getMeta(pmHistoryPluginKey)) {
58
59
  // bodiedSyncBlock deletion is expected to be permanent (cannot undo)
59
60
  // For a normal deletion (not triggered by undo), remove it from history so that it cannot be undone
@@ -69,6 +70,7 @@ export const handleBodiedSyncBlockRemoval = (bodiedSyncBlockRemoved, syncBlockSt
69
70
  }
70
71
  api === null || api === void 0 ? void 0 : (_api$core3 = api.core) === null || _api$core3 === void 0 ? void 0 : _api$core3.actions.execute(() => {
71
72
  const trToDispatch = confirmationTransaction.setMeta('isConfirmedSyncBlockDeletion', true);
73
+ feedbackCallbacks === null || feedbackCallbacks === void 0 ? void 0 : feedbackCallbacks.onDeleteTransaction(trToDispatch);
72
74
  if (!trToDispatch.getMeta(pmHistoryPluginKey)) {
73
75
  // bodiedSyncBlock deletion is expected to be permanent (cannot undo)
74
76
  // For a normal deletion (not triggered by undo), remove it from history so that it cannot be undone
@@ -78,6 +80,7 @@ export const handleBodiedSyncBlockRemoval = (bodiedSyncBlockRemoved, syncBlockSt
78
80
  });
79
81
  }, success => {
80
82
  var _api$core4;
83
+ feedbackCallbacks === null || feedbackCallbacks === void 0 ? void 0 : feedbackCallbacks.onDeleteCompleted(success);
81
84
  api === null || api === void 0 ? void 0 : (_api$core4 = api.core) === null || _api$core4 === void 0 ? void 0 : _api$core4.actions.execute(({
82
85
  tr
83
86
  }) => {
@@ -107,6 +110,7 @@ export const handleBodiedSyncBlockRemoval = (bodiedSyncBlockRemoved, syncBlockSt
107
110
  });
108
111
  }, () => {
109
112
  confirmationTransactionRef.current = undefined;
113
+ feedbackCallbacks === null || feedbackCallbacks === void 0 ? void 0 : feedbackCallbacks.onDestroy();
110
114
  }, mechanism);
111
115
  return false;
112
116
  };
@@ -26,6 +26,7 @@ export const getToolbarConfig = (state, intl, api, syncBlockStore, isLivePage, o
26
26
  if (syncBlockStore.sourceManager.isPendingCreation(syncBlockObject.node.attrs.resourceId)) {
27
27
  return;
28
28
  }
29
+ const isSyncBlockActivationEnabled = expValEquals('platform_editor_sync_block_activation', 'isEnabled', true);
29
30
  const syncBlockInstance = syncBlockStore.referenceManager.getFromCache(syncBlockObject.node.attrs.resourceId);
30
31
  const isUnsyncedBlock = (syncBlockInstance === null || syncBlockInstance === void 0 ? void 0 : (_syncBlockInstance$er = syncBlockInstance.error) === null || _syncBlockInstance$er === void 0 ? void 0 : _syncBlockInstance$er.type) === SyncBlockError.NotFound;
31
32
  const isErroredBlock = syncBlockInstance === null || syncBlockInstance === void 0 ? void 0 : syncBlockInstance.error;
@@ -53,7 +54,6 @@ export const getToolbarConfig = (state, intl, api, syncBlockStore, isLivePage, o
53
54
  onBlur: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(nodeType, false, className)
54
55
  });
55
56
  const items = [];
56
- const isSyncBlockActivationEnabled = expValEquals('platform_editor_sync_block_activation', 'isEnabled', true);
57
57
  if (isUnsyncedBlock) {
58
58
  const deleteButton = {
59
59
  type: 'button',
@@ -192,7 +192,8 @@ export const getToolbarConfig = (state, intl, api, syncBlockStore, isLivePage, o
192
192
  tr
193
193
  }) => tr.setSelection(new GapCursorSelection(tr.doc.resolve(syncBlockObject.pos + syncBlockObject.node.nodeSize), Side.RIGHT)));
194
194
  onGiveFeedback({
195
- blockType: isBodiedSyncBlock ? 'source' : 'reference'
195
+ blockType: isBodiedSyncBlock ? 'source' : 'reference',
196
+ entryPoint: 'overflow-menu'
196
197
  });
197
198
  return true;
198
199
  }
@@ -9,6 +9,7 @@ import { NodeSelection, Selection, TextSelection } from '@atlaskit/editor-prosem
9
9
  import { findSelectedNodeOfType, removeParentNodeOfType, removeSelectedNode, safeInsert } from '@atlaskit/editor-prosemirror/utils';
10
10
  import { getSourceProductFromResourceIdSafe } from '@atlaskit/editor-synced-block-provider/utils';
11
11
  import { fg } from '@atlaskit/platform-feature-flags';
12
+ import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
12
13
  import { creationMetaKey, deleteMechanismMetaKey, syncedBlockPluginKey } from '../pm-plugins/main';
13
14
  import { canBeConvertedToSyncBlock, deferDispatch, findSyncBlock, findSyncBlockOrBodiedSyncBlock, isBodiedSyncBlockNode } from '../pm-plugins/utils/utils';
14
15
  import { FLAG_ID } from '../types';
@@ -348,7 +349,11 @@ export var removeSyncedBlockAtPos = function removeSyncedBlockAtPos(api, pos) {
348
349
  var node = tr.doc.nodeAt(pos);
349
350
  if ((node === null || node === void 0 ? void 0 : node.type.name) === 'syncBlock') {
350
351
  var _node$nodeSize;
351
- return tr.replace(pos, pos + ((_node$nodeSize = node === null || node === void 0 ? void 0 : node.nodeSize) !== null && _node$nodeSize !== void 0 ? _node$nodeSize : 0));
352
+ var removeTr = tr.replace(pos, pos + ((_node$nodeSize = node === null || node === void 0 ? void 0 : node.nodeSize) !== null && _node$nodeSize !== void 0 ? _node$nodeSize : 0));
353
+ if (expValEqualsNoExposure('platform_editor_sync_block_activation', 'isEnabled', true)) {
354
+ removeTr.setMeta(deleteMechanismMetaKey, 'deleteButton');
355
+ }
356
+ return removeTr;
352
357
  }
353
358
  return tr;
354
359
  });