@atlaskit/editor-synced-block-provider 9.0.2 → 9.1.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,45 @@
1
1
  # @atlaskit/editor-synced-block-provider
2
2
 
3
+ ## 9.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`b7e3a66378c6e`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/b7e3a66378c6e) -
8
+ [ux] Restore the contextual toolbar when selecting text inside a reference synced block, behind
9
+ `platform_editor_blocks_patch_7`.
10
+ - `@atlaskit/editor-plugin-toolbar`: the toolbar no longer treats focus inside a nested editable
11
+ region that ProseMirror does not own - such as the content island a reference synced block
12
+ renders - as the editor being blurred.
13
+ - `@atlaskit/editor-synced-block-provider`: `getProviderFactory` is now side-effect free. Applying
14
+ parent and dynamically created providers moved to a new `syncProviders` method. Previously this
15
+ ran during render, scheduling a `setState` on another component mid-render, which forced an
16
+ extra render pass that replaced the synced block DOM and destroyed any in-progress text
17
+ selection.
18
+
19
+ `syncProviders` mutates the cached factory and notifies its subscribers, so call it from an effect
20
+ rather than during render:
21
+
22
+ ```ts
23
+ const providerFactory = manager.referenceManager.getProviderFactory(resourceId);
24
+
25
+ useEffect(() => {
26
+ manager.referenceManager.syncProviders(resourceId);
27
+ }, [manager.referenceManager, resourceId, syncBlockInstance]);
28
+ ```
29
+
30
+ With the flag off, `getProviderFactory` keeps applying providers inline, so existing behaviour is
31
+ unchanged.
32
+
33
+ ### Patch Changes
34
+
35
+ - Updated dependencies
36
+
37
+ ## 9.0.3
38
+
39
+ ### Patch Changes
40
+
41
+ - Updated dependencies
42
+
3
43
  ## 9.0.2
4
44
 
5
45
  ### Patch Changes
@@ -19,7 +19,7 @@ var _utils = require("../utils/utils");
19
19
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
20
20
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
21
21
  var useFetchSyncBlockData = exports.useFetchSyncBlockData = function useFetchSyncBlockData(manager, resourceId, localId, fireAnalyticsEvent) {
22
- var _manager$referenceMan2, _manager$referenceMan3, _manager$referenceMan4;
22
+ var _manager$referenceMan2, _manager$referenceMan3, _manager$referenceMan4, _manager$referenceMan5, _manager$referenceMan6, _manager$referenceMan7;
23
23
  // Initialize both states from a single cache lookup to avoid race conditions.
24
24
  // When a block is moved/remounted, the old component's cleanup may clear the cache
25
25
  // before or after the new component mounts. By doing a single lookup, we ensure
@@ -156,6 +156,27 @@ var useFetchSyncBlockData = exports.useFetchSyncBlockData = function useFetchSyn
156
156
  unsubscribe();
157
157
  };
158
158
  }, [isDataProviderReady, localId, manager.referenceManager, resourceId]);
159
+
160
+ // Pure read, safe during render. The individual provider references are effect
161
+ // dependencies below so a provider swapped by the host is re-applied to the
162
+ // cached factory, which would otherwise keep serving the previous one.
163
+ var _ref2 = (_manager$referenceMan5 = (_manager$referenceMan6 = (_manager$referenceMan7 = manager.referenceManager).getProviderOptions) === null || _manager$referenceMan6 === void 0 ? void 0 : _manager$referenceMan6.call(_manager$referenceMan7)) !== null && _manager$referenceMan5 !== void 0 ? _manager$referenceMan5 : {},
164
+ parentDataProviders = _ref2.parentDataProviders,
165
+ providerCreator = _ref2.providerCreator;
166
+
167
+ // Applying providers notifies subscribers synchronously, so it must not run during
168
+ // render - that would setState on another component mid-render and remount the block.
169
+ (0, _react.useEffect)(function () {
170
+ if (!(0, _platformFeatureFlags.fg)('platform_editor_blocks_patch_7')) {
171
+ return;
172
+ }
173
+ if (!resourceId || (0, _coreUtils.isSSR)()) {
174
+ return;
175
+ }
176
+ manager.referenceManager.syncProviders(resourceId);
177
+ // `syncBlockInstance` is a dependency because the dynamic providers are derived
178
+ // from the fetched block data, so they can only be created once it has arrived.
179
+ }, [manager.referenceManager, resourceId, syncBlockInstance, parentDataProviders === null || parentDataProviders === void 0 ? void 0 : parentDataProviders.mentionProvider, parentDataProviders === null || parentDataProviders === void 0 ? void 0 : parentDataProviders.profilecardProvider, parentDataProviders === null || parentDataProviders === void 0 ? void 0 : parentDataProviders.taskDecisionProvider, providerCreator]);
159
180
  var ssrProviders = (0, _react.useMemo)(function () {
160
181
  return resourceId ? manager.referenceManager.getSSRProviders(resourceId) : null;
161
182
  }, [resourceId, manager.referenceManager]);
