@atlaskit/editor-plugin-synced-block 5.3.0 → 5.3.1

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,13 @@
1
1
  # @atlaskit/editor-plugin-synced-block
2
2
 
3
+ ## 5.3.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [`87abc5dda86fe`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/87abc5dda86fe) -
8
+ [ux] Show last edited time in sync block tooltip
9
+ - Updated dependencies
10
+
3
11
  ## 5.3.0
4
12
 
5
13
  ### Minor Changes
@@ -1,38 +1,132 @@
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.SyncBlockLabel = void 0;
8
- var _react = _interopRequireDefault(require("react"));
8
+ exports.formatElapsedTime = exports.SyncBlockLabel = void 0;
9
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
10
+ var _react = _interopRequireWildcard(require("react"));
11
+ var _isYesterday = _interopRequireDefault(require("date-fns/isYesterday"));
9
12
  var _reactIntlNext = require("react-intl-next");
10
13
  var _messages = require("@atlaskit/editor-common/messages");
11
14
  var _syncBlock = require("@atlaskit/editor-common/sync-block");
12
15
  var _blockSynced = _interopRequireDefault(require("@atlaskit/icon-lab/core/block-synced"));
16
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
13
17
  var _compiled = require("@atlaskit/primitives/compiled");
14
18
  var _tooltip = _interopRequireDefault(require("@atlaskit/tooltip"));
15
19
  var _visuallyHidden = _interopRequireDefault(require("@atlaskit/visually-hidden"));
20
+ 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); }
16
21
  var SyncBlockLabelDataId = 'sync-block-label';
