@atlaskit/editor-plugin-synced-block 14.0.11 → 14.0.12

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,14 @@
1
1
  # @atlaskit/editor-plugin-synced-block
2
2
 
3
+ ## 14.0.12
4
+
5
+ ### Patch Changes
6
+
7
+ - [`90712d67e0e35`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/90712d67e0e35) -
8
+ Show local unpublished synced block content and publication guidance behind
9
+ editor-synced-block-same-page-sync.
10
+ - Updated dependencies
11
+
3
12
  ## 14.0.11
4
13
 
5
14
  ### Patch Changes
@@ -1,20 +1,25 @@
1
1
  "use strict";
2
2
 
3
3
  var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ var _typeof = require("@babel/runtime/helpers/typeof");
4
5
  Object.defineProperty(exports, "__esModule", {
5
6
  value: true
6
7
  });
7
- exports.bodiedSyncBlockNodeView = exports.BodiedSyncBlock = void 0;
8
+ exports.bodiedSyncBlockNodeView = exports.SourceSyncBlockLabel = exports.BodiedSyncBlock = void 0;
8
9
  var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
9
10
  var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
10
- var _react = _interopRequireDefault(require("react"));
11
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
12
+ var _react = _interopRequireWildcard(require("react"));
11
13
  var _analytics = require("@atlaskit/editor-common/analytics");
12
14
  var _errorBoundary = require("@atlaskit/editor-common/error-boundary");
13
15
  var _syncBlock = require("@atlaskit/editor-common/sync-block");
14
16
  var _editorPluginConnectivity = require("@atlaskit/editor-plugin-connectivity");
15
17
  var _model = require("@atlaskit/editor-prosemirror/model");
18
+ var _isExperimentEnabled = require("@atlaskit/platform-feature-experiments/is-experiment-enabled");
19
+ var _getUnpublishedSourceType = require("../ui/getUnpublishedSourceType");
16
20
  var _SyncBlockLabel = require("../ui/SyncBlockLabel");
17
21
  var _isEmptySourceSyncBlock = require("./isEmptySourceSyncBlock");
22
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
18
23
  var toDOM = function toDOM(node) {
19
24
  return ['div', {
20
25
  class: "".concat(_syncBlock.BodiedSyncBlockSharedCssClassName.prefix, " bodiedSyncBlockView-content-wrap"),
@@ -25,8 +30,54 @@ var toDOM = function toDOM(node) {
25
30
  contenteditable: 'true'
26
31
  }, 0]];
27
32
  };
