@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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # @atlaskit/editor-plugin-synced-block
2
2
 
3
+ ## 10.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`3d4d7717366f7`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/3d4d7717366f7) -
8
+ Prompt for feedback after eligible synced block deletion or undo
9
+
10
+ ### Patch Changes
11
+
12
+ - Updated dependencies
13
+
3
14
  ## 10.4.7
4
15
 
5
16
  ### Patch Changes
@@ -14,6 +14,7 @@ var _state = require("@atlaskit/editor-prosemirror/state");
14
14
  var _utils = require("@atlaskit/editor-prosemirror/utils");
15
15
  var _utils2 = require("@atlaskit/editor-synced-block-provider/utils");
16
16
  var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
17
+ var _expValEqualsNoExposure = require("@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure");
17
18
  var _main = require("../pm-plugins/main");
18
19
  var _utils3 = require("../pm-plugins/utils/utils");
19
20
  var _types = require("../types");
@@ -355,7 +356,11 @@ var removeSyncedBlockAtPos = exports.removeSyncedBlockAtPos = function removeSyn
355
356
  var node = tr.doc.nodeAt(pos);
356
357
  if ((node === null || node === void 0 ? void 0 : node.type.name) === 'syncBlock') {
357
358
  var _node$nodeSize;
358
- return tr.replace(pos, pos + ((_node$nodeSize = node === null || node === void 0 ? void 0 : node.nodeSize) !== null && _node$nodeSize !== void 0 ? _node$nodeSize : 0));
359
+ 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));
360
+ if ((0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_sync_block_activation', 'isEnabled', true)) {
361
+ removeTr.setMeta(_main.deleteMechanismMetaKey, 'deleteButton');
362
+ }
363
+ return removeTr;
359
364
  }
360
365
  return tr;
361
366
  });
@@ -4,7 +4,7 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
- exports.syncedBlockPluginKey = exports.getDeleteMechanism = exports.deleteMechanismMetaKey = exports.creationMetaKey = exports.createPlugin = void 0;
7
+ exports.syncedBlockPluginKey = exports.getPromptedFeedbackEntryPoint = exports.getDeleteMechanism = exports.deleteMechanismMetaKey = exports.creationMetaKey = exports.createPlugin = void 0;
8
8
  var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
9
9
  var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