22
+ var SECONDS_IN_MINUTE = 60;
23
+ var SECONDS_IN_HOUR = SECONDS_IN_MINUTE * 60;
24
+ var SECONDS_IN_DAY = SECONDS_IN_HOUR * 24;
25
+ var SECONDS_IN_WEEK = SECONDS_IN_DAY * 7;
26
+ var SECONDS_IN_MONTH = SECONDS_IN_DAY * 30;
27
+ var SECONDS_IN_YEAR = SECONDS_IN_DAY * 365;
28
+ var formatElapsedTime = exports.formatElapsedTime = function formatElapsedTime(isoDate, intl) {
29
+ var now = Date.now();
30
+ var date = new Date(isoDate).getTime();
31
+ var diffInSeconds = Math.floor((now - date) / 1000);
32
+ var dateObj = new Date(isoDate);
33
+
34
+ // Show "yesterday" when timestamp is from the previous calendar day
35
+ if ((0, _isYesterday.default)(dateObj) && diffInSeconds >= SECONDS_IN_DAY) {
36
+ return intl.formatRelativeTime(-1, 'day', {
37
+ numeric: 'auto',
38
+ style: 'long'
39
+ });
40
+ }
41
+ if (diffInSeconds < SECONDS_IN_MINUTE) {
42
+ return intl.formatRelativeTime(-Math.max(diffInSeconds, 1), 'second', {
43
+ style: 'long'
44
+ });
45
+ } else if (diffInSeconds < SECONDS_IN_HOUR) {
46
+ var minutes = Math.floor(diffInSeconds / SECONDS_IN_MINUTE);
47
+ return intl.formatRelativeTime(-minutes, 'minute', {
48
+ style: 'long'
49
+ });
50
+ } else if (diffInSeconds < SECONDS_IN_DAY) {
51
+ var hours = Math.floor(diffInSeconds / SECONDS_IN_HOUR);
52
+ return intl.formatRelativeTime(-hours, 'hour', {
53
+ style: 'long'
54
+ });
55
+ } else if (diffInSeconds < SECONDS_IN_WEEK) {
56
+ var days = Math.floor(diffInSeconds / SECONDS_IN_DAY);
57
+ return intl.formatRelativeTime(-days, 'day', {
58
+ style: 'long'
59
+ });
60
+ } else if (diffInSeconds < SECONDS_IN_MONTH) {
61
+ var weeks = Math.floor(diffInSeconds / SECONDS_IN_WEEK);
62
+ return intl.formatRelativeTime(-weeks, 'week', {
63
+ style: 'long'
64
+ });
65
+ } else if (diffInSeconds < SECONDS_IN_YEAR) {
66
+ var months = Math.floor(diffInSeconds / SECONDS_IN_MONTH);
67
+ return intl.formatRelativeTime(-months, 'month', {
68
+ style: 'long'
69
+ });
70
+ } else {
71
+ var years = Math.floor(diffInSeconds / SECONDS_IN_YEAR);
72
+ return intl.formatRelativeTime(-years, 'year', {
73
+ style: 'long'
74
+ });
75
+ }
76
+ };
17
77
  var SyncBlockLabelComponent = function SyncBlockLabelComponent(_ref) {
18
78
  var isSource = _ref.isSource,
19
79
  useFetchSyncBlockTitle = _ref.useFetchSyncBlockTitle,
20
- localId = _ref.localId;
21
- var _useIntl = (0, _reactIntlNext.useIntl)(),
22
- formatMessage = _useIntl.formatMessage;
80
+ localId = _ref.localId,
81
+ contentUpdatedAt = _ref.contentUpdatedAt;
82
+ var intl = (0, _reactIntlNext.useIntl)();
83
+ var formatMessage = intl.formatMessage;
23
84
  var title = useFetchSyncBlockTitle === null || useFetchSyncBlockTitle === void 0 ? void 0 : useFetchSyncBlockTitle();
24
- var tooltipContent = isSource ? formatMessage(_messages.syncBlockMessages.sourceSyncBlockTooltip) : title ? formatMessage(_messages.syncBlockMessages.referenceSyncBlockTooltip, {
25
- title: title
26
- }) : formatMessage(_messages.syncBlockMessages.defaultSyncBlockTooltip);
85
+ var _useState = (0, _react.useState)(formatMessage(_messages.syncBlockMessages.defaultSyncBlockTooltip)),
86
+ _useState2 = (0, _slicedToArray2.default)(_useState, 2),
87
+ tooltipContent = _useState2[0],
88
+ setTooltipContent = _useState2[1];
89
+ var tooltipMessage = formatMessage(_messages.syncBlockMessages.defaultSyncBlockTooltip);
90
+ if (isSource) {
91
+ tooltipMessage = formatMessage(_messages.syncBlockMessages.sourceSyncBlockTooltip);
92
+ } else if (title) {
93
+ tooltipMessage = formatMessage(_messages.syncBlockMessages.referenceSyncBlockTooltip, {
94
+ title: title
95
+ });
96
+ }
97
+ var updateTooltipContent = (0, _react.useCallback)(function () {
98
+ if (!(0, _platformFeatureFlags.fg)('platform_synced_block_dogfooding')) {
99
+ return;
100
+ }
101
+ var tooltipContent = tooltipMessage;
102
+ if (contentUpdatedAt) {
103
+ var elapsedTime = formatElapsedTime(contentUpdatedAt, intl);
104
+ tooltipContent = /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement(_compiled.Text, {
105
+ size: "small",
106
+ color: "color.text.inverse"
107
+ }, tooltipMessage), /*#__PURE__*/_react.default.createElement("br", null), /*#__PURE__*/_react.default.createElement("br", null), /*#__PURE__*/_react.default.createElement(_compiled.Text, {
108
+ size: "small",
109
+ color: "color.text.inverse",
110
+ weight: "bold"
111
+ }, formatMessage(_messages.syncBlockMessages.referenceSyncBlockLastEdited)), /*#__PURE__*/_react.default.createElement(_compiled.Text, {
112
+ size: "small",
113
+ color: "color.text.inverse"
114
+ }, elapsedTime));
115
+ }
116
+ setTooltipContent(tooltipContent);
117
+ }, [contentUpdatedAt, formatMessage, intl, tooltipMessage]);
27
118
  var ariaDescribedById = "sync-block-label-description-".concat(localId);