33
+ var SourceSyncBlockLabel = exports.SourceSyncBlockLabel = function SourceSyncBlockLabel(_ref) {
34
+ var localId = _ref.localId,
35
+ resourceId = _ref.resourceId,
36
+ sourceManager = _ref.sourceManager;
37
+ var isSamePageSyncEnabled = (0, _isExperimentEnabled.isExperimentEnabled)('editor-synced-block-same-page-sync');
38
+ var _useState = (0, _react.useState)(function () {
39
+ var _sourceManager$getLoc;
40
+ return isSamePageSyncEnabled && (sourceManager === null || sourceManager === void 0 || (_sourceManager$getLoc = sourceManager.getLocalSourceSnapshot(resourceId)) === null || _sourceManager$getLoc === void 0 ? void 0 : _sourceManager$getLoc.status) === 'unpublished';
41
+ }),
42
+ _useState2 = (0, _slicedToArray2.default)(_useState, 2),
43
+ isUnpublished = _useState2[0],
44
+ setIsUnpublished = _useState2[1];
45
+ var _useState3 = (0, _react.useState)(),
46
+ _useState4 = (0, _slicedToArray2.default)(_useState3, 2),
47
+ sourceInfo = _useState4[0],
48
+ setSourceInfo = _useState4[1];
49
+ (0, _react.useEffect)(function () {
50
+ if (!isSamePageSyncEnabled) {
51
+ return;
52
+ }
53
+ var isMounted = true;
54
+ void (sourceManager === null || sourceManager === void 0 ? void 0 : sourceManager.getSyncBlockSourceInfo(localId).then(function (nextSourceInfo) {
55
+ if (isMounted) {
56
+ setSourceInfo(nextSourceInfo);
57
+ }
58
+ }));
59
+ var unsubscribe = sourceManager === null || sourceManager === void 0 ? void 0 : sourceManager.subscribeToLocalSource(resourceId, function (snapshot) {
60
+ return setIsUnpublished((snapshot === null || snapshot === void 0 ? void 0 : snapshot.status) === 'unpublished');
61
+ });
62
+ return function () {
63
+ isMounted = false;
64
+ unsubscribe === null || unsubscribe === void 0 || unsubscribe();
65
+ };
66
+ }, [isSamePageSyncEnabled, localId, resourceId, sourceManager]);
67
+ return /*#__PURE__*/_react.default.createElement(_SyncBlockLabel.SyncBlockLabel, {
68
+ isSource: true,
69
+ localId: localId,
70
+ unpublishedInfo: isSamePageSyncEnabled && isUnpublished ? {
71
+ sourceType: (0, _getUnpublishedSourceType.getUnpublishedSourceType)({
72
+ sourceAri: sourceInfo === null || sourceInfo === void 0 ? void 0 : sourceInfo.sourceAri,
73
+ sourceProduct: sourceInfo === null || sourceInfo === void 0 ? void 0 : sourceInfo.productType
74
+ }),
75
+ variant: 'source'
76
+ } : undefined
77
+ });
78
+ };
28
79
  var BodiedSyncBlock = exports.BodiedSyncBlock = /*#__PURE__*/function () {
29
- function BodiedSyncBlock(node, view, getPos, api, nodeViewPortalProviderAPI) {
80
+ function BodiedSyncBlock(node, view, getPos, api, nodeViewPortalProviderAPI, sourceManager) {
30
81
  var _this = this;
31
82
  (0, _classCallCheck2.default)(this, BodiedSyncBlock);
32
83
  this.node = node;
@@ -57,9 +108,10 @@ var BodiedSyncBlock = exports.BodiedSyncBlock = /*#__PURE__*/function () {
57
108
  componentId: _analytics.ACTION_SUBJECT_ID.SYNCED_BLOCK_LABEL,
58
109
  dispatchAnalyticsEvent: (_this$api = _this.api) === null || _this$api === void 0 || (_this$api = _this$api.analytics) === null || _this$api === void 0 ? void 0 : _this$api.actions.fireAnalyticsEvent,
59
110
  fallbackComponent: null
60
- }, /*#__PURE__*/_react.default.createElement(_SyncBlockLabel.SyncBlockLabel, {
61
- isSource: true,
62
- localId: node.attrs.localId
111
+ }, /*#__PURE__*/_react.default.createElement(SourceSyncBlockLabel, {
112
+ localId: node.attrs.localId,
113
+ resourceId: node.attrs.resourceId,
114
+ sourceManager: sourceManager
63
115
  }));
64
116
  }, labelContainer, this.labelKey);
65
117
  this.updateContentEditable({});
@@ -72,10 +124,10 @@ var BodiedSyncBlock = exports.BodiedSyncBlock = /*#__PURE__*/function () {
72
124
  }
73
125
  return (0, _createClass2.default)(BodiedSyncBlock, [{
74
126
  key: "updateContentEditable",
75
- value: function updateContentEditable(_ref) {
127
+ value: function updateContentEditable(_ref2) {
76
128
  var _this$api2, _this$api3;
77
- var nextConnectivityMode = _ref.nextConnectivityMode,
78
- nextViewMode = _ref.nextViewMode;
129
+ var nextConnectivityMode = _ref2.nextConnectivityMode,
130
+ nextViewMode = _ref2.nextViewMode;
79
131
  var connectivityMode = nextConnectivityMode !== null && nextConnectivityMode !== void 0 ? nextConnectivityMode : (_this$api2 = this.api) === null || _this$api2 === void 0 || (_this$api2 = _this$api2.connectivity) === null || _this$api2 === void 0 || (_this$api2 = _this$api2.sharedState) === null || _this$api2 === void 0 || (_this$api2 = _this$api2.currentState()) === null || _this$api2 === void 0 ? void 0 : _this$api2.mode;
80
132
  var viewMode = nextViewMode !== null && nextViewMode !== void 0 ? nextViewMode : (_this$api3 = this.api) === null || _this$api3 === void 0 || (_this$api3 = _this$api3.editorViewMode) === null || _this$api3 === void 0 || (_this$api3 = _this$api3.sharedState) === null || _this$api3 === void 0 || (_this$api3 = _this$api3.currentState()) === null || _this$api3 === void 0 ? void 0 : _this$api3.mode;
81
133
  var isOnline = !(0, _editorPluginConnectivity.isOfflineMode)(connectivityMode);
@@ -89,8 +141,8 @@ var BodiedSyncBlock = exports.BodiedSyncBlock = /*#__PURE__*/function () {
89
141
  var _this$api4,
90
142
  _this2 = this;
91
143
  if ((_this$api4 = this.api) !== null && _this$api4 !== void 0 && _this$api4.connectivity) {
92
- this.cleanupConnectivityModeListener = this.api.connectivity.sharedState.onChange(function (_ref2) {
93
- var nextSharedState = _ref2.nextSharedState;
144
+ this.cleanupConnectivityModeListener = this.api.connectivity.sharedState.onChange(function (_ref3) {
145
+ var nextSharedState = _ref3.nextSharedState;
94
146
  _this2.updateContentEditable({
95
147
  nextConnectivityMode: nextSharedState.mode
96
148
  });
@@ -103,8 +155,8 @@ var BodiedSyncBlock = exports.BodiedSyncBlock = /*#__PURE__*/function () {
103
155
  var _this$api5,
104
156
  _this3 = this;
105
157
  if ((_this$api5 = this.api) !== null && _this$api5 !== void 0 && _this$api5.editorViewMode) {
106
- this.cleanupViewModeListener = this.api.editorViewMode.sharedState.onChange(function (_ref3) {
107
- var nextSharedState = _ref3.nextSharedState;
158
+ this.cleanupViewModeListener = this.api.editorViewMode.sharedState.onChange(function (_ref4) {
159
+ var nextSharedState = _ref4.nextSharedState;
108
160
  _this3.updateContentEditable({
109
161
  nextViewMode: nextSharedState === null || nextSharedState === void 0 ? void 0 : nextSharedState.mode
110
162
  });
@@ -154,8 +206,9 @@ var BodiedSyncBlock = exports.BodiedSyncBlock = /*#__PURE__*/function () {
154
206
  }();
155
207
  var bodiedSyncBlockNodeView = exports.bodiedSyncBlockNodeView = function bodiedSyncBlockNodeView(props) {
156
208
  var api = props.api,
157
- nodeViewPortalProviderAPI = props.pmPluginFactoryParams.nodeViewPortalProviderAPI;
209
+ nodeViewPortalProviderAPI = props.pmPluginFactoryParams.nodeViewPortalProviderAPI,
210
+ syncBlockStore = props.syncBlockStore;
158
211
  return function (node, view, getPos) {
159
- return new BodiedSyncBlock(node, view, getPos, api, nodeViewPortalProviderAPI);
212
+ return new BodiedSyncBlock(node, view, getPos, api, nodeViewPortalProviderAPI, syncBlockStore === null || syncBlockStore === void 0 ? void 0 : syncBlockStore.sourceManager);
160
213
  };
161
214
  };
@@ -9,8 +9,10 @@ exports.SyncBlockLabel = void 0;
9
9
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
10
10
  var _react = _interopRequireWildcard(require("react"));
11
11
  var _reactIntl = require("react-intl");
12
+ var _button = _interopRequireDefault(require("@atlaskit/button/icon/button"));
12
13
  var _messages = require("@atlaskit/editor-common/messages");
13
14
  var _syncBlock = require("@atlaskit/editor-common/sync-block");
15
+ var _statusInformation = _interopRequireDefault(require("@atlaskit/icon/core/status-information"));
14
16
  var _blockSynced = _interopRequireDefault(require("@atlaskit/icon-lab/core/block-synced"));
15
17
  var _compiled = require("@atlaskit/primitives/compiled");
16
18
  var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
@@ -24,7 +26,8 @@ var SyncBlockLabelComponent = function SyncBlockLabelComponent(_ref) {
24
26
  isSource = _ref.isSource,
25
27
  localId = _ref.localId,
26
28
  title = _ref.title,
27
- isUnsyncedBlock = _ref.isUnsyncedBlock;
29
+ isUnsyncedBlock = _ref.isUnsyncedBlock,
30
+ unpublishedInfo = _ref.unpublishedInfo;
28
31
  var intl = (0, _reactIntl.useIntl)();
29
32
  var formatMessage = intl.formatMessage;
30
33
  var _useState = (0, _react.useState)(formatMessage(_messages.syncBlockMessages.defaultSyncBlockTooltip)),
@@ -88,21 +91,30 @@ var SyncBlockLabelComponent = function SyncBlockLabelComponent(_ref) {
88
91
  color: "color.text.subtle"
89
92
  }, title);
90
93
  }, [formatMessage, isSource, isUnsyncedBlock, title]);
91
- var label = /*#__PURE__*/_react.default.createElement("div", {
94
+ var unpublishedTooltipLabel = unpublishedInfo ? formatMessage(unpublishedInfo.variant === 'source' ? _messages.syncBlockMessages.unpublishedSourceAvailabilityTooltip : _messages.syncBlockMessages.unpublishedLocalReferenceAvailabilityTooltip, {
95
+ sourceType: unpublishedInfo.sourceType
96
+ }) : undefined;
97
+ var primaryLabel = /*#__PURE__*/_react.default.createElement("div", {
92
98
  "data-testid": SyncBlockLabelDataId
93
99
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop
94
100
  ,
95
101
  className: _syncBlock.SyncBlockLabelSharedCssClassName.labelClassName,
96
102
  "aria-describedby": isSource || isUnsyncedBlock ? undefined : ariaDescribedById
97
- }, /*#__PURE__*/_react.default.createElement(_blockSynced.default, {
103
+ }, unpublishedTooltipLabel ? /*#__PURE__*/_react.default.createElement(_Tooltip.default, {
104
+ position: "top",
105
+ content: unpublishedTooltipLabel
106
+ }, /*#__PURE__*/_react.default.createElement(_button.default, {
107
+ appearance: "subtle",
108
+ icon: _statusInformation.default,
109
+ label: unpublishedTooltipLabel,
110
+ spacing: "compact",
111
+ testId: "sync-block-unpublished-information"
112
+ })) : null, /*#__PURE__*/_react.default.createElement(_blockSynced.default, {
98
113
  color: "var(--ds-icon-subtle, #505258)",
99
114
  size: "small",
100
115
  label: ""
101
116
  }), getLabelContent);
102
- if (isSource || isUnsyncedBlock) {
103
- return label;
104
- }
105
- return /*#__PURE__*/_react.default.createElement(_Tooltip.default, {
117
+ var labelWithExistingTooltip = isSource || isUnsyncedBlock ? primaryLabel : /*#__PURE__*/_react.default.createElement(_Tooltip.default, {
106
118
  position: "top",
107
119
  content: tooltipContent
108
120
  // workaround because tooltip adds aria-describedby with a new id every time the tooltip is opened
@@ -113,8 +125,9 @@ var SyncBlockLabelComponent = function SyncBlockLabelComponent(_ref) {
113
125
  // using this to ensure that the 'last edited' time is updated when the tooltip is opened
114
126
  ,
115
127
  onShow: updateTooltipContent
116
- }, label, /*#__PURE__*/_react.default.createElement(_visuallyHidden.default, {
128
+ }, primaryLabel, /*#__PURE__*/_react.default.createElement(_visuallyHidden.default, {
117
129
  id: ariaDescribedById
118
130
  }, (0, _expValEquals.expValEquals)('platform_editor_sync_block_activation', 'isEnabled', true) ? tooltipMessage : tooltipContent));
131
+ return labelWithExistingTooltip;
119
132
  };
120
133
  var SyncBlockLabel = exports.SyncBlockLabel = /*#__PURE__*/_react.default.memo(SyncBlockLabelComponent);
@@ -1,31 +1,58 @@
1
1
  "use strict";
2
2
 
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
3
4
  var _typeof = require("@babel/runtime/helpers/typeof");
4
5
  Object.defineProperty(exports, "__esModule", {
5
6
  value: true
6
7
  });
7
8
  exports.SyncBlockRendererWrapper = void 0;
9
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
8
10
  var _react = _interopRequireWildcard(require("react"));
9
11
  var _bindEventListener = require("bind-event-listener");
10
12
  var _syncBlock = require("@atlaskit/editor-common/sync-block");
13
+ var _isExperimentEnabled = require("@atlaskit/platform-feature-experiments/is-experiment-enabled");
11
14
  var _editorSyncedBlockProvider = require("@atlaskit/editor-synced-block-provider");
12
15
  var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
16
+ var _getUnpublishedSourceType = require("./getUnpublishedSourceType");
13
17
  var _SyncBlockLabel = require("./SyncBlockLabel");
14
18
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
15
19
  var SyncBlockRendererWrapperDataId = 'sync-block-plugin-renderer-wrapper';
16
20
  var SyncBlockRendererWrapperComponent = function SyncBlockRendererWrapperComponent(_ref) {
17
- var _api$analytics, _syncBlockFetchResult, _syncBlockFetchResult2, _syncBlockFetchResult3;
21
+ var _api$analytics, _syncBlockFetchResult, _syncBlockFetchResult2, _syncBlockFetchResult3, _syncBlockFetchResult4, _localSourceInfo$sour, _syncBlockFetchResult5, _localSourceInfo$prod;
18
22
  var syncedBlockRenderer = _ref.syncedBlockRenderer,
19
23
  syncBlockStore = _ref.syncBlockStore,
20
24
  node = _ref.node,
21
25
  resourceId = _ref.resourceId,
22
26
  localId = _ref.localId,
23
27
  api = _ref.api;
28
+ var isSamePageSyncEnabled = (0, _isExperimentEnabled.isExperimentEnabled)('editor-synced-block-same-page-sync');
24
29
  var syncBlockFetchResult = (0, _editorSyncedBlockProvider.useFetchSyncBlockData)(syncBlockStore, resourceId, localId, api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 || (_api$analytics = _api$analytics.actions) === null || _api$analytics === void 0 ? void 0 : _api$analytics.fireAnalyticsEvent);
25
30
  var title = (0, _editorSyncedBlockProvider.useFetchSyncBlockTitle)(syncBlockStore, node);
26
31
  var contentUpdatedAt = syncBlockFetchResult === null || syncBlockFetchResult === void 0 || (_syncBlockFetchResult = syncBlockFetchResult.syncBlockInstance) === null || _syncBlockFetchResult === void 0 || (_syncBlockFetchResult = _syncBlockFetchResult.data) === null || _syncBlockFetchResult === void 0 ? void 0 : _syncBlockFetchResult.contentUpdatedAt;
27
32
  var isUnpublishedBlock = ((_syncBlockFetchResult2 = syncBlockFetchResult.syncBlockInstance) === null || _syncBlockFetchResult2 === void 0 || (_syncBlockFetchResult2 = _syncBlockFetchResult2.data) === null || _syncBlockFetchResult2 === void 0 ? void 0 : _syncBlockFetchResult2.status) === 'unpublished';
28
- var isUnsyncedBlock = isUnpublishedBlock || (syncBlockFetchResult === null || syncBlockFetchResult === void 0 || (_syncBlockFetchResult3 = syncBlockFetchResult.syncBlockInstance) === null || _syncBlockFetchResult3 === void 0 || (_syncBlockFetchResult3 = _syncBlockFetchResult3.error) === null || _syncBlockFetchResult3 === void 0 ? void 0 : _syncBlockFetchResult3.type) === _editorSyncedBlockProvider.SyncBlockError.NotFound;
33
+ var localSameDocumentSource = isSamePageSyncEnabled ? (_syncBlockFetchResult3 = syncBlockFetchResult.syncBlockInstance) === null || _syncBlockFetchResult3 === void 0 ? void 0 : _syncBlockFetchResult3.localSameDocumentSource : undefined;
34
+ var localSourceBlockInstanceId = localSameDocumentSource === null || localSameDocumentSource === void 0 ? void 0 : localSameDocumentSource.sourceBlockInstanceId;
35
+ var _useState = (0, _react.useState)(),
36
+ _useState2 = (0, _slicedToArray2.default)(_useState, 2),
37
+ localSourceInfo = _useState2[0],
38
+ setLocalSourceInfo = _useState2[1];
39
+ (0, _react.useEffect)(function () {
40
+ var _syncBlockStore$sourc;
41
+ var isMounted = true;
42
+ if (!localSourceBlockInstanceId) {
43
+ setLocalSourceInfo(undefined);
44
+ return;
45
+ }
46
+ void ((_syncBlockStore$sourc = syncBlockStore.sourceManager) === null || _syncBlockStore$sourc === void 0 ? void 0 : _syncBlockStore$sourc.getSyncBlockSourceInfo(localSourceBlockInstanceId).then(function (sourceInfo) {
47
+ if (isMounted) {
48
+ setLocalSourceInfo(sourceInfo);
49
+ }
50
+ }));
51
+ return function () {
52
+ isMounted = false;
53
+ };
54
+ }, [localSourceBlockInstanceId, syncBlockStore.sourceManager]);
55
+ var isUnsyncedBlock = isUnpublishedBlock && !localSameDocumentSource || (syncBlockFetchResult === null || syncBlockFetchResult === void 0 || (_syncBlockFetchResult4 = syncBlockFetchResult.syncBlockInstance) === null || _syncBlockFetchResult4 === void 0 || (_syncBlockFetchResult4 = _syncBlockFetchResult4.error) === null || _syncBlockFetchResult4 === void 0 ? void 0 : _syncBlockFetchResult4.type) === _editorSyncedBlockProvider.SyncBlockError.NotFound;
29
56
 
30
57
  // Evaluated unconditionally so the experiment exposure is tracked correctly
31
58
  // (recorded on every render, not lazily on first click).
@@ -95,7 +122,14 @@ var SyncBlockRendererWrapperComponent = function SyncBlockRendererWrapperCompone
95
122
  title: title,
96
123
  contentUpdatedAt: contentUpdatedAt,
97
124
  localId: localId,
98
- isUnsyncedBlock: isUnsyncedBlock
125
+ isUnsyncedBlock: isUnsyncedBlock,
126
+ unpublishedInfo: localSameDocumentSource && isUnpublishedBlock ? {
127
+ sourceType: (0, _getUnpublishedSourceType.getUnpublishedSourceType)({
128
+ sourceAri: (_localSourceInfo$sour = localSourceInfo === null || localSourceInfo === void 0 ? void 0 : localSourceInfo.sourceAri) !== null && _localSourceInfo$sour !== void 0 ? _localSourceInfo$sour : (_syncBlockFetchResult5 = syncBlockFetchResult.syncBlockInstance) === null || _syncBlockFetchResult5 === void 0 || (_syncBlockFetchResult5 = _syncBlockFetchResult5.data) === null || _syncBlockFetchResult5 === void 0 ? void 0 : _syncBlockFetchResult5.sourceAri,
129
+ sourceProduct: (_localSourceInfo$prod = localSourceInfo === null || localSourceInfo === void 0 ? void 0 : localSourceInfo.productType) !== null && _localSourceInfo$prod !== void 0 ? _localSourceInfo$prod : localSameDocumentSource.sourceProduct
130
+ }),
131
+ variant: 'local-reference'
132
+ } : undefined
99
133
  }));
100
134
  };
101
135
  var SyncBlockRendererWrapper = exports.SyncBlockRendererWrapper = /*#__PURE__*/_react.default.memo(SyncBlockRendererWrapperComponent);
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.getUnpublishedSourceType = void 0;
7
+ var getUnpublishedSourceType = exports.getUnpublishedSourceType = function getUnpublishedSourceType(_ref) {
8
+ var sourceAri = _ref.sourceAri,
9
+ sourceProduct = _ref.sourceProduct;
10
+ if (sourceProduct === 'jira-work-item' || sourceAri !== null && sourceAri !== void 0 && sourceAri.startsWith('ari:cloud:jira:')) {
11
+ return 'jiraWorkItem';
12
+ }
13
+ if (sourceAri !== null && sourceAri !== void 0 && sourceAri.includes(':blogpost/')) {
14
+ return 'blog';
15
+ }
16
+ return 'page';
17
+ };
@@ -1,9 +1,11 @@
1
- import React from 'react';
1
+ import React, { useEffect, useState } from 'react';
2
2
  import { ACTION_SUBJECT, ACTION_SUBJECT_ID } from '@atlaskit/editor-common/analytics';
3
3
  import { ErrorBoundary } from '@atlaskit/editor-common/error-boundary';
4
4
  import { BodiedSyncBlockSharedCssClassName } from '@atlaskit/editor-common/sync-block';
5
5
  import { isOfflineMode } from '@atlaskit/editor-plugin-connectivity';
6
6
  import { DOMSerializer } from '@atlaskit/editor-prosemirror/model';
7
+ import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
8
+ import { getUnpublishedSourceType } from '../ui/getUnpublishedSourceType';
7
9
  import { SyncBlockLabel } from '../ui/SyncBlockLabel';
8
10
  import { isEmptySourceSyncBlock } from './isEmptySourceSyncBlock';
9
11
  const toDOM = node => ['div', {
@@ -14,8 +16,47 @@ const toDOM = node => ['div', {
14
16
  class: BodiedSyncBlockSharedCssClassName.content,
15
17
  contenteditable: 'true'
16
18
  }, 0]];
19
+ export const SourceSyncBlockLabel = ({
20
+ localId,
21
+ resourceId,
22
+ sourceManager
23
+ }) => {
24
+ const isSamePageSyncEnabled = isExperimentEnabled('editor-synced-block-same-page-sync');
25
+ const [isUnpublished, setIsUnpublished] = useState(() => {
26
+ var _sourceManager$getLoc;
27
+ return isSamePageSyncEnabled && (sourceManager === null || sourceManager === void 0 ? void 0 : (_sourceManager$getLoc = sourceManager.getLocalSourceSnapshot(resourceId)) === null || _sourceManager$getLoc === void 0 ? void 0 : _sourceManager$getLoc.status) === 'unpublished';
28
+ });
29
+ const [sourceInfo, setSourceInfo] = useState();
30
+ useEffect(() => {
31
+ if (!isSamePageSyncEnabled) {
32
+ return;
33
+ }
34
+ let isMounted = true;
35
+ void (sourceManager === null || sourceManager === void 0 ? void 0 : sourceManager.getSyncBlockSourceInfo(localId).then(nextSourceInfo => {
36
+ if (isMounted) {
37
+ setSourceInfo(nextSourceInfo);
38
+ }
39
+ }));
40
+ const unsubscribe = sourceManager === null || sourceManager === void 0 ? void 0 : sourceManager.subscribeToLocalSource(resourceId, snapshot => setIsUnpublished((snapshot === null || snapshot === void 0 ? void 0 : snapshot.status) === 'unpublished'));
41
+ return () => {
42
+ isMounted = false;
43
+ unsubscribe === null || unsubscribe === void 0 ? void 0 : unsubscribe();
44
+ };
45
+ }, [isSamePageSyncEnabled, localId, resourceId, sourceManager]);
46
+ return /*#__PURE__*/React.createElement(SyncBlockLabel, {
47
+ isSource: true,
48
+ localId: localId,
49
+ unpublishedInfo: isSamePageSyncEnabled && isUnpublished ? {
50
+ sourceType: getUnpublishedSourceType({
51
+ sourceAri: sourceInfo === null || sourceInfo === void 0 ? void 0 : sourceInfo.sourceAri,
52
+ sourceProduct: sourceInfo === null || sourceInfo === void 0 ? void 0 : sourceInfo.productType
53
+ }),
54
+ variant: 'source'
55
+ } : undefined
56
+ });
57
+ };
17
58
  export class BodiedSyncBlock {
18
- constructor(node, view, getPos, api, nodeViewPortalProviderAPI) {
59
+ constructor(node, view, getPos, api, nodeViewPortalProviderAPI, sourceManager) {
19
60
  this.node = node;
20
61
  this.view = view;
21
62
  this.getPos = getPos;
@@ -45,9 +86,10 @@ export class BodiedSyncBlock {
45
86
  componentId: ACTION_SUBJECT_ID.SYNCED_BLOCK_LABEL,
46
87
  dispatchAnalyticsEvent: (_this$api = this.api) === null || _this$api === void 0 ? void 0 : (_this$api$analytics = _this$api.analytics) === null || _this$api$analytics === void 0 ? void 0 : _this$api$analytics.actions.fireAnalyticsEvent,
47
88
  fallbackComponent: null
48
- }, /*#__PURE__*/React.createElement(SyncBlockLabel, {
49
- isSource: true,
50
- localId: node.attrs.localId
89
+ }, /*#__PURE__*/React.createElement(SourceSyncBlockLabel, {
90
+ localId: node.attrs.localId,
91
+ resourceId: node.attrs.resourceId,
92
+ sourceManager: sourceManager
51
93
  }));
52
94
  }, labelContainer, this.labelKey);
53
95
  this.updateContentEditable({});
@@ -131,9 +173,10 @@ export const bodiedSyncBlockNodeView = props => {
131
173
  api,
132
174
  pmPluginFactoryParams: {
133
175
  nodeViewPortalProviderAPI
134
- }
176
+ },
177
+ syncBlockStore
135
178
  } = props;
136
179
  return (node, view, getPos) => {
137
- return new BodiedSyncBlock(node, view, getPos, api, nodeViewPortalProviderAPI);
180
+ return new BodiedSyncBlock(node, view, getPos, api, nodeViewPortalProviderAPI, syncBlockStore === null || syncBlockStore === void 0 ? void 0 : syncBlockStore.sourceManager);
138
181
  };
139
182
  };
@@ -1,7 +1,9 @@
1
1
  import React, { useCallback, useMemo, useState } from 'react';
2
2
  import { useIntl } from 'react-intl';
3
+ import IconButton from '@atlaskit/button/icon/button';
3
4
  import { syncBlockMessages as messages } from '@atlaskit/editor-common/messages';
4
5
  import { SyncBlockLabelSharedCssClassName } from '@atlaskit/editor-common/sync-block';
6
+ import InformationIcon from '@atlaskit/icon/core/status-information';
5
7
  import BlockSyncedIcon from '@atlaskit/icon-lab/core/block-synced';
6
8
  import { Text } from '@atlaskit/primitives/compiled';
7
9
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
@@ -14,7 +16,8 @@ const SyncBlockLabelComponent = ({
14
16
  isSource,
15
17
  localId,
16
18
  title,
17
- isUnsyncedBlock
19
+ isUnsyncedBlock,
20
+ unpublishedInfo
18
21
  }) => {
19
22
  const intl = useIntl();
20
23
  const {
@@ -78,21 +81,30 @@ const SyncBlockLabelComponent = ({
78
81
  color: "color.text.subtle"
79
82
  }, title);
80
83
  }, [formatMessage, isSource, isUnsyncedBlock, title]);
81
- const label = /*#__PURE__*/React.createElement("div", {
84
+ const unpublishedTooltipLabel = unpublishedInfo ? formatMessage(unpublishedInfo.variant === 'source' ? messages.unpublishedSourceAvailabilityTooltip : messages.unpublishedLocalReferenceAvailabilityTooltip, {
85
+ sourceType: unpublishedInfo.sourceType
86
+ }) : undefined;
87
+ const primaryLabel = /*#__PURE__*/React.createElement("div", {
82
88
  "data-testid": SyncBlockLabelDataId
83
89
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop
84
90
  ,
85
91
  className: SyncBlockLabelSharedCssClassName.labelClassName,
86
92
  "aria-describedby": isSource || isUnsyncedBlock ? undefined : ariaDescribedById
87
- }, /*#__PURE__*/React.createElement(BlockSyncedIcon, {
93
+ }, unpublishedTooltipLabel ? /*#__PURE__*/React.createElement(Tooltip, {
94
+ position: "top",
95
+ content: unpublishedTooltipLabel
96
+ }, /*#__PURE__*/React.createElement(IconButton, {
97
+ appearance: "subtle",
98
+ icon: InformationIcon,
99
+ label: unpublishedTooltipLabel,
100
+ spacing: "compact",
101
+ testId: "sync-block-unpublished-information"
102
+ })) : null, /*#__PURE__*/React.createElement(BlockSyncedIcon, {
88
103
  color: "var(--ds-icon-subtle, #505258)",
89
104
  size: "small",
90
105
  label: ""
91
106
  }), getLabelContent);
92
- if (isSource || isUnsyncedBlock) {
93
- return label;
94
- }
95
- return /*#__PURE__*/React.createElement(Tooltip, {
107
+ const labelWithExistingTooltip = isSource || isUnsyncedBlock ? primaryLabel : /*#__PURE__*/React.createElement(Tooltip, {
96
108
  position: "top",
97
109
  content: tooltipContent
98
110
  // workaround because tooltip adds aria-describedby with a new id every time the tooltip is opened
@@ -103,8 +115,9 @@ const SyncBlockLabelComponent = ({
103
115
  // using this to ensure that the 'last edited' time is updated when the tooltip is opened
104
116
  ,
105
117
  onShow: updateTooltipContent
106
- }, label, /*#__PURE__*/React.createElement(VisuallyHidden, {
118
+ }, primaryLabel, /*#__PURE__*/React.createElement(VisuallyHidden, {
107
119
  id: ariaDescribedById
108
120
  }, expValEquals('platform_editor_sync_block_activation', 'isEnabled', true) ? tooltipMessage : tooltipContent));
121
+ return labelWithExistingTooltip;
109
122
  };
110
123
  export const SyncBlockLabel = /*#__PURE__*/React.memo(SyncBlockLabelComponent);
@@ -1,8 +1,10 @@
1
- import React, { useCallback, useEffect, useRef } from 'react';
1
+ import React, { useCallback, useEffect, useRef, useState } from 'react';
2
2
  import { bind } from 'bind-event-listener';
3
3
  import { SyncBlockSharedCssClassName } from '@atlaskit/editor-common/sync-block';
4
+ import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
4
5
  import { SyncBlockError, useFetchSyncBlockData, useFetchSyncBlockTitle } from '@atlaskit/editor-synced-block-provider';
5
6
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
7
+ import { getUnpublishedSourceType } from './getUnpublishedSourceType';
6
8
  import { SyncBlockLabel } from './SyncBlockLabel';
7
9
  const SyncBlockRendererWrapperDataId = 'sync-block-plugin-renderer-wrapper';
8
10
  const SyncBlockRendererWrapperComponent = ({
@@ -13,12 +15,32 @@ const SyncBlockRendererWrapperComponent = ({
13
15
  localId,
14
16
  api
15
17
  }) => {
16
- var _api$analytics, _api$analytics$action, _syncBlockFetchResult, _syncBlockFetchResult2, _syncBlockFetchResult3, _syncBlockFetchResult4, _syncBlockFetchResult5, _syncBlockFetchResult6;
18
+ var _api$analytics, _api$analytics$action, _syncBlockFetchResult, _syncBlockFetchResult2, _syncBlockFetchResult3, _syncBlockFetchResult4, _syncBlockFetchResult5, _syncBlockFetchResult6, _syncBlockFetchResult7, _localSourceInfo$sour, _syncBlockFetchResult8, _syncBlockFetchResult9, _localSourceInfo$prod;
19
+ const isSamePageSyncEnabled = isExperimentEnabled('editor-synced-block-same-page-sync');
17
20
  const syncBlockFetchResult = useFetchSyncBlockData(syncBlockStore, resourceId, localId, api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : (_api$analytics$action = _api$analytics.actions) === null || _api$analytics$action === void 0 ? void 0 : _api$analytics$action.fireAnalyticsEvent);
18
21
  const title = useFetchSyncBlockTitle(syncBlockStore, node);
19
22
  const contentUpdatedAt = syncBlockFetchResult === null || syncBlockFetchResult === void 0 ? void 0 : (_syncBlockFetchResult = syncBlockFetchResult.syncBlockInstance) === null || _syncBlockFetchResult === void 0 ? void 0 : (_syncBlockFetchResult2 = _syncBlockFetchResult.data) === null || _syncBlockFetchResult2 === void 0 ? void 0 : _syncBlockFetchResult2.contentUpdatedAt;
20
23
  const isUnpublishedBlock = ((_syncBlockFetchResult3 = syncBlockFetchResult.syncBlockInstance) === null || _syncBlockFetchResult3 === void 0 ? void 0 : (_syncBlockFetchResult4 = _syncBlockFetchResult3.data) === null || _syncBlockFetchResult4 === void 0 ? void 0 : _syncBlockFetchResult4.status) === 'unpublished';
21
- const isUnsyncedBlock = isUnpublishedBlock || (syncBlockFetchResult === null || syncBlockFetchResult === void 0 ? void 0 : (_syncBlockFetchResult5 = syncBlockFetchResult.syncBlockInstance) === null || _syncBlockFetchResult5 === void 0 ? void 0 : (_syncBlockFetchResult6 = _syncBlockFetchResult5.error) === null || _syncBlockFetchResult6 === void 0 ? void 0 : _syncBlockFetchResult6.type) === SyncBlockError.NotFound;
24
+ const localSameDocumentSource = isSamePageSyncEnabled ? (_syncBlockFetchResult5 = syncBlockFetchResult.syncBlockInstance) === null || _syncBlockFetchResult5 === void 0 ? void 0 : _syncBlockFetchResult5.localSameDocumentSource : undefined;
25
+ const localSourceBlockInstanceId = localSameDocumentSource === null || localSameDocumentSource === void 0 ? void 0 : localSameDocumentSource.sourceBlockInstanceId;
26
+ const [localSourceInfo, setLocalSourceInfo] = useState();
27
+ useEffect(() => {
28
+ var _syncBlockStore$sourc;
29
+ let isMounted = true;
30
+ if (!localSourceBlockInstanceId) {
31
+ setLocalSourceInfo(undefined);
32
+ return;
33
+ }
34
+ void ((_syncBlockStore$sourc = syncBlockStore.sourceManager) === null || _syncBlockStore$sourc === void 0 ? void 0 : _syncBlockStore$sourc.getSyncBlockSourceInfo(localSourceBlockInstanceId).then(sourceInfo => {
35
+ if (isMounted) {
36
+ setLocalSourceInfo(sourceInfo);
37
+ }
38
+ }));
39
+ return () => {
40
+ isMounted = false;
41
+ };
42
+ }, [localSourceBlockInstanceId, syncBlockStore.sourceManager]);
43
+ const isUnsyncedBlock = isUnpublishedBlock && !localSameDocumentSource || (syncBlockFetchResult === null || syncBlockFetchResult === void 0 ? void 0 : (_syncBlockFetchResult6 = syncBlockFetchResult.syncBlockInstance) === null || _syncBlockFetchResult6 === void 0 ? void 0 : (_syncBlockFetchResult7 = _syncBlockFetchResult6.error) === null || _syncBlockFetchResult7 === void 0 ? void 0 : _syncBlockFetchResult7.type) === SyncBlockError.NotFound;
22
44
 
23
45
  // Evaluated unconditionally so the experiment exposure is tracked correctly
24
46
  // (recorded on every render, not lazily on first click).
@@ -88,7 +110,14 @@ const SyncBlockRendererWrapperComponent = ({
88
110
  title: title,
89
111
  contentUpdatedAt: contentUpdatedAt,
90
112
  localId: localId,
91
- isUnsyncedBlock: isUnsyncedBlock
113
+ isUnsyncedBlock: isUnsyncedBlock,
114
+ unpublishedInfo: localSameDocumentSource && isUnpublishedBlock ? {
115
+ sourceType: getUnpublishedSourceType({
116
+ sourceAri: (_localSourceInfo$sour = localSourceInfo === null || localSourceInfo === void 0 ? void 0 : localSourceInfo.sourceAri) !== null && _localSourceInfo$sour !== void 0 ? _localSourceInfo$sour : (_syncBlockFetchResult8 = syncBlockFetchResult.syncBlockInstance) === null || _syncBlockFetchResult8 === void 0 ? void 0 : (_syncBlockFetchResult9 = _syncBlockFetchResult8.data) === null || _syncBlockFetchResult9 === void 0 ? void 0 : _syncBlockFetchResult9.sourceAri,
117
+ sourceProduct: (_localSourceInfo$prod = localSourceInfo === null || localSourceInfo === void 0 ? void 0 : localSourceInfo.productType) !== null && _localSourceInfo$prod !== void 0 ? _localSourceInfo$prod : localSameDocumentSource.sourceProduct
118
+ }),
119
+ variant: 'local-reference'
120
+ } : undefined
92
121
  }));
93
122
  };
94
123
  export const SyncBlockRendererWrapper = /*#__PURE__*/React.memo(SyncBlockRendererWrapperComponent);
@@ -0,0 +1,12 @@
1
+ export const getUnpublishedSourceType = ({
2
+ sourceAri,
3
+ sourceProduct
4
+ }) => {
5
+ if (sourceProduct === 'jira-work-item' || sourceAri !== null && sourceAri !== void 0 && sourceAri.startsWith('ari:cloud:jira:')) {
6
+ return 'jiraWorkItem';
7
+ }
8
+ if (sourceAri !== null && sourceAri !== void 0 && sourceAri.includes(':blogpost/')) {
9
+ return 'blog';
10
+ }
11
+ return 'page';
12
+ };
@@ -1,11 +1,14 @@
1
1
  import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
2
2
  import _createClass from "@babel/runtime/helpers/createClass";
3
- import React from 'react';
3
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
4
+ import React, { useEffect, useState } from 'react';
4
5
  import { ACTION_SUBJECT, ACTION_SUBJECT_ID } from '@atlaskit/editor-common/analytics';
5
6
  import { ErrorBoundary } from '@atlaskit/editor-common/error-boundary';
6
7
  import { BodiedSyncBlockSharedCssClassName } from '@atlaskit/editor-common/sync-block';
7
8
  import { isOfflineMode } from '@atlaskit/editor-plugin-connectivity';
8
9
  import { DOMSerializer } from '@atlaskit/editor-prosemirror/model';
10
+ import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
11
+ import { getUnpublishedSourceType } from '../ui/getUnpublishedSourceType';
9
12
  import { SyncBlockLabel } from '../ui/SyncBlockLabel';
10
13
  import { isEmptySourceSyncBlock } from './isEmptySourceSyncBlock';
11
14
  var toDOM = function toDOM(node) {
@@ -18,8 +21,54 @@ var toDOM = function toDOM(node) {
18
21
  contenteditable: 'true'
19
22
  }, 0]];
20
23
  };
24
+ export var SourceSyncBlockLabel = function SourceSyncBlockLabel(_ref) {
25
+ var localId = _ref.localId,
26
+ resourceId = _ref.resourceId,
27
+ sourceManager = _ref.sourceManager;
28
+ var isSamePageSyncEnabled = isExperimentEnabled('editor-synced-block-same-page-sync');
29
+ var _useState = useState(function () {
30
+ var _sourceManager$getLoc;
31
+ return isSamePageSyncEnabled && (sourceManager === null || sourceManager === void 0 || (_sourceManager$getLoc = sourceManager.getLocalSourceSnapshot(resourceId)) === null || _sourceManager$getLoc === void 0 ? void 0 : _sourceManager$getLoc.status) === 'unpublished';
32
+ }),
33
+ _useState2 = _slicedToArray(_useState, 2),
34
+ isUnpublished = _useState2[0],
35
+ setIsUnpublished = _useState2[1];
36
+ var _useState3 = useState(),
37
+ _useState4 = _slicedToArray(_useState3, 2),
38
+ sourceInfo = _useState4[0],
39
+ setSourceInfo = _useState4[1];
40
+ useEffect(function () {
41
+ if (!isSamePageSyncEnabled) {
42
+ return;
43
+ }
44
+ var isMounted = true;
45
+ void (sourceManager === null || sourceManager === void 0 ? void 0 : sourceManager.getSyncBlockSourceInfo(localId).then(function (nextSourceInfo) {
46
+ if (isMounted) {
47
+ setSourceInfo(nextSourceInfo);
48
+ }
49
+ }));
50
+ var unsubscribe = sourceManager === null || sourceManager === void 0 ? void 0 : sourceManager.subscribeToLocalSource(resourceId, function (snapshot) {
51
+ return setIsUnpublished((snapshot === null || snapshot === void 0 ? void 0 : snapshot.status) === 'unpublished');
52
+ });
53
+ return function () {
54
+ isMounted = false;
55
+ unsubscribe === null || unsubscribe === void 0 || unsubscribe();
56
+ };
57
+ }, [isSamePageSyncEnabled, localId, resourceId, sourceManager]);
58
+ return /*#__PURE__*/React.createElement(SyncBlockLabel, {
59
+ isSource: true,
60
+ localId: localId,
61
+ unpublishedInfo: isSamePageSyncEnabled && isUnpublished ? {
62
+ sourceType: getUnpublishedSourceType({
63
+ sourceAri: sourceInfo === null || sourceInfo === void 0 ? void 0 : sourceInfo.sourceAri,
64
+ sourceProduct: sourceInfo === null || sourceInfo === void 0 ? void 0 : sourceInfo.productType
65
+ }),
66
+ variant: 'source'
67
+ } : undefined
68
+ });
69
+ };
21
70
  export var BodiedSyncBlock = /*#__PURE__*/function () {
22
- function BodiedSyncBlock(node, view, getPos, api, nodeViewPortalProviderAPI) {
71
+ function BodiedSyncBlock(node, view, getPos, api, nodeViewPortalProviderAPI, sourceManager) {
23
72
  var _this = this;
24
73
  _classCallCheck(this, BodiedSyncBlock);
25
74
  this.node = node;
@@ -50,9 +99,10 @@ export var BodiedSyncBlock = /*#__PURE__*/function () {
50
99
  componentId: ACTION_SUBJECT_ID.SYNCED_BLOCK_LABEL,
51
100
  dispatchAnalyticsEvent: (_this$api = _this.api) === null || _this$api === void 0 || (_this$api = _this$api.analytics) === null || _this$api === void 0 ? void 0 : _this$api.actions.fireAnalyticsEvent,
52
101
  fallbackComponent: null
53
- }, /*#__PURE__*/React.createElement(SyncBlockLabel, {
54
- isSource: true,
55
- localId: node.attrs.localId
102
+ }, /*#__PURE__*/React.createElement(SourceSyncBlockLabel, {
103
+ localId: node.attrs.localId,
104
+ resourceId: node.attrs.resourceId,
105
+ sourceManager: sourceManager
56
106
  }));
57
107
  }, labelContainer, this.labelKey);
58
108
  this.updateContentEditable({});
@@ -65,10 +115,10 @@ export var BodiedSyncBlock = /*#__PURE__*/function () {
65
115
  }
66
116
  return _createClass(BodiedSyncBlock, [{
67
117
  key: "updateContentEditable",
68
- value: function updateContentEditable(_ref) {
118
+ value: function updateContentEditable(_ref2) {
69
119
  var _this$api2, _this$api3;
70
- var nextConnectivityMode = _ref.nextConnectivityMode,
71
- nextViewMode = _ref.nextViewMode;
120
+ var nextConnectivityMode = _ref2.nextConnectivityMode,
121
+ nextViewMode = _ref2.nextViewMode;
72
122
  var connectivityMode = nextConnectivityMode !== null && nextConnectivityMode !== void 0 ? nextConnectivityMode : (_this$api2 = this.api) === null || _this$api2 === void 0 || (_this$api2 = _this$api2.connectivity) === null || _this$api2 === void 0 || (_this$api2 = _this$api2.sharedState) === null || _this$api2 === void 0 || (_this$api2 = _this$api2.currentState()) === null || _this$api2 === void 0 ? void 0 : _this$api2.mode;
73
123
  var viewMode = nextViewMode !== null && nextViewMode !== void 0 ? nextViewMode : (_this$api3 = this.api) === null || _this$api3 === void 0 || (_this$api3 = _this$api3.editorViewMode) === null || _this$api3 === void 0 || (_this$api3 = _this$api3.sharedState) === null || _this$api3 === void 0 || (_this$api3 = _this$api3.currentState()) === null || _this$api3 === void 0 ? void 0 : _this$api3.mode;
74
124
  var isOnline = !isOfflineMode(connectivityMode);
@@ -82,8 +132,8 @@ export var BodiedSyncBlock = /*#__PURE__*/function () {
82
132
  var _this$api4,
83
133
  _this2 = this;
84
134
  if ((_this$api4 = this.api) !== null && _this$api4 !== void 0 && _this$api4.connectivity) {
85
- this.cleanupConnectivityModeListener = this.api.connectivity.sharedState.onChange(function (_ref2) {
86
- var nextSharedState = _ref2.nextSharedState;
135
+ this.cleanupConnectivityModeListener = this.api.connectivity.sharedState.onChange(function (_ref3) {
136
+ var nextSharedState = _ref3.nextSharedState;
87
137
  _this2.updateContentEditable({
88
138
  nextConnectivityMode: nextSharedState.mode
89
139
  });
@@ -96,8 +146,8 @@ export var BodiedSyncBlock = /*#__PURE__*/function () {
96
146
  var _this$api5,
97
147
  _this3 = this;
98
148
  if ((_this$api5 = this.api) !== null && _this$api5 !== void 0 && _this$api5.editorViewMode) {
99
- this.cleanupViewModeListener = this.api.editorViewMode.sharedState.onChange(function (_ref3) {
100
- var nextSharedState = _ref3.nextSharedState;
149
+ this.cleanupViewModeListener = this.api.editorViewMode.sharedState.onChange(function (_ref4) {
150
+ var nextSharedState = _ref4.nextSharedState;
101
151
  _this3.updateContentEditable({
102
152
  nextViewMode: nextSharedState === null || nextSharedState === void 0 ? void 0 : nextSharedState.mode
103
153
  });
@@ -147,8 +197,9 @@ export var BodiedSyncBlock = /*#__PURE__*/function () {
147
197
  }();
148
198
  export var bodiedSyncBlockNodeView = function bodiedSyncBlockNodeView(props) {
149
199
  var api = props.api,
150
- nodeViewPortalProviderAPI = props.pmPluginFactoryParams.nodeViewPortalProviderAPI;
200
+ nodeViewPortalProviderAPI = props.pmPluginFactoryParams.nodeViewPortalProviderAPI,
201
+ syncBlockStore = props.syncBlockStore;
151
202
  return function (node, view, getPos) {
152
- return new BodiedSyncBlock(node, view, getPos, api, nodeViewPortalProviderAPI);
203
+ return new BodiedSyncBlock(node, view, getPos, api, nodeViewPortalProviderAPI, syncBlockStore === null || syncBlockStore === void 0 ? void 0 : syncBlockStore.sourceManager);
153
204
  };
154
205
  };
@@ -1,8 +1,10 @@
1
1
  import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
2
  import React, { useCallback, useMemo, useState } from 'react';
3
3
  import { useIntl } from 'react-intl';
4
+ import IconButton from '@atlaskit/button/icon/button';
4
5
  import { syncBlockMessages as messages } from '@atlaskit/editor-common/messages';
5
6
  import { SyncBlockLabelSharedCssClassName } from '@atlaskit/editor-common/sync-block';
7
+ import InformationIcon from '@atlaskit/icon/core/status-information';
6
8
  import BlockSyncedIcon from '@atlaskit/icon-lab/core/block-synced';
7
9
  import { Text } from '@atlaskit/primitives/compiled';
8
10
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
@@ -15,7 +17,8 @@ var SyncBlockLabelComponent = function SyncBlockLabelComponent(_ref) {
15
17
  isSource = _ref.isSource,
16
18
  localId = _ref.localId,
17
19
  title = _ref.title,
18
- isUnsyncedBlock = _ref.isUnsyncedBlock;
20
+ isUnsyncedBlock = _ref.isUnsyncedBlock,
21
+ unpublishedInfo = _ref.unpublishedInfo;
19
22
  var intl = useIntl();
20
23
  var formatMessage = intl.formatMessage;
21
24
  var _useState = useState(formatMessage(messages.defaultSyncBlockTooltip)),
@@ -79,21 +82,30 @@ var SyncBlockLabelComponent = function SyncBlockLabelComponent(_ref) {
79
82
  color: "color.text.subtle"
80
83
  }, title);
81
84
  }, [formatMessage, isSource, isUnsyncedBlock, title]);
82
- var label = /*#__PURE__*/React.createElement("div", {
85
+ var unpublishedTooltipLabel = unpublishedInfo ? formatMessage(unpublishedInfo.variant === 'source' ? messages.unpublishedSourceAvailabilityTooltip : messages.unpublishedLocalReferenceAvailabilityTooltip, {
86
+ sourceType: unpublishedInfo.sourceType
87
+ }) : undefined;
88
+ var primaryLabel = /*#__PURE__*/React.createElement("div", {
83
89
  "data-testid": SyncBlockLabelDataId
84
90
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop
85
91
  ,
86
92
  className: SyncBlockLabelSharedCssClassName.labelClassName,
87
93
  "aria-describedby": isSource || isUnsyncedBlock ? undefined : ariaDescribedById
88
- }, /*#__PURE__*/React.createElement(BlockSyncedIcon, {
94
+ }, unpublishedTooltipLabel ? /*#__PURE__*/React.createElement(Tooltip, {
95
+ position: "top",
96
+ content: unpublishedTooltipLabel
97
+ }, /*#__PURE__*/React.createElement(IconButton, {
98
+ appearance: "subtle",
99
+ icon: InformationIcon,
100
+ label: unpublishedTooltipLabel,
101
+ spacing: "compact",
102
+ testId: "sync-block-unpublished-information"
103
+ })) : null, /*#__PURE__*/React.createElement(BlockSyncedIcon, {
89
104
  color: "var(--ds-icon-subtle, #505258)",
90
105
  size: "small",
91
106
  label: ""
92
107
  }), getLabelContent);
93
- if (isSource || isUnsyncedBlock) {
94
- return label;
95
- }
96
- return /*#__PURE__*/React.createElement(Tooltip, {
108
+ var labelWithExistingTooltip = isSource || isUnsyncedBlock ? primaryLabel : /*#__PURE__*/React.createElement(Tooltip, {
97
109
  position: "top",
98
110
  content: tooltipContent
99
111
  // workaround because tooltip adds aria-describedby with a new id every time the tooltip is opened
@@ -104,8 +116,9 @@ var SyncBlockLabelComponent = function SyncBlockLabelComponent(_ref) {
104
116
  // using this to ensure that the 'last edited' time is updated when the tooltip is opened
105
117
  ,
106
118
  onShow: updateTooltipContent
107
- }, label, /*#__PURE__*/React.createElement(VisuallyHidden, {
119
+ }, primaryLabel, /*#__PURE__*/React.createElement(VisuallyHidden, {
108
120
  id: ariaDescribedById
109
121
  }, expValEquals('platform_editor_sync_block_activation', 'isEnabled', true) ? tooltipMessage : tooltipContent));
122
+ return labelWithExistingTooltip;
110
123
  };
111
124
  export var SyncBlockLabel = /*#__PURE__*/React.memo(SyncBlockLabelComponent);
@@ -1,23 +1,49 @@
1
- import React, { useCallback, useEffect, useRef } from 'react';
1
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
+ import React, { useCallback, useEffect, useRef, useState } from 'react';
2
3
  import { bind } from 'bind-event-listener';
3
4
  import { SyncBlockSharedCssClassName } from '@atlaskit/editor-common/sync-block';
5
+ import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
4
6
  import { SyncBlockError, useFetchSyncBlockData, useFetchSyncBlockTitle } from '@atlaskit/editor-synced-block-provider';
5
7
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
8
+ import { getUnpublishedSourceType } from './getUnpublishedSourceType';
6
9
  import { SyncBlockLabel } from './SyncBlockLabel';
7
10
  var SyncBlockRendererWrapperDataId = 'sync-block-plugin-renderer-wrapper';
8
11
  var SyncBlockRendererWrapperComponent = function SyncBlockRendererWrapperComponent(_ref) {
9
- var _api$analytics, _syncBlockFetchResult, _syncBlockFetchResult2, _syncBlockFetchResult3;
12
+ var _api$analytics, _syncBlockFetchResult, _syncBlockFetchResult2, _syncBlockFetchResult3, _syncBlockFetchResult4, _localSourceInfo$sour, _syncBlockFetchResult5, _localSourceInfo$prod;
10
13
  var syncedBlockRenderer = _ref.syncedBlockRenderer,
11
14
  syncBlockStore = _ref.syncBlockStore,
12
15
  node = _ref.node,
13
16
  resourceId = _ref.resourceId,
14
17
  localId = _ref.localId,
15
18
  api = _ref.api;
19
+ var isSamePageSyncEnabled = isExperimentEnabled('editor-synced-block-same-page-sync');
16
20
  var syncBlockFetchResult = useFetchSyncBlockData(syncBlockStore, resourceId, localId, api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 || (_api$analytics = _api$analytics.actions) === null || _api$analytics === void 0 ? void 0 : _api$analytics.fireAnalyticsEvent);
17
21
  var title = useFetchSyncBlockTitle(syncBlockStore, node);
18
22
  var contentUpdatedAt = syncBlockFetchResult === null || syncBlockFetchResult === void 0 || (_syncBlockFetchResult = syncBlockFetchResult.syncBlockInstance) === null || _syncBlockFetchResult === void 0 || (_syncBlockFetchResult = _syncBlockFetchResult.data) === null || _syncBlockFetchResult === void 0 ? void 0 : _syncBlockFetchResult.contentUpdatedAt;
19
23
  var isUnpublishedBlock = ((_syncBlockFetchResult2 = syncBlockFetchResult.syncBlockInstance) === null || _syncBlockFetchResult2 === void 0 || (_syncBlockFetchResult2 = _syncBlockFetchResult2.data) === null || _syncBlockFetchResult2 === void 0 ? void 0 : _syncBlockFetchResult2.status) === 'unpublished';
20
- var isUnsyncedBlock = isUnpublishedBlock || (syncBlockFetchResult === null || syncBlockFetchResult === void 0 || (_syncBlockFetchResult3 = syncBlockFetchResult.syncBlockInstance) === null || _syncBlockFetchResult3 === void 0 || (_syncBlockFetchResult3 = _syncBlockFetchResult3.error) === null || _syncBlockFetchResult3 === void 0 ? void 0 : _syncBlockFetchResult3.type) === SyncBlockError.NotFound;
24
+ var localSameDocumentSource = isSamePageSyncEnabled ? (_syncBlockFetchResult3 = syncBlockFetchResult.syncBlockInstance) === null || _syncBlockFetchResult3 === void 0 ? void 0 : _syncBlockFetchResult3.localSameDocumentSource : undefined;
25
+ var localSourceBlockInstanceId = localSameDocumentSource === null || localSameDocumentSource === void 0 ? void 0 : localSameDocumentSource.sourceBlockInstanceId;
26
+ var _useState = useState(),
27
+ _useState2 = _slicedToArray(_useState, 2),
28
+ localSourceInfo = _useState2[0],
29
+ setLocalSourceInfo = _useState2[1];
30
+ useEffect(function () {
31
+ var _syncBlockStore$sourc;
32
+ var isMounted = true;
33
+ if (!localSourceBlockInstanceId) {
34
+ setLocalSourceInfo(undefined);
35
+ return;
36
+ }
37
+ void ((_syncBlockStore$sourc = syncBlockStore.sourceManager) === null || _syncBlockStore$sourc === void 0 ? void 0 : _syncBlockStore$sourc.getSyncBlockSourceInfo(localSourceBlockInstanceId).then(function (sourceInfo) {
38
+ if (isMounted) {
39
+ setLocalSourceInfo(sourceInfo);
40
+ }
41
+ }));
42
+ return function () {
43
+ isMounted = false;
44
+ };
45
+ }, [localSourceBlockInstanceId, syncBlockStore.sourceManager]);
46
+ var isUnsyncedBlock = isUnpublishedBlock && !localSameDocumentSource || (syncBlockFetchResult === null || syncBlockFetchResult === void 0 || (_syncBlockFetchResult4 = syncBlockFetchResult.syncBlockInstance) === null || _syncBlockFetchResult4 === void 0 || (_syncBlockFetchResult4 = _syncBlockFetchResult4.error) === null || _syncBlockFetchResult4 === void 0 ? void 0 : _syncBlockFetchResult4.type) === SyncBlockError.NotFound;
21
47
 
22
48
  // Evaluated unconditionally so the experiment exposure is tracked correctly
23
49
  // (recorded on every render, not lazily on first click).
@@ -87,7 +113,14 @@ var SyncBlockRendererWrapperComponent = function SyncBlockRendererWrapperCompone
87
113
  title: title,
88
114
  contentUpdatedAt: contentUpdatedAt,
89
115
  localId: localId,
90
- isUnsyncedBlock: isUnsyncedBlock
116
+ isUnsyncedBlock: isUnsyncedBlock,
117
+ unpublishedInfo: localSameDocumentSource && isUnpublishedBlock ? {
118
+ sourceType: getUnpublishedSourceType({
119
+ sourceAri: (_localSourceInfo$sour = localSourceInfo === null || localSourceInfo === void 0 ? void 0 : localSourceInfo.sourceAri) !== null && _localSourceInfo$sour !== void 0 ? _localSourceInfo$sour : (_syncBlockFetchResult5 = syncBlockFetchResult.syncBlockInstance) === null || _syncBlockFetchResult5 === void 0 || (_syncBlockFetchResult5 = _syncBlockFetchResult5.data) === null || _syncBlockFetchResult5 === void 0 ? void 0 : _syncBlockFetchResult5.sourceAri,
120
+ sourceProduct: (_localSourceInfo$prod = localSourceInfo === null || localSourceInfo === void 0 ? void 0 : localSourceInfo.productType) !== null && _localSourceInfo$prod !== void 0 ? _localSourceInfo$prod : localSameDocumentSource.sourceProduct
121
+ }),
122
+ variant: 'local-reference'
123
+ } : undefined
91
124
  }));
92
125
  };
93
126
  export var SyncBlockRendererWrapper = /*#__PURE__*/React.memo(SyncBlockRendererWrapperComponent);
@@ -0,0 +1,11 @@
1
+ export var getUnpublishedSourceType = function getUnpublishedSourceType(_ref) {
2
+ var sourceAri = _ref.sourceAri,
3
+ sourceProduct = _ref.sourceProduct;
4
+ if (sourceProduct === 'jira-work-item' || sourceAri !== null && sourceAri !== void 0 && sourceAri.startsWith('ari:cloud:jira:')) {
5
+ return 'jiraWorkItem';
6
+ }
7
+ if (sourceAri !== null && sourceAri !== void 0 && sourceAri.includes(':blogpost/')) {
8
+ return 'blog';
9
+ }
10
+ return 'page';
11
+ };
@@ -1,9 +1,11 @@
1
+ import React from 'react';
1
2
  import type { PortalProviderAPI } from '@atlaskit/editor-common/portal';
2
3
  import type { getPosHandler } from '@atlaskit/editor-common/react-node-view';
3
4
  import type { ExtractInjectionAPI, getPosHandlerNode, PMPluginFactoryParams } from '@atlaskit/editor-common/types';
4
5
  import { type Node as PMNode } from '@atlaskit/editor-prosemirror/model';
5
6
  import type { EditorView, NodeView } from '@atlaskit/editor-prosemirror/view';
6
7
  import type { SyncBlockStoreManager } from '@atlaskit/editor-synced-block-provider';
8
+ import type { SourceSyncBlockStoreManager } from '@atlaskit/editor-synced-block-provider/syncBlockStoreManager';
7
9
  import type { SyncedBlockPlugin, SyncedBlockPluginOptions } from '../syncedBlockPluginType';
8
10
  export interface BodiedSyncBlockNodeViewProperties {
9
11
  api?: ExtractInjectionAPI<SyncedBlockPlugin>;
@@ -11,6 +13,11 @@ export interface BodiedSyncBlockNodeViewProperties {
11
13
  pmPluginFactoryParams: PMPluginFactoryParams;
12
14
  syncBlockStore?: SyncBlockStoreManager;
13
15
  }
16
+ export declare const SourceSyncBlockLabel: ({ localId, resourceId, sourceManager, }: {
17
+ localId: string;
18
+ resourceId: string;
19
+ sourceManager?: SourceSyncBlockStoreManager;
20
+ }) => React.JSX.Element;
14
21
  export declare class BodiedSyncBlock implements NodeView {
15
22
  dom: HTMLElement;
16
23
  contentDOM: HTMLElement;
@@ -22,7 +29,7 @@ export declare class BodiedSyncBlock implements NodeView {
22
29
  private cleanupConnectivityModeListener?;
23
30
  private cleanupViewModeListener?;
24
31
  private labelKey;
25
- constructor(node: PMNode, view: EditorView, getPos: getPosHandlerNode, api: ExtractInjectionAPI<SyncedBlockPlugin> | undefined, nodeViewPortalProviderAPI: PortalProviderAPI);
32
+ constructor(node: PMNode, view: EditorView, getPos: getPosHandlerNode, api: ExtractInjectionAPI<SyncedBlockPlugin> | undefined, nodeViewPortalProviderAPI: PortalProviderAPI, sourceManager?: SourceSyncBlockStoreManager);
26
33
  private updateContentEditable;
27
34
  private handleConnectivityModeChange;
28
35
  private handleViewModeChange;
@@ -1,10 +1,16 @@
1
1
  import React from 'react';
2
+ import type { UnpublishedSourceType } from './getUnpublishedSourceType';
3
+ export type UnpublishedLabelInfo = Readonly<{
4
+ sourceType: UnpublishedSourceType;
5
+ variant: 'local-reference' | 'source';
6
+ }>;
2
7
  type SyncBlockLabelProps = {
3
8
  contentUpdatedAt?: string;
4
9
  isSource: boolean;
5
10
  isUnsyncedBlock?: boolean;
6
11
  localId: string;
7
12
  title?: string;
13
+ unpublishedInfo?: UnpublishedLabelInfo;
8
14
  };
9
- export declare const SyncBlockLabel: React.MemoExoticComponent<({ contentUpdatedAt, isSource, localId, title, isUnsyncedBlock, }: SyncBlockLabelProps) => React.JSX.Element>;
15
+ export declare const SyncBlockLabel: React.MemoExoticComponent<({ contentUpdatedAt, isSource, localId, title, isUnsyncedBlock, unpublishedInfo, }: SyncBlockLabelProps) => React.JSX.Element>;
10
16
  export {};
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
3
3
  import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
4
- import type { SyncBlockStoreManager } from '@atlaskit/editor-synced-block-provider';
4
+ import { type SyncBlockStoreManager } from '@atlaskit/editor-synced-block-provider';
5
5
  import type { SyncedBlockPlugin, SyncedBlockRendererProps } from '../syncedBlockPluginType';
6
6
  type Props = {
7
7
  api?: ExtractInjectionAPI<SyncedBlockPlugin>;
@@ -0,0 +1,6 @@
1
+ import type { SyncBlockProduct } from '@atlaskit/editor-synced-block-provider';
2
+ export type UnpublishedSourceType = 'blog' | 'jiraWorkItem' | 'page';
3
+ export declare const getUnpublishedSourceType: ({ sourceAri, sourceProduct, }: {
4
+ sourceAri?: string;
5
+ sourceProduct?: SyncBlockProduct;
6
+ }) => UnpublishedSourceType;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-synced-block",
3
- "version": "14.0.11",
3
+ "version": "14.0.12",
4
4
  "description": "SyncedBlock plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -40,7 +40,7 @@
40
40
  "@atlaskit/editor-plugin-user-intent": "^14.0.0",
41
41
  "@atlaskit/editor-prosemirror": "^8.0.0",
42
42
  "@atlaskit/editor-shared-styles": "^4.0.0",
43
- "@atlaskit/editor-synced-block-provider": "^12.0.0",
43
+ "@atlaskit/editor-synced-block-provider": "^12.1.0",
44
44
  "@atlaskit/editor-toolbar": "^2.6.0",
45
45
  "@atlaskit/flag": "^18.4.0",
46
46
  "@atlaskit/icon": "37.5.3",
@@ -52,7 +52,7 @@
52
52
  "@atlaskit/platform-feature-flags": "^2.1.0",
53
53
  "@atlaskit/primitives": "^22.4.0",
54
54
  "@atlaskit/spinner": "20.3.0",
55
- "@atlaskit/tmp-editor-statsig": "^167.0.0",
55
+ "@atlaskit/tmp-editor-statsig": "^168.0.0",
56
56
  "@atlaskit/tokens": "16.9.0",
57
57
  "@atlaskit/tooltip": "^24.2.0",
58
58
  "@atlaskit/visually-hidden": "^4.3.0",