@@ -996,6 +996,26 @@ var ReferenceSyncBlockStoreManager = exports.ReferenceSyncBlockStoreManager = /*
996
996
  value: function getProviderFactory(resourceId) {
997
997
  return this._providerFactoryManager.getProviderFactory(resourceId);
998
998
  }
999
+
1000
+ /**
1001
+ * The provider options currently supplied by the host. Safe to read during
1002
+ * render - it notifies nobody.
1003
+ */
1004
+ }, {
1005
+ key: "getProviderOptions",
1006
+ value: function getProviderOptions() {
1007
+ return this._providerFactoryManager.getProviderOptions();
1008
+ }
1009
+
1010
+ /**
1011
+ * Applies the latest providers to the cached factory. Notifies subscribers, so
1012
+ * call from an effect rather than during render.
1013
+ */
1014
+ }, {
1015
+ key: "syncProviders",
1016
+ value: function syncProviders(resourceId) {
1017
+ this._providerFactoryManager.syncProviders(resourceId);
1018
+ }
999
1019
  }, {
1000
1020
  key: "getSSRProviders",
1001
1021
  value: function getSSRProviders(resourceId) {
@@ -8,6 +8,7 @@ exports.SyncBlockProviderFactoryManager = 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"));
11
+ var _coreUtils = require("@atlaskit/editor-common/core-utils");
11
12
  var _monitoring = require("@atlaskit/editor-common/monitoring");
12
13
  var _providerFactory = require("@atlaskit/editor-common/provider-factory");
13
14
  var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
@@ -37,41 +38,82 @@ var SyncBlockProviderFactoryManager = exports.SyncBlockProviderFactoryManager =
37
38
  (_this$deps$getFireAna = this.deps.getFireAnalyticsEvent()) === null || _this$deps$getFireAna === void 0 || _this$deps$getFireAna((0, _errorHandling.fetchErrorPayload)(error.message, resourceId, (0, _utils.getSourceProductFromResourceIdSafe)(resourceId), (0, _errorHandling.buildFetchErrorAttribution)((0, _platformFeatureFlags.fg)('platform_editor_blocks_patch_3'), error.message)));
38
39
  return undefined;
39
40
  }
40
- var _dataProvider$getSync = dataProvider.getSyncedBlockRendererProviderOptions(),
41
- parentDataProviders = _dataProvider$getSync.parentDataProviders,
42
- providerCreator = _dataProvider$getSync.providerCreator;
43
41
  var providerFactory = this.providerFactories.get(resourceId);
44
42
  if (!providerFactory) {
43
+ var _dataProvider$getSync = dataProvider.getSyncedBlockRendererProviderOptions(),
44
+ parentDataProviders = _dataProvider$getSync.parentDataProviders;
45
45
  providerFactory = _providerFactory.ProviderFactory.create({
46
46
  mentionProvider: parentDataProviders === null || parentDataProviders === void 0 ? void 0 : parentDataProviders.mentionProvider,
47
47
  profilecardProvider: parentDataProviders === null || parentDataProviders === void 0 ? void 0 : parentDataProviders.profilecardProvider,
48
48
  taskDecisionProvider: parentDataProviders === null || parentDataProviders === void 0 ? void 0 : parentDataProviders.taskDecisionProvider
49
49
  });
50
50
  this.providerFactories.set(resourceId, providerFactory);
51
- } else {
52
- if (parentDataProviders !== null && parentDataProviders !== void 0 && parentDataProviders.mentionProvider) {
53
- providerFactory.setProvider('mentionProvider', parentDataProviders === null || parentDataProviders === void 0 ? void 0 : parentDataProviders.mentionProvider);
54
- }
55
- if (parentDataProviders !== null && parentDataProviders !== void 0 && parentDataProviders.profilecardProvider) {
56
- providerFactory.setProvider('profilecardProvider', parentDataProviders === null || parentDataProviders === void 0 ? void 0 : parentDataProviders.profilecardProvider);
57
- }
58
- if (parentDataProviders !== null && parentDataProviders !== void 0 && parentDataProviders.taskDecisionProvider) {
59
- providerFactory.setProvider('taskDecisionProvider', parentDataProviders === null || parentDataProviders === void 0 ? void 0 : parentDataProviders.taskDecisionProvider);
60
- }
61
- }
62
- if (providerCreator) {
63
- try {
64
- this.retrieveDynamicProviders(resourceId, providerFactory, providerCreator);
65
- } catch (error) {
66
- var _this$deps$getFireAna2;
67
- (0, _monitoring.logException)(error, {
68
- location: 'editor-synced-block-provider/syncBlockProviderFactoryManager'
69
- });
70
- (_this$deps$getFireAna2 = this.deps.getFireAnalyticsEvent()) === null || _this$deps$getFireAna2 === void 0 || _this$deps$getFireAna2((0, _errorHandling.fetchErrorPayload)(error.message, resourceId, (0, _utils.getSourceProductFromResourceIdSafe)(resourceId), (0, _errorHandling.buildFetchErrorAttribution)((0, _platformFeatureFlags.fg)('platform_editor_blocks_patch_3'), error.message)));
71
- }
72
51
  }
52
+
53
+ // Syncing providers notifies subscribers synchronously, so on the client it
54
+ // cannot run during render - callers invoke `syncProviders` from an effect
55
+ // instead. Effects never run while server rendering, and there are no mounted
56
+ // subscribers to notify, so the server keeps applying them inline.
57
+ var deferProviderSync = (0, _platformFeatureFlags.fg)('platform_editor_blocks_patch_7');
58
+ if (deferProviderSync && !(0, _coreUtils.isSSR)()) {
59
+ return providerFactory;
60
+ }
61
+ this.syncProviders(resourceId, providerFactory);
73
62
  return providerFactory;
74
63
  }