28
119
  return /*#__PURE__*/_react.default.createElement(_tooltip.default, {
29
120
  position: "top",
30
- content: tooltipContent
121
+ content: (0, _platformFeatureFlags.fg)('platform_synced_block_dogfooding') ? tooltipContent : tooltipMessage
31
122
  // workaround because tooltip adds aria-describedby with a new id every time the tooltip is opened
32
123
  // this causes an infinite rerender loop because of the forwardRef from the node view we are inside in bodiedSyncBlock
33
124
  // tooltip content is available for screen readers in visually hidden content after the label
34
125
  ,
35
126
  isScreenReaderAnnouncementDisabled: true
127
+ // using this to ensure that the 'last edited' time is updated when the tooltip is opened
128
+ ,
129
+ onShow: updateTooltipContent
36
130
  }, /*#__PURE__*/_react.default.createElement("div", {
37
131
  "data-testid": SyncBlockLabelDataId
38
132
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop
@@ -10,22 +10,26 @@ var _syncBlock = require("@atlaskit/editor-common/sync-block");
10
10
  var _SyncBlockLabel = require("./SyncBlockLabel");
11
11
  var SyncBlockRendererWrapperDataId = 'sync-block-plugin-renderer-wrapper';
12
12
  var SyncBlockRendererWrapperComponent = function SyncBlockRendererWrapperComponent(_ref) {
13
+ var _syncBlockFetchResult;
13
14
  var syncedBlockRenderer = _ref.syncedBlockRenderer,
14
15
  useFetchSyncBlockData = _ref.useFetchSyncBlockData,
15
- localId = _ref.localId,
16
16
  useFetchSyncBlockTitle = _ref.useFetchSyncBlockTitle,
17
+ localId = _ref.localId,
17
18
  api = _ref.api;
19
+ var syncBlockFetchResult = useFetchSyncBlockData();
20
+ 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;
18
21
  return /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("div", {
19
22
  "data-testid": SyncBlockRendererWrapperDataId
20
23
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop
21
24
  ,
22
25
  className: _syncBlock.SyncBlockSharedCssClassName.renderer
23
26
  }, syncedBlockRenderer({
24
- useFetchSyncBlockData: useFetchSyncBlockData,
27
+ syncBlockFetchResult: syncBlockFetchResult,
25
28
  api: api
26
29
  })), /*#__PURE__*/_react.default.createElement(_SyncBlockLabel.SyncBlockLabel, {
27
30
  isSource: false,
28
31
  useFetchSyncBlockTitle: useFetchSyncBlockTitle,
32
+ contentUpdatedAt: contentUpdatedAt,
29
33
  localId: localId
30
34
  }));
31
35
  };
@@ -1,33 +1,122 @@
1
- import React from 'react';
1
+ import React, { useCallback, useState } from 'react';
2
+ import isYesterday from 'date-fns/isYesterday';
2
3
  import { useIntl } from 'react-intl-next';
3
4
  import { syncBlockMessages as messages } from '@atlaskit/editor-common/messages';
4
5
  import { SyncBlockLabelSharedCssClassName } from '@atlaskit/editor-common/sync-block';
5
6
  import BlockSyncedIcon from '@atlaskit/icon-lab/core/block-synced';
7
+ import { fg } from '@atlaskit/platform-feature-flags';
6
8
  import { Text } from '@atlaskit/primitives/compiled';
7
9
  import Tooltip from '@atlaskit/tooltip';
8
10
  import VisuallyHidden from '@atlaskit/visually-hidden';
9
11
  const SyncBlockLabelDataId = 'sync-block-label';
12
+ const SECONDS_IN_MINUTE = 60;
13
+ const SECONDS_IN_HOUR = SECONDS_IN_MINUTE * 60;
14
+ const SECONDS_IN_DAY = SECONDS_IN_HOUR * 24;
15
+ const SECONDS_IN_WEEK = SECONDS_IN_DAY * 7;
16
+ const SECONDS_IN_MONTH = SECONDS_IN_DAY * 30;
17
+ const SECONDS_IN_YEAR = SECONDS_IN_DAY * 365;
18
+ export const formatElapsedTime = (isoDate, intl) => {
19
+ const now = Date.now();
20
+ const date = new Date(isoDate).getTime();
21
+ const diffInSeconds = Math.floor((now - date) / 1000);
22
+ const dateObj = new Date(isoDate);
23
+
24
+ // Show "yesterday" when timestamp is from the previous calendar day
25
+ if (isYesterday(dateObj) && diffInSeconds >= SECONDS_IN_DAY) {
26
+ return intl.formatRelativeTime(-1, 'day', {
27
+ numeric: 'auto',
28
+ style: 'long'
29
+ });
30
+ }
31
+ if (diffInSeconds < SECONDS_IN_MINUTE) {
32
+ return intl.formatRelativeTime(-Math.max(diffInSeconds, 1), 'second', {
33
+ style: 'long'
34
+ });
35
+ } else if (diffInSeconds < SECONDS_IN_HOUR) {
36
+ const minutes = Math.floor(diffInSeconds / SECONDS_IN_MINUTE);
37
+ return intl.formatRelativeTime(-minutes, 'minute', {
38
+ style: 'long'
39
+ });
40
+ } else if (diffInSeconds < SECONDS_IN_DAY) {
41
+ const hours = Math.floor(diffInSeconds / SECONDS_IN_HOUR);
42
+ return intl.formatRelativeTime(-hours, 'hour', {
43
+ style: 'long'
44
+ });
45
+ } else if (diffInSeconds < SECONDS_IN_WEEK) {
46
+ const days = Math.floor(diffInSeconds / SECONDS_IN_DAY);
47
+ return intl.formatRelativeTime(-days, 'day', {
48
+ style: 'long'
49
+ });
50
+ } else if (diffInSeconds < SECONDS_IN_MONTH) {
51
+ const weeks = Math.floor(diffInSeconds / SECONDS_IN_WEEK);
52
+ return intl.formatRelativeTime(-weeks, 'week', {
53
+ style: 'long'
54
+ });
55
+ } else if (diffInSeconds < SECONDS_IN_YEAR) {
56
+ const months = Math.floor(diffInSeconds / SECONDS_IN_MONTH);
57
+ return intl.formatRelativeTime(-months, 'month', {
58
+ style: 'long'
59
+ });
60
+ } else {
61
+ const years = Math.floor(diffInSeconds / SECONDS_IN_YEAR);
62
+ return intl.formatRelativeTime(-years, 'year', {
63
+ style: 'long'
64
+ });
65
+ }
66
+ };
10
67
  const SyncBlockLabelComponent = ({
11
68
  isSource,
12
69
  useFetchSyncBlockTitle,
13
- localId
70
+ localId,
71
+ contentUpdatedAt
14
72
  }) => {
73
+ const intl = useIntl();
15
74
  const {
16
75
  formatMessage
17
- } = useIntl();
76
+ } = intl;
18
77
  const title = useFetchSyncBlockTitle === null || useFetchSyncBlockTitle === void 0 ? void 0 : useFetchSyncBlockTitle();
19
- const tooltipContent = isSource ? formatMessage(messages.sourceSyncBlockTooltip) : title ? formatMessage(messages.referenceSyncBlockTooltip, {
20
- title
21
- }) : formatMessage(messages.defaultSyncBlockTooltip);
78
+ const [tooltipContent, setTooltipContent] = useState(formatMessage(messages.defaultSyncBlockTooltip));
79
+ let tooltipMessage = formatMessage(messages.defaultSyncBlockTooltip);
80
+ if (isSource) {
81
+ tooltipMessage = formatMessage(messages.sourceSyncBlockTooltip);
82
+ } else if (title) {
83
+ tooltipMessage = formatMessage(messages.referenceSyncBlockTooltip, {
84
+ title
85
+ });
86
+ }
87
+ const updateTooltipContent = useCallback(() => {
88
+ if (!fg('platform_synced_block_dogfooding')) {
89
+ return;
90
+ }
91
+ let tooltipContent = tooltipMessage;
92
+ if (contentUpdatedAt) {
93
+ const elapsedTime = formatElapsedTime(contentUpdatedAt, intl);
94
+ tooltipContent = /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(Text, {
95
+ size: "small",
96
+ color: "color.text.inverse"
97
+ }, tooltipMessage), /*#__PURE__*/React.createElement("br", null), /*#__PURE__*/React.createElement("br", null), /*#__PURE__*/React.createElement(Text, {
98
+ size: "small",
99
+ color: "color.text.inverse",
100
+ weight: "bold"
101
+ }, formatMessage(messages.referenceSyncBlockLastEdited)), /*#__PURE__*/React.createElement(Text, {
102
+ size: "small",
103
+ color: "color.text.inverse"
104
+ }, elapsedTime));
105
+ }
106
+ setTooltipContent(tooltipContent);
107
+ }, [contentUpdatedAt, formatMessage, intl, tooltipMessage]);
22
108
  const ariaDescribedById = `sync-block-label-description-${localId}`;
23
109
  return /*#__PURE__*/React.createElement(Tooltip, {
24
110
  position: "top",
25
- content: tooltipContent
111
+ content: fg('platform_synced_block_dogfooding') ? tooltipContent : tooltipMessage
26
112
  // workaround because tooltip adds aria-describedby with a new id every time the tooltip is opened
27
113
  // this causes an infinite rerender loop because of the forwardRef from the node view we are inside in bodiedSyncBlock
28
114
  // tooltip content is available for screen readers in visually hidden content after the label
29
115
  ,
30
116
  isScreenReaderAnnouncementDisabled: true
117
+ // using this to ensure that the 'last edited' time is updated when the tooltip is opened
118
+ ,
119
+ onShow: updateTooltipContent
31
120
  }, /*#__PURE__*/React.createElement("div", {
32
121
  "data-testid": SyncBlockLabelDataId
33
122
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop
@@ -5,21 +5,25 @@ const SyncBlockRendererWrapperDataId = 'sync-block-plugin-renderer-wrapper';
5
5
  const SyncBlockRendererWrapperComponent = ({
6
6
  syncedBlockRenderer,
7
7
  useFetchSyncBlockData,
8
- localId,
9
8
  useFetchSyncBlockTitle,
9
+ localId,
10
10
  api
11
11
  }) => {
12
+ var _syncBlockFetchResult, _syncBlockFetchResult2;
13
+ const syncBlockFetchResult = useFetchSyncBlockData();
14
+ 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;
12
15
  return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
13
16
  "data-testid": SyncBlockRendererWrapperDataId
14
17
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop
15
18
  ,
16
19
  className: SyncBlockSharedCssClassName.renderer
17
20
  }, syncedBlockRenderer({
18
- useFetchSyncBlockData,
21
+ syncBlockFetchResult,
19
22
  api
20
23
  })), /*#__PURE__*/React.createElement(SyncBlockLabel, {
21
24
  isSource: false,
22
25
  useFetchSyncBlockTitle: useFetchSyncBlockTitle,
26
+ contentUpdatedAt: contentUpdatedAt,
23
27
  localId: localId
24
28
  }));
25
29
  };
@@ -1,31 +1,123 @@
1
- import React from 'react';
1
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
+ import React, { useCallback, useState } from 'react';
3
+ import isYesterday from 'date-fns/isYesterday';
2
4
  import { useIntl } from 'react-intl-next';
3
5
  import { syncBlockMessages as messages } from '@atlaskit/editor-common/messages';
4
6
  import { SyncBlockLabelSharedCssClassName } from '@atlaskit/editor-common/sync-block';
5
7
  import BlockSyncedIcon from '@atlaskit/icon-lab/core/block-synced';
8
+ import { fg } from '@atlaskit/platform-feature-flags';
6
9
  import { Text } from '@atlaskit/primitives/compiled';
7
10
  import Tooltip from '@atlaskit/tooltip';
8
11
  import VisuallyHidden from '@atlaskit/visually-hidden';
9
12
  var SyncBlockLabelDataId = 'sync-block-label';
13
+ var SECONDS_IN_MINUTE = 60;
14
+ var SECONDS_IN_HOUR = SECONDS_IN_MINUTE * 60;
15
+ var SECONDS_IN_DAY = SECONDS_IN_HOUR * 24;
16
+ var SECONDS_IN_WEEK = SECONDS_IN_DAY * 7;
17
+ var SECONDS_IN_MONTH = SECONDS_IN_DAY * 30;
18
+ var SECONDS_IN_YEAR = SECONDS_IN_DAY * 365;
19
+ export var formatElapsedTime = function formatElapsedTime(isoDate, intl) {
20
+ var now = Date.now();
21
+ var date = new Date(isoDate).getTime();
22
+ var diffInSeconds = Math.floor((now - date) / 1000);
23
+ var dateObj = new Date(isoDate);
24
+
25
+ // Show "yesterday" when timestamp is from the previous calendar day
26
+ if (isYesterday(dateObj) && diffInSeconds >= SECONDS_IN_DAY) {
27
+ return intl.formatRelativeTime(-1, 'day', {
28
+ numeric: 'auto',
29
+ style: 'long'
30
+ });
31
+ }
32
+ if (diffInSeconds < SECONDS_IN_MINUTE) {
33
+ return intl.formatRelativeTime(-Math.max(diffInSeconds, 1), 'second', {
34
+ style: 'long'
35
+ });
36
+ } else if (diffInSeconds < SECONDS_IN_HOUR) {
37
+ var minutes = Math.floor(diffInSeconds / SECONDS_IN_MINUTE);
38
+ return intl.formatRelativeTime(-minutes, 'minute', {
39
+ style: 'long'
40
+ });
41
+ } else if (diffInSeconds < SECONDS_IN_DAY) {
42
+ var hours = Math.floor(diffInSeconds / SECONDS_IN_HOUR);
43
+ return intl.formatRelativeTime(-hours, 'hour', {
44
+ style: 'long'
45
+ });
46
+ } else if (diffInSeconds < SECONDS_IN_WEEK) {
47
+ var days = Math.floor(diffInSeconds / SECONDS_IN_DAY);
48
+ return intl.formatRelativeTime(-days, 'day', {
49
+ style: 'long'
50
+ });
51
+ } else if (diffInSeconds < SECONDS_IN_MONTH) {
52
+ var weeks = Math.floor(diffInSeconds / SECONDS_IN_WEEK);
53
+ return intl.formatRelativeTime(-weeks, 'week', {
54
+ style: 'long'
55
+ });
56
+ } else if (diffInSeconds < SECONDS_IN_YEAR) {
57
+ var months = Math.floor(diffInSeconds / SECONDS_IN_MONTH);
58
+ return intl.formatRelativeTime(-months, 'month', {
59
+ style: 'long'
60
+ });
61
+ } else {
62
+ var years = Math.floor(diffInSeconds / SECONDS_IN_YEAR);
63
+ return intl.formatRelativeTime(-years, 'year', {
64
+ style: 'long'
65
+ });
66
+ }
67
+ };
10
68
  var SyncBlockLabelComponent = function SyncBlockLabelComponent(_ref) {
11
69
  var isSource = _ref.isSource,
12
70
  useFetchSyncBlockTitle = _ref.useFetchSyncBlockTitle,
13
- localId = _ref.localId;
14
- var _useIntl = useIntl(),
15
- formatMessage = _useIntl.formatMessage;
71
+ localId = _ref.localId,
72
+ contentUpdatedAt = _ref.contentUpdatedAt;
73
+ var intl = useIntl();
74
+ var formatMessage = intl.formatMessage;
16
75
  var title = useFetchSyncBlockTitle === null || useFetchSyncBlockTitle === void 0 ? void 0 : useFetchSyncBlockTitle();
17
- var tooltipContent = isSource ? formatMessage(messages.sourceSyncBlockTooltip) : title ? formatMessage(messages.referenceSyncBlockTooltip, {
18
- title: title
19
- }) : formatMessage(messages.defaultSyncBlockTooltip);
76
+ var _useState = useState(formatMessage(messages.defaultSyncBlockTooltip)),
77
+ _useState2 = _slicedToArray(_useState, 2),
78
+ tooltipContent = _useState2[0],
79
+ setTooltipContent = _useState2[1];
80
+ var tooltipMessage = formatMessage(messages.defaultSyncBlockTooltip);
81
+ if (isSource) {
82
+ tooltipMessage = formatMessage(messages.sourceSyncBlockTooltip);
83
+ } else if (title) {
84
+ tooltipMessage = formatMessage(messages.referenceSyncBlockTooltip, {
85
+ title: title
86
+ });
87
+ }
88
+ var updateTooltipContent = useCallback(function () {
89
+ if (!fg('platform_synced_block_dogfooding')) {
90
+ return;
91
+ }
92
+ var tooltipContent = tooltipMessage;
93
+ if (contentUpdatedAt) {
94
+ var elapsedTime = formatElapsedTime(contentUpdatedAt, intl);
95
+ tooltipContent = /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(Text, {
96
+ size: "small",
97
+ color: "color.text.inverse"
98
+ }, tooltipMessage), /*#__PURE__*/React.createElement("br", null), /*#__PURE__*/React.createElement("br", null), /*#__PURE__*/React.createElement(Text, {
99
+ size: "small",
100
+ color: "color.text.inverse",
101
+ weight: "bold"
102
+ }, formatMessage(messages.referenceSyncBlockLastEdited)), /*#__PURE__*/React.createElement(Text, {
103
+ size: "small",
104
+ color: "color.text.inverse"
105
+ }, elapsedTime));
106
+ }
107
+ setTooltipContent(tooltipContent);
108
+ }, [contentUpdatedAt, formatMessage, intl, tooltipMessage]);
20
109
  var ariaDescribedById = "sync-block-label-description-".concat(localId);
21
110
  return /*#__PURE__*/React.createElement(Tooltip, {
22
111
  position: "top",
23
- content: tooltipContent
112
+ content: fg('platform_synced_block_dogfooding') ? tooltipContent : tooltipMessage
24
113
  // workaround because tooltip adds aria-describedby with a new id every time the tooltip is opened
25
114
  // this causes an infinite rerender loop because of the forwardRef from the node view we are inside in bodiedSyncBlock
26
115
  // tooltip content is available for screen readers in visually hidden content after the label
27
116
  ,
28
117
  isScreenReaderAnnouncementDisabled: true
118
+ // using this to ensure that the 'last edited' time is updated when the tooltip is opened
119
+ ,
120
+ onShow: updateTooltipContent
29
121
  }, /*#__PURE__*/React.createElement("div", {
30
122
  "data-testid": SyncBlockLabelDataId
31
123
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop
@@ -3,22 +3,26 @@ import { SyncBlockSharedCssClassName } from '@atlaskit/editor-common/sync-block'
3
3
  import { SyncBlockLabel } from './SyncBlockLabel';
4
4
  var SyncBlockRendererWrapperDataId = 'sync-block-plugin-renderer-wrapper';
5
5
  var SyncBlockRendererWrapperComponent = function SyncBlockRendererWrapperComponent(_ref) {
6
+ var _syncBlockFetchResult;
6
7
  var syncedBlockRenderer = _ref.syncedBlockRenderer,
7
8
  useFetchSyncBlockData = _ref.useFetchSyncBlockData,
8
- localId = _ref.localId,
9
9
  useFetchSyncBlockTitle = _ref.useFetchSyncBlockTitle,
10
+ localId = _ref.localId,
10
11
  api = _ref.api;
12
+ var syncBlockFetchResult = useFetchSyncBlockData();
13
+ 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;
11
14
  return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
12
15
  "data-testid": SyncBlockRendererWrapperDataId
13
16
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop
14
17
  ,
15
18
  className: SyncBlockSharedCssClassName.renderer
16
19
  }, syncedBlockRenderer({
17
- useFetchSyncBlockData: useFetchSyncBlockData,
20
+ syncBlockFetchResult: syncBlockFetchResult,
18
21
  api: api
19
22
  })), /*#__PURE__*/React.createElement(SyncBlockLabel, {
20
23
  isSource: false,
21
24
  useFetchSyncBlockTitle: useFetchSyncBlockTitle,
25
+ contentUpdatedAt: contentUpdatedAt,
22
26
  localId: localId
23
27
  }));
24
28
  };
@@ -37,7 +37,7 @@ export type SyncedBlockEditorProps = {
37
37
  };
38
38
  export type SyncedBlockRendererProps = {
39
39
  api?: ExtractInjectionAPI<SyncedBlockPlugin>;
40
- useFetchSyncBlockData: () => UseFetchSyncBlockDataResult;
40
+ syncBlockFetchResult: UseFetchSyncBlockDataResult;
41
41
  };
42
42
  export interface SyncedBlockPluginOptions extends LongPressSelectionPluginOptions {
43
43
  enableSourceCreation?: boolean;
@@ -1,8 +1,11 @@
1
1
  import React from 'react';
2
+ import { type IntlShape } from 'react-intl-next';
3
+ export declare const formatElapsedTime: (isoDate: string, intl: IntlShape) => string;
2
4
  type SyncBlockLabelProps = {
5
+ contentUpdatedAt?: string;
3
6
  isSource: boolean;
4
7
  localId: string;
5
8
  useFetchSyncBlockTitle?: () => string | undefined;
6
9
  };
7
- export declare const SyncBlockLabel: React.MemoExoticComponent<({ isSource, useFetchSyncBlockTitle, localId, }: SyncBlockLabelProps) => React.JSX.Element>;
10
+ export declare const SyncBlockLabel: React.MemoExoticComponent<({ isSource, useFetchSyncBlockTitle, localId, contentUpdatedAt, }: SyncBlockLabelProps) => React.JSX.Element>;
8
11
  export {};
@@ -9,5 +9,5 @@ type Props = {
9
9
  useFetchSyncBlockData: () => UseFetchSyncBlockDataResult;
10
10
  useFetchSyncBlockTitle: () => string | undefined;
11
11
  };
12
- export declare const SyncBlockRendererWrapper: React.MemoExoticComponent<({ syncedBlockRenderer, useFetchSyncBlockData, localId, useFetchSyncBlockTitle, api, }: Props) => React.JSX.Element>;
12
+ export declare const SyncBlockRendererWrapper: React.MemoExoticComponent<({ syncedBlockRenderer, useFetchSyncBlockData, useFetchSyncBlockTitle, localId, api, }: Props) => React.JSX.Element>;
13
13
  export {};
@@ -37,7 +37,7 @@ export type SyncedBlockEditorProps = {
37
37
  };
38
38
  export type SyncedBlockRendererProps = {
39
39
  api?: ExtractInjectionAPI<SyncedBlockPlugin>;
40
- useFetchSyncBlockData: () => UseFetchSyncBlockDataResult;
40
+ syncBlockFetchResult: UseFetchSyncBlockDataResult;
41
41
  };
42
42
  export interface SyncedBlockPluginOptions extends LongPressSelectionPluginOptions {
43
43
  enableSourceCreation?: boolean;
@@ -1,8 +1,11 @@
1
1
  import React from 'react';
2
+ import { type IntlShape } from 'react-intl-next';
3
+ export declare const formatElapsedTime: (isoDate: string, intl: IntlShape) => string;
2
4
  type SyncBlockLabelProps = {
5
+ contentUpdatedAt?: string;
3
6
  isSource: boolean;
4
7
  localId: string;
5
8
  useFetchSyncBlockTitle?: () => string | undefined;
6
9
  };
7
- export declare const SyncBlockLabel: React.MemoExoticComponent<({ isSource, useFetchSyncBlockTitle, localId, }: SyncBlockLabelProps) => React.JSX.Element>;
10
+ export declare const SyncBlockLabel: React.MemoExoticComponent<({ isSource, useFetchSyncBlockTitle, localId, contentUpdatedAt, }: SyncBlockLabelProps) => React.JSX.Element>;
8
11
  export {};
@@ -9,5 +9,5 @@ type Props = {
9
9
  useFetchSyncBlockData: () => UseFetchSyncBlockDataResult;
10
10
  useFetchSyncBlockTitle: () => string | undefined;
11
11
  };
12
- export declare const SyncBlockRendererWrapper: React.MemoExoticComponent<({ syncedBlockRenderer, useFetchSyncBlockData, localId, useFetchSyncBlockTitle, api, }: Props) => React.JSX.Element>;
12
+ export declare const SyncBlockRendererWrapper: React.MemoExoticComponent<({ syncedBlockRenderer, useFetchSyncBlockData, useFetchSyncBlockTitle, localId, api, }: Props) => React.JSX.Element>;
13
13
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-synced-block",
3
- "version": "5.3.0",
3
+ "version": "5.3.1",
4
4
  "description": "SyncedBlock plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -29,8 +29,8 @@
29
29
  "atlaskit:src": "src/index.ts",
30
30
  "dependencies": {
31
31
  "@atlaskit/adf-schema": "^51.5.0",
32
- "@atlaskit/button": "23.9.4",
33
- "@atlaskit/dropdown-menu": "16.4.2",
32
+ "@atlaskit/button": "23.9.5",
33
+ "@atlaskit/dropdown-menu": "16.4.3",
34
34
  "@atlaskit/editor-json-transformer": "^8.31.0",
35
35
  "@atlaskit/editor-plugin-analytics": "^7.0.0",
36
36
  "@atlaskit/editor-plugin-block-menu": "^6.0.0",
@@ -44,7 +44,7 @@
44
44
  "@atlaskit/editor-tables": "^2.9.0",
45
45
  "@atlaskit/editor-toolbar": "^0.19.0",
46
46
  "@atlaskit/flag": "^17.8.0",
47
- "@atlaskit/icon": "29.4.2",
47
+ "@atlaskit/icon": "30.0.0",
48
48
  "@atlaskit/icon-lab": "^5.14.0",
49
49
  "@atlaskit/logo": "^19.10.0",
50
50
  "@atlaskit/lozenge": "^13.3.0",
@@ -59,6 +59,7 @@
59
59
  "@babel/runtime": "^7.0.0",
60
60
  "@compiled/react": "^0.18.6",
61
61
  "bind-event-listener": "^3.0.0",
62
+ "date-fns": "^2.17.0",
62
63
  "react-intl-next": "npm:react-intl@^5.18.1"
63
64
  },
64
65
  "peerDependencies": {