10
10
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
@@ -24,6 +24,7 @@ var _editorSyncedBlockProvider = require("@atlaskit/editor-synced-block-provider
24
24
  var _utils2 = require("@atlaskit/editor-synced-block-provider/utils");
25
25
  var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
26
26
  var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
27
+ var _expValEqualsNoExposure = require("@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure");
27
28
  var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
28
29
  var _bodiedSyncedBlock = require("../nodeviews/bodiedSyncedBlock");
29
30
  var _syncedBlock = require("../nodeviews/syncedBlock");
@@ -53,6 +54,7 @@ var syncedBlockPluginKey = exports.syncedBlockPluginKey = new _state.PluginKey('
53
54
  * `platform_editor_blocks_patch_4`.
54
55
  */
55
56
  var deleteMechanismMetaKey = exports.deleteMechanismMetaKey = new _state.PluginKey('syncedBlockDeleteMechanism');
57
+ var syncedBlockPromptedFeedbackMetaKey = new _state.PluginKey('syncedBlockPromptedFeedback');
56
58
 
57
59
  /**
58
60
  * Creation-type signals set by {@link createSyncedBlock} on the creating
@@ -149,7 +151,7 @@ var getDeleteReason = function getDeleteReason(tr) {
149
151
 
150
152
  /** Narrows the history plugin meta, matching the editor-plugin-card pattern. */
151
153
  var isHistoryMeta = function isHistoryMeta(meta) {
152
- return (0, _typeof2.default)(meta) === 'object' && meta !== null && 'redo' in meta;
154
+ return (0, _typeof2.default)(meta) === 'object' && meta !== null && 'redo' in meta && typeof meta.redo === 'boolean';
153
155
  };
154
156
 
155
157
  /**
@@ -174,9 +176,16 @@ var isHistoryMeta = function isHistoryMeta(meta) {
174
176
  * selected when the edit was made.
175
177
  */
176
178
  var getDeleteMechanism = exports.getDeleteMechanism = function getDeleteMechanism(tr, state) {
177
- var _selection$node;
179
+ var _selection$node, _selection$node2;
180
+ // Keep the legacy analytics classification unchanged while activation is off.
181
+ // The stricter history, structural-step, and reference-selection handling is
182
+ // only needed by the prompted feedback rollout.
183
+ var isSyncBlockActivationEnabled = (0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_sync_block_activation', 'isEnabled', true);
178
184
  var historyMeta = tr.getMeta(_utils.pmHistoryPluginKey);
179
185
  if (historyMeta) {
186
+ if (isSyncBlockActivationEnabled && !isHistoryMeta(historyMeta)) {
187
+ return 'other';
188
+ }
180
189
  return isHistoryMeta(historyMeta) && historyMeta.redo ? 'redo' : 'undo';
181
190
  }
182
191
 
@@ -185,16 +194,28 @@ var getDeleteMechanism = exports.getDeleteMechanism = function getDeleteMechanis
185
194
  if (tr.getMeta(deleteMechanismMetaKey) === 'deleteButton') {
186
195
  return 'deleteButton';
187
196
  }
197
+ var hasStructuralStep = isSyncBlockActivationEnabled && tr.steps.some(function (step) {
198
+ return step instanceof _transform.ReplaceAroundStep;
199
+ });
188
200
  var hasReplaceStep = tr.steps.some(function (step) {
189
201
  return step instanceof _transform.ReplaceStep;
190
202
  });
191
- if (!hasReplaceStep) {
203
+ if (hasStructuralStep || !hasReplaceStep) {
192
204
  return 'other';
193
205
  }
194
206
  var selection = state.selection;
195
- var isNodeSelected = selection instanceof _state.NodeSelection && ((_selection$node = selection.node) === null || _selection$node === void 0 ? void 0 : _selection$node.type.name) === 'bodiedSyncBlock';
207
+ var isNodeSelected = selection instanceof _state.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');
196
208
  return isNodeSelected ? 'selectionReplaced' : 'keyboardDelete';
197
209
  };
210
+ var getPromptedFeedbackEntryPoint = exports.getPromptedFeedbackEntryPoint = function getPromptedFeedbackEntryPoint(mechanism) {
211
+ if (mechanism === 'undo') {
212
+ return 'prompted-undo';
213
+ }
214
+ if (mechanism === 'deleteButton' || mechanism === 'keyboardDelete' || mechanism === 'selectionReplaced') {
215
+ return 'prompted-delete';
216
+ }
217
+ return undefined;
218
+ };
198
219
  /**
199
220
  * True when a source `bodiedSyncBlock` holds user content — i.e. not just the
200
221
  * single empty paragraph it is created with (used to detect empty→content).
@@ -239,6 +260,7 @@ var filterTransactionOnline = function filterTransactionOnline(_ref3) {
239
260
  syncBlockStore = _ref3.syncBlockStore,
240
261
  api = _ref3.api,
241
262
  confirmationTransactionRef = _ref3.confirmationTransactionRef,
263
+ ctx = _ref3.ctx,
242
264
  bodiedSyncBlockRemoved = _ref3.bodiedSyncBlockRemoved,
243
265
  bodiedSyncBlockAdded = _ref3.bodiedSyncBlockAdded,
244
266
  extensionFlagShown = _ref3.extensionFlagShown;
@@ -266,6 +288,19 @@ var filterTransactionOnline = function filterTransactionOnline(_ref3) {
266
288
  // dedicated `cross_product_paste` event subject.
267
289
  var isPaste = Boolean((_tr$getMeta = tr.getMeta('paste')) !== null && _tr$getMeta !== void 0 ? _tr$getMeta : tr.getMeta('uiEvent') === 'paste');
268
290
  var isPasteOrDrop = isPaste || tr.getMeta('uiEvent') === 'drop';
291
+ var hasRemovedSyncBlock = syncBlockRemoved.length > 0 || bodiedSyncBlockRemoved.length > 0;
292
+ var isCutRemoval = hasRemovedSyncBlock && ctx.consumeCutRemoval();
293
+ var canPromptForRemoval = Boolean(ctx.onGiveFeedback) && hasRemovedSyncBlock && !(0, _collab.isDirtyTransaction)(tr) && !isCutRemoval && !isPasteOrDrop;
294
+ var promptedFeedbackEntryPoint = canPromptForRemoval ? getPromptedFeedbackEntryPoint(getDeleteMechanism(tr, state)) : undefined;
295
+ var entryPoint = promptedFeedbackEntryPoint && (0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_sync_block_activation', 'isEnabled', true) ? promptedFeedbackEntryPoint : undefined;
296
+ if (entryPoint && syncBlockRemoved.length > 0 && bodiedSyncBlockRemoved.length === 0) {
297
+ tr.setMeta(syncedBlockPromptedFeedbackMetaKey, {
298
+ context: {
299
+ blockType: 'reference',
300
+ entryPoint: entryPoint
301
+ }
302
+ });
303
+ }
269
304
  syncBlockAdded.forEach(function (syncBlock) {
270
305
  var _api$analytics2;
271
306
  api === null || api === void 0 || (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 || (_api$analytics2 = _api$analytics2.actions) === null || _api$analytics2 === void 0 || _api$analytics2.fireAnalyticsEvent({
@@ -293,7 +328,28 @@ var filterTransactionOnline = function filterTransactionOnline(_ref3) {
293
328
  }
294
329
  // Only derive the mechanism behind the gate — discarded gate-off.
295
330
  var mechanism = (0, _platformFeatureFlags.fg)('platform_editor_blocks_patch_4') ? getDeleteMechanism(tr, state) : undefined;
296
- return (0, _handleBodiedSyncBlockRemoval.handleBodiedSyncBlockRemoval)(bodiedSyncBlockRemoved, syncBlockStore, api, confirmationTransactionRef, getDeleteReason(tr), mechanism);
331
+ var feedbackContext = entryPoint && getDeleteReason(tr) !== 'source-block-unsynced' ? {
332
+ blockType: 'source',
333
+ entryPoint: entryPoint
334
+ } : undefined;
335
+ if (feedbackContext) {
336
+ var sourceAttempt = ctx.queueSourceFeedback(feedbackContext);
337
+ return (0, _handleBodiedSyncBlockRemoval.handleBodiedSyncBlockRemoval)(bodiedSyncBlockRemoved, syncBlockStore, api, confirmationTransactionRef, getDeleteReason(tr), mechanism, {
338
+ onDeleteCompleted: function onDeleteCompleted(success) {
339
+ return ctx.completeSourceDeletion(sourceAttempt, success);
340
+ },
341
+ onDeleteTransaction: function onDeleteTransaction(deleteTr) {
342
+ return deleteTr.setMeta(syncedBlockPromptedFeedbackMetaKey, {
343
+ context: feedbackContext,
344
+ sourceAttempt: sourceAttempt
345
+ });
346
+ },
347
+ onDestroy: function onDestroy() {
348
+ return ctx.clearSourceFeedback(sourceAttempt);
349
+ }
350
+ });
351
+ }
352
+ return (0, _handleBodiedSyncBlockRemoval.handleBodiedSyncBlockRemoval)(bodiedSyncBlockRemoved, syncBlockStore, api, confirmationTransactionRef, getDeleteReason(tr), mechanism, undefined);
297
353
  }
298
354
  if (bodiedSyncBlockAdded.length > 0) {
299
355
  if (tr.getMeta(_utils.pmHistoryPluginKey)) {
@@ -415,15 +471,18 @@ var buildStatusDecorations = function buildStatusDecorations(doc, syncBlockStore
415
471
  * is explicitly scoped to a single plugin instance.
416
472
  */
417
473
  var SyncedBlockPluginContext = /*#__PURE__*/function () {
418
- function SyncedBlockPluginContext() {
474
+ function SyncedBlockPluginContext(onGiveFeedback) {
419
475
  (0, _classCallCheck2.default)(this, SyncedBlockPluginContext);
420
476
  (0, _defineProperty2.default)(this, "confirmationTransactionRef", {
421
477
  current: undefined
422
478
  });
479
+ (0, _defineProperty2.default)(this, "_cutRemovalPending", false);
480
+ (0, _defineProperty2.default)(this, "cutRemovalGeneration", 0);
423
481
  (0, _defineProperty2.default)(this, "_isCopyEvent", false);
424
482
  (0, _defineProperty2.default)(this, "_isCutEvent", false);
425
483
  (0, _defineProperty2.default)(this, "unpublishedFlagShown", new Set());
426
484
  (0, _defineProperty2.default)(this, "extensionFlagShown", new Set());
485
+ this.onGiveFeedback = onGiveFeedback;
427
486
  }
428
487
  return (0, _createClass2.default)(SyncedBlockPluginContext, [{
429
488
  key: "isCopyEvent",
@@ -454,19 +513,114 @@ var SyncedBlockPluginContext = /*#__PURE__*/function () {
454
513
  this._isCutEvent = false;
455
514
  return was;
456
515
  }
516
+ }, {
517
+ key: "markCutRemovalPending",
518
+ value: function markCutRemovalPending() {
519
+ var _this = this;
520
+ this._cutRemovalPending = true;
521
+ var generation = ++this.cutRemovalGeneration;
522
+ queueMicrotask(function () {
523
+ if (_this.cutRemovalGeneration === generation) {
524
+ _this._cutRemovalPending = false;
525
+ }
526
+ });
527
+ }
528
+ }, {
529
+ key: "consumeCutRemoval",
530
+ value: function consumeCutRemoval() {
531
+ var wasPending = this._cutRemovalPending;
532
+ this._cutRemovalPending = false;
533
+ this.cutRemovalGeneration += 1;
534
+ return wasPending;
535
+ }
536
+ }, {
537
+ key: "queueSourceFeedback",
538
+ value: function queueSourceFeedback(context) {
539
+ var sourceAttempt = Symbol('syncedBlockSourceFeedbackAttempt');
540
+ this.pendingSourceFeedback = {
541
+ context: context,
542
+ deletionSucceeded: false,
543
+ sourceAttempt: sourceAttempt,
544
+ transactionApplied: false
545
+ };
546
+ return sourceAttempt;
547
+ }
548
+ }, {
549
+ key: "completeSourceDeletion",
550
+ value: function completeSourceDeletion(sourceAttempt, success) {
551
+ var _this$pendingSourceFe;
552
+ if (((_this$pendingSourceFe = this.pendingSourceFeedback) === null || _this$pendingSourceFe === void 0 ? void 0 : _this$pendingSourceFe.sourceAttempt) !== sourceAttempt) {
553
+ return;
554
+ }
555
+ if (!success) {
556
+ // Keep the attempt pending because retryDeletion reuses these callbacks.
557
+ // Dismissal or cancellation clears it through the destroy callback.
558
+ return;
559
+ }
560
+ this.pendingSourceFeedback.deletionSucceeded = true;
561
+ this.flushSourceFeedback(sourceAttempt);
562
+ }
563
+ }, {
564
+ key: "clearSourceFeedback",
565
+ value: function clearSourceFeedback(sourceAttempt) {
566
+ var _this$pendingSourceFe2;
567
+ if (((_this$pendingSourceFe2 = this.pendingSourceFeedback) === null || _this$pendingSourceFe2 === void 0 ? void 0 : _this$pendingSourceFe2.sourceAttempt) === sourceAttempt) {
568
+ this.pendingSourceFeedback = undefined;
569
+ }
570
+ }
571
+ }, {
572
+ key: "handleAppliedFeedback",
573
+ value: function handleAppliedFeedback(_ref6) {
574
+ var _this$pendingSourceFe3;
575
+ var context = _ref6.context,
576
+ sourceAttempt = _ref6.sourceAttempt;
577
+ if (context.blockType === 'source' && sourceAttempt && ((_this$pendingSourceFe3 = this.pendingSourceFeedback) === null || _this$pendingSourceFe3 === void 0 ? void 0 : _this$pendingSourceFe3.sourceAttempt) === sourceAttempt) {
578
+ this.pendingSourceFeedback.transactionApplied = true;
579
+ this.flushSourceFeedback(sourceAttempt);
580
+ return;
581
+ }
582
+ if (context.blockType === 'reference') {
583
+ this.invokeFeedback(context);
584
+ }
585
+ }
586
+ }, {
587
+ key: "flushSourceFeedback",
588
+ value: function flushSourceFeedback(sourceAttempt) {
589
+ var _this$pendingSourceFe4, _this$pendingSourceFe5;
590
+ 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) {
591
+ var context = this.pendingSourceFeedback.context;
592
+ this.pendingSourceFeedback = undefined;
593
+ this.invokeFeedback(context);
594
+ }
595
+ }
596
+ }, {
597
+ key: "invokeFeedback",
598
+ value: function invokeFeedback(context) {
599
+ var _this2 = this;
600
+ (0, _utils3.deferDispatch)(function () {
601
+ try {
602
+ var _this2$onGiveFeedback;
603
+ var feedbackResult = (_this2$onGiveFeedback = _this2.onGiveFeedback) === null || _this2$onGiveFeedback === void 0 ? void 0 : _this2$onGiveFeedback.call(_this2, context);
604
+ void Promise.resolve(feedbackResult).catch(function () {});
605
+ } catch (_unused) {
606
+ // Product feedback collectors are optional UI. Their failures must
607
+ // never affect the already-applied editor transaction.
608
+ }
609
+ });
610
+ }
457
611
  }]);
458
612
  }();
459
613
  var createPlugin = exports.createPlugin = function createPlugin(options, pmPluginFactoryParams, syncBlockStore, api) {
460
- var _ref6 = options || {},
461
- _ref6$useLongPressSel = _ref6.useLongPressSelection,
462
- useLongPressSelection = _ref6$useLongPressSel === void 0 ? false : _ref6$useLongPressSel;
614
+ var _ref7 = options || {},
615
+ _ref7$useLongPressSel = _ref7.useLongPressSelection,
616
+ useLongPressSelection = _ref7$useLongPressSel === void 0 ? false : _ref7$useLongPressSel;
463
617
 
464
618
  // Cache the experiment value once at plugin creation time.
465
619
  // This fires the exposure event exactly once (correct per Exposure Events 101)
466
620
  // and avoids ~10 redundant Statsig SDK evaluations per keystroke in hot paths
467
621
  // (apply, filterTransaction, appendTransaction, decorations).
468
622
  var isPerfExperimentOn = (0, _expValEquals.expValEquals)('editor_synced_block_perf', 'isEnabled', true);
469
- var ctx = new SyncedBlockPluginContext();
623
+ var ctx = new SyncedBlockPluginContext(options === null || options === void 0 ? void 0 : options.onGiveFeedback);
470
624
  var confirmationTransactionRef = ctx.confirmationTransactionRef;
471
625
  var unpublishedFlagShown = ctx.unpublishedFlagShown;
472
626
  var extensionFlagShown = ctx.extensionFlagShown;
@@ -476,8 +630,8 @@ var createPlugin = exports.createPlugin = function createPlugin(options, pmPlugi
476
630
  // Classic pages with sync blocks.
477
631
  syncBlockStore.sourceManager.registerFlushCompletionCallback(function () {
478
632
  (0, _utils3.deferDispatch)(function () {
479
- api === null || api === void 0 || api.core.actions.execute(function (_ref7) {
480
- var tr = _ref7.tr;
633
+ api === null || api === void 0 || api.core.actions.execute(function (_ref8) {
634
+ var tr = _ref8.tr;
481
635
  return tr;
482
636
  });
483
637
  });
@@ -505,8 +659,8 @@ var createPlugin = exports.createPlugin = function createPlugin(options, pmPlugi
505
659
  // case the Confluence default is used.
506
660
  var sourceProduct = (0, _utils2.getSourceProductFromResourceIdSafe)(resourceId);
507
661
  (0, _utils3.deferDispatch)(function () {
508
- api === null || api === void 0 || api.core.actions.execute(function (_ref8) {
509
- var tr = _ref8.tr;
662
+ api === null || api === void 0 || api.core.actions.execute(function (_ref9) {
663
+ var tr = _ref9.tr;
510
664
  return tr.setMeta(syncedBlockPluginKey, {
511
665
  activeFlag: {
512
666
  id: _types.FLAG_ID.UNPUBLISHED_SYNC_BLOCK_PASTED,
@@ -818,7 +972,7 @@ var createPlugin = exports.createPlugin = function createPlugin(options, pmPlugi
818
972
  return false;
819
973
  },
820
974
  cut: function cut() {
821
- if ((0, _platformFeatureFlags.fg)('platform_synced_block_patch_13')) {
975
+ if ((0, _platformFeatureFlags.fg)('platform_synced_block_patch_13') || Boolean(options === null || options === void 0 ? void 0 : options.onGiveFeedback) && (0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_sync_block_activation', 'isEnabled', true)) {
822
976
  ctx.consumeCutEvent();
823
977
  ctx.markCutEvent();
824
978
  }
@@ -849,27 +1003,47 @@ var createPlugin = exports.createPlugin = function createPlugin(options, pmPlugi
849
1003
  return node;
850
1004
  });
851
1005
  },
852
- transformCopied: function transformCopied(slice, _ref9) {
853
- var state = _ref9.state;
1006
+ transformCopied: function transformCopied(slice, _ref0) {
1007
+ var state = _ref0.state;
854
1008
  var pluginState = syncedBlockPluginKey.getState(state);
855
1009
  var syncBlockStore = pluginState === null || pluginState === void 0 ? void 0 : pluginState.syncBlockStore;
856
1010
  var schema = state.schema;
857
1011
  var isCopy = ctx.consumeCopyEvent();
858
1012
  var isSyncedBlockPatch13Enabled = (0, _platformFeatureFlags.fg)('platform_synced_block_patch_13');
859
- var isCut = isSyncedBlockPatch13Enabled && ctx.consumeCutEvent();
860
- if (!syncBlockStore || !isCopy && !isCut) {
1013
+ var wasCut = ctx.consumeCutEvent();
1014
+ var isCut = isSyncedBlockPatch13Enabled && wasCut;
1015
+ var isPromptedFeedbackCut = Boolean(syncBlockStore && (options === null || options === void 0 ? void 0 : options.onGiveFeedback) && wasCut) && (0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_sync_block_activation', 'isEnabled', true);
1016
+ if (!syncBlockStore || !isCopy && !isCut && !isPromptedFeedbackCut) {
861
1017
  return slice;
862
1018
  }
863
- return (0, _utils.mapSlice)(slice, function (node) {
1019
+ if (isPromptedFeedbackCut && !isCut) {
1020
+ var _containsCutSyncBlock = false;
1021
+ (0, _utils.mapSlice)(slice, function (node) {
1022
+ if (syncBlockStore.referenceManager.isReferenceBlock(node) || node.type.name === 'bodiedSyncBlock' && (0, _utils3.sliceFullyContainsNode)(slice, node)) {
1023
+ _containsCutSyncBlock = true;
1024
+ }
1025
+ return node;
1026
+ });
1027
+ if (_containsCutSyncBlock) {
1028
+ ctx.markCutRemovalPending();
1029
+ }
1030
+ return slice;
1031
+ }
1032
+ var containsCutSyncBlock = false;
1033
+ var transformedSlice = (0, _utils.mapSlice)(slice, function (node) {
864
1034
  if (syncBlockStore.referenceManager.isReferenceBlock(node)) {
865
1035
  var _syncBlockStore$refer;
866
1036
  if (isCut) {
1037
+ containsCutSyncBlock = isPromptedFeedbackCut;
867
1038
  return node;
868
1039
  }
869
1040
  showCopiedFlag(api, options === null || options === void 0 ? void 0 : options.__livePage, ((_syncBlockStore$refer = syncBlockStore.referenceManager.getFromCache(node.attrs.resourceId)) === null || _syncBlockStore$refer === void 0 || (_syncBlockStore$refer = _syncBlockStore$refer.data) === null || _syncBlockStore$refer === void 0 ? void 0 : _syncBlockStore$refer.status) === 'unpublished', (0, _utils2.getSourceProductFromResourceIdSafe)(node.attrs.resourceId));
870
1041
  return node;
871
1042
  }
872
1043
  if (node.type.name === 'bodiedSyncBlock' && node.attrs.resourceId) {
1044
+ if (isPromptedFeedbackCut && isCut && (0, _utils3.sliceFullyContainsNode)(slice, node)) {
1045
+ containsCutSyncBlock = true;
1046
+ }
873
1047
  // if we only selected part of the bodied sync block content,
874
1048
  // remove the sync block node and only keep the content
875
1049
  if (!(0, _utils3.sliceFullyContainsNode)(slice, node)) {
@@ -893,6 +1067,10 @@ var createPlugin = exports.createPlugin = function createPlugin(options, pmPlugi
893
1067
  }
894
1068
  return node;
895
1069
  });
1070
+ if (containsCutSyncBlock) {
1071
+ ctx.markCutRemovalPending();
1072
+ }
1073
+ return transformedSlice;
896
1074
  }
897
1075
  },
898
1076
  filterTransaction: function filterTransaction(tr, state) {
@@ -979,6 +1157,7 @@ var createPlugin = exports.createPlugin = function createPlugin(options, pmPlugi
979
1157
  syncBlockStore: syncBlockStore,
980
1158
  api: api,
981
1159
  confirmationTransactionRef: confirmationTransactionRef,
1160
+ ctx: ctx,
982
1161
  bodiedSyncBlockRemoved: bodiedSyncBlockRemoved,
983
1162
  bodiedSyncBlockAdded: bodiedSyncBlockAdded,
984
1163
  extensionFlagShown: extensionFlagShown
@@ -1002,6 +1181,17 @@ var createPlugin = exports.createPlugin = function createPlugin(options, pmPlugi
1002
1181
  if (viewMode === 'view') {
1003
1182
  return null;
1004
1183
  }
1184
+ if (options !== null && options !== void 0 && options.onGiveFeedback) {
1185
+ var feedbackContexts = trs.flatMap(function (tr) {
1186
+ var feedbackContext = tr.getMeta(syncedBlockPromptedFeedbackMetaKey);
1187
+ return feedbackContext ? [feedbackContext] : [];
1188
+ });
1189
+ if (feedbackContexts.length > 0 && (0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_sync_block_activation', 'isEnabled', true)) {
1190
+ feedbackContexts.forEach(function (feedbackContext) {
1191
+ ctx.handleAppliedFeedback(feedbackContext);
1192
+ });
1193
+ }
1194
+ }
1005
1195
 
1006
1196
  // Update source sync block cache for user-initiated changes only.
1007
1197
  // Cache updates are handled here instead of in the nodeview update() so we
@@ -1086,8 +1276,8 @@ var createPlugin = exports.createPlugin = function createPlugin(options, pmPlugi
1086
1276
  if (!(step instanceof _transform.ReplaceStep || step instanceof _transform.ReplaceAroundStep) || !('slice' in step)) {
1087
1277
  return false;
1088
1278
  }
1089
- var _ref0 = step,
1090
- slice = _ref0.slice;
1279
+ var _ref1 = step,
1280
+ slice = _ref1.slice;
1091
1281
  var found = false;
1092
1282
  slice.content.descendants(function (node) {
1093
1283
  if (syncBlockStore.sourceManager.isSourceBlock(node) && node.attrs.resourceId) {
@@ -1127,8 +1317,8 @@ var createPlugin = exports.createPlugin = function createPlugin(options, pmPlugi
1127
1317
  tr.setMeta('addToHistory', false);
1128
1318
  (0, _utils3.deferDispatch)(function () {
1129
1319
  var _api$core;
1130
- api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(function (_ref1) {
1131
- var tr = _ref1.tr;
1320
+ api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(function (_ref10) {
1321
+ var tr = _ref10.tr;
1132
1322
  return tr.setMeta(syncedBlockPluginKey, {
1133
1323
  activeFlag: {
1134
1324
  id: _types.FLAG_ID.DUPLICATE_SOURCE_SYNC_BLOCK
@@ -34,7 +34,7 @@ var onDismissed = function onDismissed(syncBlockStore) {
34
34
  });
35
35
  };
36
36
  };
37
- var handleBodiedSyncBlockRemoval = exports.handleBodiedSyncBlockRemoval = function handleBodiedSyncBlockRemoval(bodiedSyncBlockRemoved, syncBlockStore, api, confirmationTransactionRef, deletionReason, mechanism) {
37
+ var handleBodiedSyncBlockRemoval = exports.handleBodiedSyncBlockRemoval = function handleBodiedSyncBlockRemoval(bodiedSyncBlockRemoved, syncBlockStore, api, confirmationTransactionRef, deletionReason, mechanism, feedbackCallbacks) {
38
38
  // Clear potential old pending deletion to retreat the deletion as first attempt
39
39
  syncBlockStore.sourceManager.clearPendingDeletion();
40
40
 
@@ -70,6 +70,7 @@ var handleBodiedSyncBlockRemoval = exports.handleBodiedSyncBlockRemoval = functi
70
70
  return null;
71
71
  }
72
72
  recomputedTr.setMeta('isConfirmedSyncBlockDeletion', true);
73
+ feedbackCallbacks === null || feedbackCallbacks === void 0 || feedbackCallbacks.onDeleteTransaction(recomputedTr);
73
74
  if (!recomputedTr.getMeta(_utils.pmHistoryPluginKey)) {
74
75
  // bodiedSyncBlock deletion is expected to be permanent (cannot undo)
75
76
  // For a normal deletion (not triggered by undo), remove it from history so that it cannot be undone
@@ -85,6 +86,7 @@ var handleBodiedSyncBlockRemoval = exports.handleBodiedSyncBlockRemoval = functi
85
86
  }
86
87
  api === null || api === void 0 || (_api$core3 = api.core) === null || _api$core3 === void 0 || _api$core3.actions.execute(function () {
87
88
  var trToDispatch = confirmationTransaction.setMeta('isConfirmedSyncBlockDeletion', true);
89
+ feedbackCallbacks === null || feedbackCallbacks === void 0 || feedbackCallbacks.onDeleteTransaction(trToDispatch);
88
90
  if (!trToDispatch.getMeta(_utils.pmHistoryPluginKey)) {
89
91
  // bodiedSyncBlock deletion is expected to be permanent (cannot undo)
90
92
  // For a normal deletion (not triggered by undo), remove it from history so that it cannot be undone
@@ -94,6 +96,7 @@ var handleBodiedSyncBlockRemoval = exports.handleBodiedSyncBlockRemoval = functi
94
96
  });
95
97
  }, function (success) {
96
98
  var _api$core4;
99
+ feedbackCallbacks === null || feedbackCallbacks === void 0 || feedbackCallbacks.onDeleteCompleted(success);
97
100
  api === null || api === void 0 || (_api$core4 = api.core) === null || _api$core4 === void 0 || _api$core4.actions.execute(function (_ref3) {
98
101
  var tr = _ref3.tr;
99
102
  var newState;
@@ -121,6 +124,7 @@ var handleBodiedSyncBlockRemoval = exports.handleBodiedSyncBlockRemoval = functi
121
124
  });
122
125
  }, function () {
123
126
  confirmationTransactionRef.current = undefined;
127
+ feedbackCallbacks === null || feedbackCallbacks === void 0 || feedbackCallbacks.onDestroy();
124
128
  }, mechanism);
125
129
  return false;
126
130
  };
@@ -39,6 +39,7 @@ var getToolbarConfig = exports.getToolbarConfig = function getToolbarConfig(stat
39
39
  if (syncBlockStore.sourceManager.isPendingCreation(syncBlockObject.node.attrs.resourceId)) {
40
40
  return;
41
41
  }
42
+ var isSyncBlockActivationEnabled = (0, _expValEquals.expValEquals)('platform_editor_sync_block_activation', 'isEnabled', true);
42
43
  var syncBlockInstance = syncBlockStore.referenceManager.getFromCache(syncBlockObject.node.attrs.resourceId);
43
44
  var isUnsyncedBlock = (syncBlockInstance === null || syncBlockInstance === void 0 || (_syncBlockInstance$er = syncBlockInstance.error) === null || _syncBlockInstance$er === void 0 ? void 0 : _syncBlockInstance$er.type) === _editorSyncedBlockProvider.SyncBlockError.NotFound;
44
45
  var isErroredBlock = syncBlockInstance === null || syncBlockInstance === void 0 ? void 0 : syncBlockInstance.error;
@@ -59,7 +60,6 @@ var getToolbarConfig = exports.getToolbarConfig = function getToolbarConfig(stat
59
60
  };
60
61
  };
61
62
  var items = [];
62
- var isSyncBlockActivationEnabled = (0, _expValEquals.expValEquals)('platform_editor_sync_block_activation', 'isEnabled', true);
63
63
  if (isUnsyncedBlock) {
64
64
  var deleteButton = _objectSpread({
65
65
  type: 'button',
@@ -197,7 +197,8 @@ var getToolbarConfig = exports.getToolbarConfig = function getToolbarConfig(stat
197
197
  return tr.setSelection(new _selection.GapCursorSelection(tr.doc.resolve(syncBlockObject.pos + syncBlockObject.node.nodeSize), _selection.Side.RIGHT));
198
198
  });
199
199
  onGiveFeedback({
200
- blockType: isBodiedSyncBlock ? 'source' : 'reference'
200
+ blockType: isBodiedSyncBlock ? 'source' : 'reference',
201
+ entryPoint: 'overflow-menu'
201
202
  });
202
203
  return true;
203
204
  }
@@ -6,6 +6,7 @@ import { NodeSelection, Selection, TextSelection } from '@atlaskit/editor-prosem
6
6
  import { findSelectedNodeOfType, removeParentNodeOfType, removeSelectedNode, safeInsert } from '@atlaskit/editor-prosemirror/utils';
7
7
  import { getSourceProductFromResourceIdSafe } from '@atlaskit/editor-synced-block-provider/utils';
8
8
  import { fg } from '@atlaskit/platform-feature-flags';
9
+ import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
9
10
  import { creationMetaKey, deleteMechanismMetaKey, syncedBlockPluginKey } from '../pm-plugins/main';
10
11
  import { canBeConvertedToSyncBlock, deferDispatch, findSyncBlock, findSyncBlockOrBodiedSyncBlock, isBodiedSyncBlockNode } from '../pm-plugins/utils/utils';
11
12
  import { FLAG_ID } from '../types';
@@ -355,7 +356,11 @@ export const removeSyncedBlockAtPos = (api, pos) => {
355
356
  const node = tr.doc.nodeAt(pos);
356
357
  if ((node === null || node === void 0 ? void 0 : node.type.name) === 'syncBlock') {
357
358
  var _node$nodeSize;
358
- return tr.replace(pos, pos + ((_node$nodeSize = node === null || node === void 0 ? void 0 : node.nodeSize) !== null && _node$nodeSize !== void 0 ? _node$nodeSize : 0));
359
+ const removeTr = tr.replace(pos, pos + ((_node$nodeSize = node === null || node === void 0 ? void 0 : node.nodeSize) !== null && _node$nodeSize !== void 0 ? _node$nodeSize : 0));
360
+ if (expValEqualsNoExposure('platform_editor_sync_block_activation', 'isEnabled', true)) {
361
+ removeTr.setMeta(deleteMechanismMetaKey, 'deleteButton');
362
+ }
363
+ return removeTr;
359
364
  }
360
365
  return tr;
361
366
  });