64
+
65
+ /**
66
+ * The provider options currently supplied by the host. A pure read that does
67
+ * not notify subscribers, so it is safe to call during render - callers use it
68
+ * to detect when the host swaps a provider and the factory needs re-syncing.
69
+ */
70
+ }, {
71
+ key: "getProviderOptions",
72
+ value: function getProviderOptions() {
73
+ var _this$deps$getDataPro;
74
+ return (_this$deps$getDataPro = this.deps.getDataProvider()) === null || _this$deps$getDataPro === void 0 ? void 0 : _this$deps$getDataPro.getSyncedBlockRendererProviderOptions();
75
+ }
76
+
77
+ /**
78
+ * Applies the latest parent and dynamic providers to the cached factory.
79
+ * Notifies subscribers, so must be called from an effect, never during render.
80
+ */
81
+ }, {
82
+ key: "syncProviders",
83
+ value: function syncProviders(resourceId, factory) {
84
+ var dataProvider = this.deps.getDataProvider();
85
+ if (!dataProvider) {
86
+ return;
87
+ }
88
+ var providerFactory = factory !== null && factory !== void 0 ? factory : this.providerFactories.get(resourceId);
89
+ if (!providerFactory) {
90
+ return;
91
+ }
92
+ var _dataProvider$getSync2 = dataProvider.getSyncedBlockRendererProviderOptions(),
93
+ parentDataProviders = _dataProvider$getSync2.parentDataProviders,
94
+ providerCreator = _dataProvider$getSync2.providerCreator;
95
+ if (parentDataProviders !== null && parentDataProviders !== void 0 && parentDataProviders.mentionProvider) {
96
+ providerFactory.setProvider('mentionProvider', parentDataProviders === null || parentDataProviders === void 0 ? void 0 : parentDataProviders.mentionProvider);
97
+ }
98
+ if (parentDataProviders !== null && parentDataProviders !== void 0 && parentDataProviders.profilecardProvider) {
99
+ providerFactory.setProvider('profilecardProvider', parentDataProviders === null || parentDataProviders === void 0 ? void 0 : parentDataProviders.profilecardProvider);
100
+ }
101
+ if (parentDataProviders !== null && parentDataProviders !== void 0 && parentDataProviders.taskDecisionProvider) {
102
+ providerFactory.setProvider('taskDecisionProvider', parentDataProviders === null || parentDataProviders === void 0 ? void 0 : parentDataProviders.taskDecisionProvider);
103
+ }
104
+ if (!providerCreator) {
105
+ return;
106
+ }
107
+ try {
108
+ this.retrieveDynamicProviders(resourceId, providerFactory, providerCreator);
109
+ } catch (error) {
110
+ var _this$deps$getFireAna2;
111
+ (0, _monitoring.logException)(error, {
112
+ location: 'editor-synced-block-provider/syncBlockProviderFactoryManager'
113
+ });
114
+ (_this$deps$getFireAna2 = this.deps.getFireAnalyticsEvent()) === null || _this$deps$getFireAna2 === void 0 || _this$deps$getFireAna2((0, _errorHandling.fetchErrorPayload)(error.message, resourceId, (0, _utils.getSourceProductFromResourceIdSafe)(resourceId), (0, _errorHandling.buildFetchErrorAttribution)((0, _platformFeatureFlags.fg)('platform_editor_blocks_patch_3'), error.message)));
115
+ }
116
+ }
75
117
  }, {
76
118
  key: "getSSRProviders",
77
119
  value: function getSSRProviders(resourceId) {
@@ -80,8 +122,8 @@ var SyncBlockProviderFactoryManager = exports.SyncBlockProviderFactoryManager =
80
122
  if (!dataProvider) {
81
123
  return null;
82
124
  }
83
- var _dataProvider$getSync2 = dataProvider.getSyncedBlockRendererProviderOptions(),
84
- providerCreator = _dataProvider$getSync2.providerCreator;
125
+ var _dataProvider$getSync3 = dataProvider.getSyncedBlockRendererProviderOptions(),
126
+ providerCreator = _dataProvider$getSync3.providerCreator;
85
127
  if (!(providerCreator !== null && providerCreator !== void 0 && providerCreator.createSSRMediaProvider)) {
86
128
  return null;
87
129
  }
@@ -6,7 +6,7 @@ import { isProviderNotReadyError, SyncBlockError } from '../common/types';
6
6
  import { buildFetchErrorAttribution, fetchErrorPayload, getPiiSafeOriginalError } from '../utils/errorHandling';
7
7
  import { createSyncBlockNode, getSourceProductFromResourceIdSafe } from '../utils/utils';
8
8
  export const useFetchSyncBlockData = (manager, resourceId, localId, fireAnalyticsEvent) => {
9
- var _manager$referenceMan2, _manager$referenceMan3, _manager$referenceMan4;
9
+ var _manager$referenceMan2, _manager$referenceMan3, _manager$referenceMan4, _manager$referenceMan5, _manager$referenceMan6, _manager$referenceMan7;
10
10
  // Initialize both states from a single cache lookup to avoid race conditions.
11
11
  // When a block is moved/remounted, the old component's cleanup may clear the cache
12
12
  // before or after the new component mounts. By doing a single lookup, we ensure
@@ -118,6 +118,28 @@ export const useFetchSyncBlockData = (manager, resourceId, localId, fireAnalytic
118
118
  unsubscribe();
119
119
  };
120
120
  }, [isDataProviderReady, localId, manager.referenceManager, resourceId]);
121
+
122
+ // Pure read, safe during render. The individual provider references are effect
123
+ // dependencies below so a provider swapped by the host is re-applied to the
124
+ // cached factory, which would otherwise keep serving the previous one.
125
+ const {
126
+ parentDataProviders,
127
+ providerCreator
128
+ } = (_manager$referenceMan5 = (_manager$referenceMan6 = (_manager$referenceMan7 = manager.referenceManager).getProviderOptions) === null || _manager$referenceMan6 === void 0 ? void 0 : _manager$referenceMan6.call(_manager$referenceMan7)) !== null && _manager$referenceMan5 !== void 0 ? _manager$referenceMan5 : {};
129
+
130
+ // Applying providers notifies subscribers synchronously, so it must not run during
131
+ // render - that would setState on another component mid-render and remount the block.
132
+ useEffect(() => {
133
+ if (!fg('platform_editor_blocks_patch_7')) {
134
+ return;
135
+ }
136
+ if (!resourceId || isSSR()) {
137
+ return;
138
+ }
139
+ manager.referenceManager.syncProviders(resourceId);
140
+ // `syncBlockInstance` is a dependency because the dynamic providers are derived
141
+ // from the fetched block data, so they can only be created once it has arrived.
142
+ }, [manager.referenceManager, resourceId, syncBlockInstance, parentDataProviders === null || parentDataProviders === void 0 ? void 0 : parentDataProviders.mentionProvider, parentDataProviders === null || parentDataProviders === void 0 ? void 0 : parentDataProviders.profilecardProvider, parentDataProviders === null || parentDataProviders === void 0 ? void 0 : parentDataProviders.taskDecisionProvider, providerCreator]);
121
143
  const ssrProviders = useMemo(() => {
122
144
  return resourceId ? manager.referenceManager.getSSRProviders(resourceId) : null;
123
145
  }, [resourceId, manager.referenceManager]);
@@ -828,6 +828,22 @@ export class ReferenceSyncBlockStoreManager {
828
828
  getProviderFactory(resourceId) {
829
829
  return this._providerFactoryManager.getProviderFactory(resourceId);
830
830
  }
831
+
832
+ /**
833
+ * The provider options currently supplied by the host. Safe to read during
834
+ * render - it notifies nobody.
835
+ */
836
+ getProviderOptions() {
837
+ return this._providerFactoryManager.getProviderOptions();
838
+ }
839
+
840
+ /**
841
+ * Applies the latest providers to the cached factory. Notifies subscribers, so
842
+ * call from an effect rather than during render.
843
+ */
844
+ syncProviders(resourceId) {
845
+ this._providerFactoryManager.syncProviders(resourceId);
846
+ }
831
847
  getSSRProviders(resourceId) {
832
848
  return this._providerFactoryManager.getSSRProviders(resourceId);
833
849
  }
@@ -1,4 +1,5 @@
1
1
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ import { isSSR } from '@atlaskit/editor-common/core-utils';
2
3
  import { logException } from '@atlaskit/editor-common/monitoring';
3
4
  import { ProviderFactory } from '@atlaskit/editor-common/provider-factory';
4
5
  import { fg } from '@atlaskit/platform-feature-flags';
@@ -25,42 +26,80 @@ export class SyncBlockProviderFactoryManager {
25
26
  (_this$deps$getFireAna = this.deps.getFireAnalyticsEvent()) === null || _this$deps$getFireAna === void 0 ? void 0 : _this$deps$getFireAna(fetchErrorPayload(error.message, resourceId, getSourceProductFromResourceIdSafe(resourceId), buildFetchErrorAttribution(fg('platform_editor_blocks_patch_3'), error.message)));
26
27
  return undefined;
27
28
  }
28
- const {
29
- parentDataProviders,
30
- providerCreator
31
- } = dataProvider.getSyncedBlockRendererProviderOptions();
32
29
  let providerFactory = this.providerFactories.get(resourceId);
33
30
  if (!providerFactory) {
31
+ const {
32
+ parentDataProviders
33
+ } = dataProvider.getSyncedBlockRendererProviderOptions();
34
34
  providerFactory = ProviderFactory.create({
35
35
  mentionProvider: parentDataProviders === null || parentDataProviders === void 0 ? void 0 : parentDataProviders.mentionProvider,
36
36
  profilecardProvider: parentDataProviders === null || parentDataProviders === void 0 ? void 0 : parentDataProviders.profilecardProvider,
37
37
  taskDecisionProvider: parentDataProviders === null || parentDataProviders === void 0 ? void 0 : parentDataProviders.taskDecisionProvider
38
38
  });
39
39
  this.providerFactories.set(resourceId, providerFactory);
40
- } else {
41
- if (parentDataProviders !== null && parentDataProviders !== void 0 && parentDataProviders.mentionProvider) {
42
- providerFactory.setProvider('mentionProvider', parentDataProviders === null || parentDataProviders === void 0 ? void 0 : parentDataProviders.mentionProvider);
43
- }
44
- if (parentDataProviders !== null && parentDataProviders !== void 0 && parentDataProviders.profilecardProvider) {
45
- providerFactory.setProvider('profilecardProvider', parentDataProviders === null || parentDataProviders === void 0 ? void 0 : parentDataProviders.profilecardProvider);
46
- }
47
- if (parentDataProviders !== null && parentDataProviders !== void 0 && parentDataProviders.taskDecisionProvider) {
48
- providerFactory.setProvider('taskDecisionProvider', parentDataProviders === null || parentDataProviders === void 0 ? void 0 : parentDataProviders.taskDecisionProvider);
49
- }
50
40
  }
51
- if (providerCreator) {
52
- try {
53
- this.retrieveDynamicProviders(resourceId, providerFactory, providerCreator);
54
- } catch (error) {
55
- var _this$deps$getFireAna2;
56
- logException(error, {
57
- location: 'editor-synced-block-provider/syncBlockProviderFactoryManager'
58
- });
59
- (_this$deps$getFireAna2 = this.deps.getFireAnalyticsEvent()) === null || _this$deps$getFireAna2 === void 0 ? void 0 : _this$deps$getFireAna2(fetchErrorPayload(error.message, resourceId, getSourceProductFromResourceIdSafe(resourceId), buildFetchErrorAttribution(fg('platform_editor_blocks_patch_3'), error.message)));
60
- }
41
+
42
+ // Syncing providers notifies subscribers synchronously, so on the client it
43
+ // cannot run during render - callers invoke `syncProviders` from an effect
44
+ // instead. Effects never run while server rendering, and there are no mounted
45
+ // subscribers to notify, so the server keeps applying them inline.
46
+ const deferProviderSync = fg('platform_editor_blocks_patch_7');
47
+ if (deferProviderSync && !isSSR()) {
48
+ return providerFactory;
61
49
  }
50
+ this.syncProviders(resourceId, providerFactory);
62
51
  return providerFactory;
63
52
  }
53
+
54
+ /**
55
+ * The provider options currently supplied by the host. A pure read that does
56
+ * not notify subscribers, so it is safe to call during render - callers use it
57
+ * to detect when the host swaps a provider and the factory needs re-syncing.
58
+ */
59
+ getProviderOptions() {
60
+ var _this$deps$getDataPro;
61
+ return (_this$deps$getDataPro = this.deps.getDataProvider()) === null || _this$deps$getDataPro === void 0 ? void 0 : _this$deps$getDataPro.getSyncedBlockRendererProviderOptions();
62
+ }
63
+
64
+ /**
65
+ * Applies the latest parent and dynamic providers to the cached factory.
66
+ * Notifies subscribers, so must be called from an effect, never during render.
67
+ */
68
+ syncProviders(resourceId, factory) {
69
+ const dataProvider = this.deps.getDataProvider();
70
+ if (!dataProvider) {
71
+ return;
72
+ }
73
+ const providerFactory = factory !== null && factory !== void 0 ? factory : this.providerFactories.get(resourceId);
74
+ if (!providerFactory) {
75
+ return;
76
+ }
77
+ const {
78
+ parentDataProviders,
79
+ providerCreator
80
+ } = dataProvider.getSyncedBlockRendererProviderOptions();
81
+ if (parentDataProviders !== null && parentDataProviders !== void 0 && parentDataProviders.mentionProvider) {
82
+ providerFactory.setProvider('mentionProvider', parentDataProviders === null || parentDataProviders === void 0 ? void 0 : parentDataProviders.mentionProvider);
83
+ }
84
+ if (parentDataProviders !== null && parentDataProviders !== void 0 && parentDataProviders.profilecardProvider) {
85
+ providerFactory.setProvider('profilecardProvider', parentDataProviders === null || parentDataProviders === void 0 ? void 0 : parentDataProviders.profilecardProvider);
86
+ }
87
+ if (parentDataProviders !== null && parentDataProviders !== void 0 && parentDataProviders.taskDecisionProvider) {
88
+ providerFactory.setProvider('taskDecisionProvider', parentDataProviders === null || parentDataProviders === void 0 ? void 0 : parentDataProviders.taskDecisionProvider);
89
+ }
90
+ if (!providerCreator) {
91
+ return;
92
+ }
93
+ try {
94
+ this.retrieveDynamicProviders(resourceId, providerFactory, providerCreator);
95
+ } catch (error) {
96
+ var _this$deps$getFireAna2;
97
+ logException(error, {
98
+ location: 'editor-synced-block-provider/syncBlockProviderFactoryManager'
99
+ });
100
+ (_this$deps$getFireAna2 = this.deps.getFireAnalyticsEvent()) === null || _this$deps$getFireAna2 === void 0 ? void 0 : _this$deps$getFireAna2(fetchErrorPayload(error.message, resourceId, getSourceProductFromResourceIdSafe(resourceId), buildFetchErrorAttribution(fg('platform_editor_blocks_patch_3'), error.message)));
101
+ }
102
+ }
64
103
  getSSRProviders(resourceId) {
65
104
  var _syncBlock$data, _syncBlock$data2;
66
105
  const dataProvider = this.deps.getDataProvider();
@@ -12,7 +12,7 @@ import { isProviderNotReadyError, SyncBlockError } from '../common/types';
12
12
  import { buildFetchErrorAttribution, fetchErrorPayload, getPiiSafeOriginalError } from '../utils/errorHandling';
13
13
  import { createSyncBlockNode, getSourceProductFromResourceIdSafe } from '../utils/utils';
14
14
  export var useFetchSyncBlockData = function useFetchSyncBlockData(manager, resourceId, localId, fireAnalyticsEvent) {
15
- var _manager$referenceMan2, _manager$referenceMan3, _manager$referenceMan4;
15
+ var _manager$referenceMan2, _manager$referenceMan3, _manager$referenceMan4, _manager$referenceMan5, _manager$referenceMan6, _manager$referenceMan7;
16
16
  // Initialize both states from a single cache lookup to avoid race conditions.
17
17
  // When a block is moved/remounted, the old component's cleanup may clear the cache
18
18
  // before or after the new component mounts. By doing a single lookup, we ensure
@@ -149,6 +149,27 @@ export var useFetchSyncBlockData = function useFetchSyncBlockData(manager, resou
149
149
  unsubscribe();
150
150
  };
151
151
  }, [isDataProviderReady, localId, manager.referenceManager, resourceId]);
152
+
153
+ // Pure read, safe during render. The individual provider references are effect
154
+ // dependencies below so a provider swapped by the host is re-applied to the
155
+ // cached factory, which would otherwise keep serving the previous one.
156
+ var _ref2 = (_manager$referenceMan5 = (_manager$referenceMan6 = (_manager$referenceMan7 = manager.referenceManager).getProviderOptions) === null || _manager$referenceMan6 === void 0 ? void 0 : _manager$referenceMan6.call(_manager$referenceMan7)) !== null && _manager$referenceMan5 !== void 0 ? _manager$referenceMan5 : {},
157
+ parentDataProviders = _ref2.parentDataProviders,
158
+ providerCreator = _ref2.providerCreator;
159
+
160
+ // Applying providers notifies subscribers synchronously, so it must not run during
161
+ // render - that would setState on another component mid-render and remount the block.
162
+ useEffect(function () {
163
+ if (!fg('platform_editor_blocks_patch_7')) {
164
+ return;
165
+ }
166
+ if (!resourceId || isSSR()) {
167
+ return;
168
+ }
169
+ manager.referenceManager.syncProviders(resourceId);
170
+ // `syncBlockInstance` is a dependency because the dynamic providers are derived
171
+ // from the fetched block data, so they can only be created once it has arrived.
172
+ }, [manager.referenceManager, resourceId, syncBlockInstance, parentDataProviders === null || parentDataProviders === void 0 ? void 0 : parentDataProviders.mentionProvider, parentDataProviders === null || parentDataProviders === void 0 ? void 0 : parentDataProviders.profilecardProvider, parentDataProviders === null || parentDataProviders === void 0 ? void 0 : parentDataProviders.taskDecisionProvider, providerCreator]);
152
173
  var ssrProviders = useMemo(function () {
153
174
  return resourceId ? manager.referenceManager.getSSRProviders(resourceId) : null;
154
175
  }, [resourceId, manager.referenceManager]);
@@ -989,6 +989,26 @@ export var ReferenceSyncBlockStoreManager = /*#__PURE__*/function () {
989
989
  value: function getProviderFactory(resourceId) {
990
990
  return this._providerFactoryManager.getProviderFactory(resourceId);
991
991
  }
992
+
993
+ /**
994
+ * The provider options currently supplied by the host. Safe to read during
995
+ * render - it notifies nobody.
996
+ */
997
+ }, {
998
+ key: "getProviderOptions",
999
+ value: function getProviderOptions() {
1000
+ return this._providerFactoryManager.getProviderOptions();
1001
+ }
1002
+
1003
+ /**
1004
+ * Applies the latest providers to the cached factory. Notifies subscribers, so
1005
+ * call from an effect rather than during render.
1006
+ */
1007
+ }, {
1008
+ key: "syncProviders",
1009
+ value: function syncProviders(resourceId) {
1010
+ this._providerFactoryManager.syncProviders(resourceId);
1011
+ }
992
1012
  }, {
993
1013
  key: "getSSRProviders",
994
1014
  value: function getSSRProviders(resourceId) {
@@ -1,6 +1,7 @@
1
1
  import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
2
2
  import _createClass from "@babel/runtime/helpers/createClass";
3
3
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
4
+ import { isSSR } from '@atlaskit/editor-common/core-utils';
4
5
  import { logException } from '@atlaskit/editor-common/monitoring';
5
6
  import { ProviderFactory } from '@atlaskit/editor-common/provider-factory';
6
7
  import { fg } from '@atlaskit/platform-feature-flags';
@@ -30,41 +31,82 @@ export var SyncBlockProviderFactoryManager = /*#__PURE__*/function () {
30
31
  (_this$deps$getFireAna = this.deps.getFireAnalyticsEvent()) === null || _this$deps$getFireAna === void 0 || _this$deps$getFireAna(fetchErrorPayload(error.message, resourceId, getSourceProductFromResourceIdSafe(resourceId), buildFetchErrorAttribution(fg('platform_editor_blocks_patch_3'), error.message)));
31
32
  return undefined;
32
33
  }
33
- var _dataProvider$getSync = dataProvider.getSyncedBlockRendererProviderOptions(),
34
- parentDataProviders = _dataProvider$getSync.parentDataProviders,
35
- providerCreator = _dataProvider$getSync.providerCreator;
36
34
  var providerFactory = this.providerFactories.get(resourceId);
37
35
  if (!providerFactory) {
36
+ var _dataProvider$getSync = dataProvider.getSyncedBlockRendererProviderOptions(),
37
+ parentDataProviders = _dataProvider$getSync.parentDataProviders;
38
38
  providerFactory = ProviderFactory.create({
39
39
  mentionProvider: parentDataProviders === null || parentDataProviders === void 0 ? void 0 : parentDataProviders.mentionProvider,
40
40
  profilecardProvider: parentDataProviders === null || parentDataProviders === void 0 ? void 0 : parentDataProviders.profilecardProvider,
41
41
  taskDecisionProvider: parentDataProviders === null || parentDataProviders === void 0 ? void 0 : parentDataProviders.taskDecisionProvider
42
42
  });
43
43
  this.providerFactories.set(resourceId, providerFactory);
44
- } else {
45
- if (parentDataProviders !== null && parentDataProviders !== void 0 && parentDataProviders.mentionProvider) {
46
- providerFactory.setProvider('mentionProvider', parentDataProviders === null || parentDataProviders === void 0 ? void 0 : parentDataProviders.mentionProvider);
47
- }
48
- if (parentDataProviders !== null && parentDataProviders !== void 0 && parentDataProviders.profilecardProvider) {
49
- providerFactory.setProvider('profilecardProvider', parentDataProviders === null || parentDataProviders === void 0 ? void 0 : parentDataProviders.profilecardProvider);
50
- }
51
- if (parentDataProviders !== null && parentDataProviders !== void 0 && parentDataProviders.taskDecisionProvider) {
52
- providerFactory.setProvider('taskDecisionProvider', parentDataProviders === null || parentDataProviders === void 0 ? void 0 : parentDataProviders.taskDecisionProvider);
53
- }
54
44
  }
55
- if (providerCreator) {
56
- try {
57
- this.retrieveDynamicProviders(resourceId, providerFactory, providerCreator);
58
- } catch (error) {
59
- var _this$deps$getFireAna2;
60
- logException(error, {
61
- location: 'editor-synced-block-provider/syncBlockProviderFactoryManager'
62
- });
63
- (_this$deps$getFireAna2 = this.deps.getFireAnalyticsEvent()) === null || _this$deps$getFireAna2 === void 0 || _this$deps$getFireAna2(fetchErrorPayload(error.message, resourceId, getSourceProductFromResourceIdSafe(resourceId), buildFetchErrorAttribution(fg('platform_editor_blocks_patch_3'), error.message)));
64
- }
45
+
46
+ // Syncing providers notifies subscribers synchronously, so on the client it
47
+ // cannot run during render - callers invoke `syncProviders` from an effect
48
+ // instead. Effects never run while server rendering, and there are no mounted
49
+ // subscribers to notify, so the server keeps applying them inline.
50
+ var deferProviderSync = fg('platform_editor_blocks_patch_7');
51
+ if (deferProviderSync && !isSSR()) {
52
+ return providerFactory;
65
53
  }
54
+ this.syncProviders(resourceId, providerFactory);
66
55
  return providerFactory;
67
56
  }
57
+
58
+ /**
59
+ * The provider options currently supplied by the host. A pure read that does
60
+ * not notify subscribers, so it is safe to call during render - callers use it
61
+ * to detect when the host swaps a provider and the factory needs re-syncing.
62
+ */
63
+ }, {
64
+ key: "getProviderOptions",
65
+ value: function getProviderOptions() {
66
+ var _this$deps$getDataPro;
67
+ return (_this$deps$getDataPro = this.deps.getDataProvider()) === null || _this$deps$getDataPro === void 0 ? void 0 : _this$deps$getDataPro.getSyncedBlockRendererProviderOptions();
68
+ }
69
+
70
+ /**
71
+ * Applies the latest parent and dynamic providers to the cached factory.
72
+ * Notifies subscribers, so must be called from an effect, never during render.
73
+ */
74
+ }, {
75
+ key: "syncProviders",
76
+ value: function syncProviders(resourceId, factory) {
77
+ var dataProvider = this.deps.getDataProvider();
78
+ if (!dataProvider) {
79
+ return;
80
+ }
81
+ var providerFactory = factory !== null && factory !== void 0 ? factory : this.providerFactories.get(resourceId);
82
+ if (!providerFactory) {
83
+ return;
84
+ }
85
+ var _dataProvider$getSync2 = dataProvider.getSyncedBlockRendererProviderOptions(),
86
+ parentDataProviders = _dataProvider$getSync2.parentDataProviders,
87
+ providerCreator = _dataProvider$getSync2.providerCreator;
88
+ if (parentDataProviders !== null && parentDataProviders !== void 0 && parentDataProviders.mentionProvider) {
89
+ providerFactory.setProvider('mentionProvider', parentDataProviders === null || parentDataProviders === void 0 ? void 0 : parentDataProviders.mentionProvider);
90
+ }
91
+ if (parentDataProviders !== null && parentDataProviders !== void 0 && parentDataProviders.profilecardProvider) {
92
+ providerFactory.setProvider('profilecardProvider', parentDataProviders === null || parentDataProviders === void 0 ? void 0 : parentDataProviders.profilecardProvider);
93
+ }
94
+ if (parentDataProviders !== null && parentDataProviders !== void 0 && parentDataProviders.taskDecisionProvider) {
95
+ providerFactory.setProvider('taskDecisionProvider', parentDataProviders === null || parentDataProviders === void 0 ? void 0 : parentDataProviders.taskDecisionProvider);
96
+ }
97
+ if (!providerCreator) {
98
+ return;
99
+ }
100
+ try {
101
+ this.retrieveDynamicProviders(resourceId, providerFactory, providerCreator);
102
+ } catch (error) {
103
+ var _this$deps$getFireAna2;
104
+ logException(error, {
105
+ location: 'editor-synced-block-provider/syncBlockProviderFactoryManager'
106
+ });
107
+ (_this$deps$getFireAna2 = this.deps.getFireAnalyticsEvent()) === null || _this$deps$getFireAna2 === void 0 || _this$deps$getFireAna2(fetchErrorPayload(error.message, resourceId, getSourceProductFromResourceIdSafe(resourceId), buildFetchErrorAttribution(fg('platform_editor_blocks_patch_3'), error.message)));
108
+ }
109
+ }
68
110
  }, {
69
111
  key: "getSSRProviders",
70
112
  value: function getSSRProviders(resourceId) {
@@ -73,8 +115,8 @@ export var SyncBlockProviderFactoryManager = /*#__PURE__*/function () {
73
115
  if (!dataProvider) {
74
116
  return null;
75
117
  }
76
- var _dataProvider$getSync2 = dataProvider.getSyncedBlockRendererProviderOptions(),
77
- providerCreator = _dataProvider$getSync2.providerCreator;
118
+ var _dataProvider$getSync3 = dataProvider.getSyncedBlockRendererProviderOptions(),
119
+ providerCreator = _dataProvider$getSync3.providerCreator;
78
120
  if (!(providerCreator !== null && providerCreator !== void 0 && providerCreator.createSSRMediaProvider)) {
79
121
  return null;
80
122
  }
@@ -4,7 +4,7 @@ import type { ProviderFactory, MediaProvider } from '@atlaskit/editor-common/pro
4
4
  import type { ViewMode } from '@atlaskit/editor-plugin-editor-viewmode';
5
5
  import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
6
6
  import type { BlockInstanceId, ResourceId, SyncBlockNode, SyncBlockPrefetchData } from '../common/types';
7
- import type { SyncBlockInstance, SubscriptionCallback, SyncBlockDataProviderInterface, TitleSubscriptionCallback, SyncBlockSourceInfo } from '../providers/types';
7
+ import type { SyncBlockInstance, SubscriptionCallback, SyncBlockDataProviderInterface, TitleSubscriptionCallback, SyncBlockSourceInfo, SyncedBlockRendererProviderOptions } from '../providers/types';
8
8
  export declare class ReferenceSyncBlockStoreManager {
9
9
  private viewMode?;
10
10
  private dataProvider?;
@@ -142,6 +142,16 @@ export declare class ReferenceSyncBlockStoreManager {
142
142
  */
143
143
  getSyncBlockURL(resourceId: ResourceId): string | undefined;
144
144
  getProviderFactory(resourceId: ResourceId): ProviderFactory | undefined;
145
+ /**
146
+ * The provider options currently supplied by the host. Safe to read during
147
+ * render - it notifies nobody.
148
+ */
149
+ getProviderOptions(): SyncedBlockRendererProviderOptions | undefined;
150
+ /**
151
+ * Applies the latest providers to the cached factory. Notifies subscribers, so
152
+ * call from an effect rather than during render.
153
+ */
154
+ syncProviders(resourceId: ResourceId): void;
145
155
  getSSRProviders(resourceId: ResourceId): {
146
156
  media: MediaProvider;
147
157
  } | null;
@@ -2,7 +2,7 @@ import type { RendererSyncBlockEventPayload } from '@atlaskit/editor-common/anal
2
2
  import { ProviderFactory } from '@atlaskit/editor-common/provider-factory';
3
3
  import type { MediaProvider } from '@atlaskit/editor-common/provider-factory';
4
4
  import type { ResourceId } from '../common/types';
5
- import type { SyncBlockInstance, SyncBlockDataProviderInterface } from '../providers/types';
5
+ import type { SyncBlockInstance, SyncBlockDataProviderInterface, SyncedBlockRendererProviderOptions } from '../providers/types';
6
6
  export interface SyncBlockProviderFactoryManagerDeps {
7
7
  getDataProvider: () => SyncBlockDataProviderInterface | undefined;
8
8
  getFireAnalyticsEvent: () => ((payload: RendererSyncBlockEventPayload) => void) | undefined;
@@ -17,6 +17,17 @@ export declare class SyncBlockProviderFactoryManager {
17
17
  private providerFactories;
18
18
  constructor(deps: SyncBlockProviderFactoryManagerDeps);
19
19
  getProviderFactory(resourceId: ResourceId): ProviderFactory | undefined;
20
+ /**
21
+ * The provider options currently supplied by the host. A pure read that does
22
+ * not notify subscribers, so it is safe to call during render - callers use it
23
+ * to detect when the host swaps a provider and the factory needs re-syncing.
24
+ */
25
+ getProviderOptions(): SyncedBlockRendererProviderOptions | undefined;
26
+ /**
27
+ * Applies the latest parent and dynamic providers to the cached factory.
28
+ * Notifies subscribers, so must be called from an effect, never during render.
29
+ */
30
+ syncProviders(resourceId: ResourceId, factory?: ProviderFactory): void;
20
31
  getSSRProviders(resourceId: ResourceId): {
21
32
  media: MediaProvider;
22
33
  } | null;
package/package.json CHANGED
@@ -22,7 +22,7 @@
22
22
  "@atlaskit/editor-prosemirror": "^8.0.0",
23
23
  "@atlaskit/node-data-provider": "^14.0.0",
24
24
  "@atlaskit/platform-feature-flags": "^2.1.0",
25
- "@atlaskit/tmp-editor-statsig": "^140.0.0",
25
+ "@atlaskit/tmp-editor-statsig": "^141.1.0",
26
26
  "@babel/runtime": "^7.0.0",
27
27
  "@compiled/react": "^1.0.0",
28
28
  "bind-event-listener": "^3.0.0",
@@ -32,7 +32,7 @@
32
32
  "uuid": "^3.1.0"
33
33
  },
34
34
  "peerDependencies": {
35
- "@atlaskit/editor-common": "^117.2.0",
35
+ "@atlaskit/editor-common": "^117.3.0",
36
36
  "react": "^18.2.0 || ^19.2.0"
37
37
  },
38
38
  "devDependencies": {
@@ -76,7 +76,7 @@
76
76
  }
77
77
  },
78
78
  "name": "@atlaskit/editor-synced-block-provider",
79
- "version": "9.0.2",
79
+ "version": "9.1.0",
80
80
  "description": "Synced Block Provider for @atlaskit/editor-plugin-synced-block",
81
81
  "author": "Atlassian Pty Ltd",
82
82
  "license": "Apache-2.0",
@@ -95,6 +95,9 @@
95
95
  },
96
96
  "platform_synced_block_patch_14": {
97
97
  "type": "boolean"
98
+ },
99
+ "platform_editor_blocks_patch_7": {
100
+ "type": "boolean"
98
101
  }
99
102
  }
100
103
  }