@atlaskit/editor-plugin-synced-block 0.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 +12 -0
- package/LICENSE.md +11 -0
- package/README.md +1 -0
- package/SyncedBlock/package.json +15 -0
- package/afm-cc/tsconfig.json +44 -0
- package/build/tsconfig.json +22 -0
- package/dist/cjs/index.js +12 -0
- package/dist/cjs/pm-plugins/SyncClient.js +167 -0
- package/dist/cjs/pm-plugins/main.js +50 -0
- package/dist/cjs/pm-plugins/utils.js +19 -0
- package/dist/cjs/syncedBlockPlugin.js +24 -0
- package/dist/cjs/syncedBlockPluginType.js +5 -0
- package/dist/cjs/types/index.js +1 -0
- package/dist/cjs/ui/extensions/synced-block/components/GlobalStyles.js +25 -0
- package/dist/cjs/ui/extensions/synced-block/components/SyncedBlockLiveView.js +25 -0
- package/dist/cjs/ui/extensions/synced-block/components/SyncedBlockRenderer.js +25 -0
- package/dist/cjs/ui/extensions/synced-block/constants.js +32 -0
- package/dist/cjs/ui/extensions/synced-block/getSyncedBlockExtensionProvider.js +11 -0
- package/dist/cjs/ui/extensions/synced-block/hooks/useLiveSyncedBlockContent.js +29 -0
- package/dist/cjs/ui/extensions/synced-block/hooks/usePollContentProperty.js +121 -0
- package/dist/cjs/ui/extensions/synced-block/index.js +19 -0
- package/dist/cjs/ui/extensions/synced-block/manifest.js +254 -0
- package/dist/cjs/ui/extensions/synced-block/utils/ari.js +29 -0
- package/dist/cjs/ui/extensions/synced-block/utils/content-property.js +159 -0
- package/dist/cjs/ui/extensions/synced-block/utils/synced-block.js +65 -0
- package/dist/es2019/index.js +4 -0
- package/dist/es2019/pm-plugins/SyncClient.js +102 -0
- package/dist/es2019/pm-plugins/main.js +47 -0
- package/dist/es2019/pm-plugins/utils.js +13 -0
- package/dist/es2019/syncedBlockPlugin.js +17 -0
- package/dist/es2019/syncedBlockPluginType.js +1 -0
- package/dist/es2019/types/index.js +0 -0
- package/dist/es2019/ui/extensions/synced-block/components/GlobalStyles.js +18 -0
- package/dist/es2019/ui/extensions/synced-block/components/SyncedBlockLiveView.js +19 -0
- package/dist/es2019/ui/extensions/synced-block/components/SyncedBlockRenderer.js +19 -0
- package/dist/es2019/ui/extensions/synced-block/constants.js +26 -0
- package/dist/es2019/ui/extensions/synced-block/getSyncedBlockExtensionProvider.js +5 -0
- package/dist/es2019/ui/extensions/synced-block/hooks/useLiveSyncedBlockContent.js +24 -0
- package/dist/es2019/ui/extensions/synced-block/hooks/usePollContentProperty.js +107 -0
- package/dist/es2019/ui/extensions/synced-block/index.js +5 -0
- package/dist/es2019/ui/extensions/synced-block/manifest.js +147 -0
- package/dist/es2019/ui/extensions/synced-block/utils/ari.js +19 -0
- package/dist/es2019/ui/extensions/synced-block/utils/content-property.js +108 -0
- package/dist/es2019/ui/extensions/synced-block/utils/synced-block.js +57 -0
- package/dist/esm/index.js +4 -0
- package/dist/esm/pm-plugins/SyncClient.js +160 -0
- package/dist/esm/pm-plugins/main.js +44 -0
- package/dist/esm/pm-plugins/utils.js +13 -0
- package/dist/esm/syncedBlockPlugin.js +17 -0
- package/dist/esm/syncedBlockPluginType.js +1 -0
- package/dist/esm/types/index.js +0 -0
- package/dist/esm/ui/extensions/synced-block/components/GlobalStyles.js +18 -0
- package/dist/esm/ui/extensions/synced-block/components/SyncedBlockLiveView.js +18 -0
- package/dist/esm/ui/extensions/synced-block/components/SyncedBlockRenderer.js +18 -0
- package/dist/esm/ui/extensions/synced-block/constants.js +26 -0
- package/dist/esm/ui/extensions/synced-block/getSyncedBlockExtensionProvider.js +5 -0
- package/dist/esm/ui/extensions/synced-block/hooks/useLiveSyncedBlockContent.js +23 -0
- package/dist/esm/ui/extensions/synced-block/hooks/usePollContentProperty.js +114 -0
- package/dist/esm/ui/extensions/synced-block/index.js +5 -0
- package/dist/esm/ui/extensions/synced-block/manifest.js +247 -0
- package/dist/esm/ui/extensions/synced-block/utils/ari.js +23 -0
- package/dist/esm/ui/extensions/synced-block/utils/content-property.js +153 -0
- package/dist/esm/ui/extensions/synced-block/utils/synced-block.js +58 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/pm-plugins/SyncClient.d.ts +14 -0
- package/dist/types/pm-plugins/main.d.ts +6 -0
- package/dist/types/pm-plugins/utils.d.ts +5 -0
- package/dist/types/syncedBlockPlugin.d.ts +2 -0
- package/dist/types/syncedBlockPluginType.d.ts +2 -0
- package/dist/types/types/index.d.ts +3 -0
- package/dist/types/ui/extensions/synced-block/components/GlobalStyles.d.ts +6 -0
- package/dist/types/ui/extensions/synced-block/components/SyncedBlockLiveView.d.ts +7 -0
- package/dist/types/ui/extensions/synced-block/components/SyncedBlockRenderer.d.ts +7 -0
- package/dist/types/ui/extensions/synced-block/constants.d.ts +8 -0
- package/dist/types/ui/extensions/synced-block/getSyncedBlockExtensionProvider.d.ts +2 -0
- package/dist/types/ui/extensions/synced-block/hooks/useLiveSyncedBlockContent.d.ts +6 -0
- package/dist/types/ui/extensions/synced-block/hooks/usePollContentProperty.d.ts +7 -0
- package/dist/types/ui/extensions/synced-block/index.d.ts +2 -0
- package/dist/types/ui/extensions/synced-block/manifest.d.ts +2 -0
- package/dist/types/ui/extensions/synced-block/utils/ari.d.ts +4 -0
- package/dist/types/ui/extensions/synced-block/utils/content-property.d.ts +33 -0
- package/dist/types/ui/extensions/synced-block/utils/synced-block.d.ts +24 -0
- package/dist/types-ts4.5/index.d.ts +3 -0
- package/dist/types-ts4.5/pm-plugins/SyncClient.d.ts +14 -0
- package/dist/types-ts4.5/pm-plugins/main.d.ts +6 -0
- package/dist/types-ts4.5/pm-plugins/utils.d.ts +5 -0
- package/dist/types-ts4.5/syncedBlockPlugin.d.ts +2 -0
- package/dist/types-ts4.5/syncedBlockPluginType.d.ts +2 -0
- package/dist/types-ts4.5/types/index.d.ts +3 -0
- package/dist/types-ts4.5/ui/extensions/synced-block/components/GlobalStyles.d.ts +6 -0
- package/dist/types-ts4.5/ui/extensions/synced-block/components/SyncedBlockLiveView.d.ts +7 -0
- package/dist/types-ts4.5/ui/extensions/synced-block/components/SyncedBlockRenderer.d.ts +7 -0
- package/dist/types-ts4.5/ui/extensions/synced-block/constants.d.ts +8 -0
- package/dist/types-ts4.5/ui/extensions/synced-block/getSyncedBlockExtensionProvider.d.ts +2 -0
- package/dist/types-ts4.5/ui/extensions/synced-block/hooks/useLiveSyncedBlockContent.d.ts +6 -0
- package/dist/types-ts4.5/ui/extensions/synced-block/hooks/usePollContentProperty.d.ts +7 -0
- package/dist/types-ts4.5/ui/extensions/synced-block/index.d.ts +2 -0
- package/dist/types-ts4.5/ui/extensions/synced-block/manifest.d.ts +2 -0
- package/dist/types-ts4.5/ui/extensions/synced-block/utils/ari.d.ts +4 -0
- package/dist/types-ts4.5/ui/extensions/synced-block/utils/content-property.d.ts +33 -0
- package/dist/types-ts4.5/ui/extensions/synced-block/utils/synced-block.d.ts +24 -0
- package/docs/0-intro.tsx +43 -0
- package/package.json +88 -0
- package/tsconfig.json +8 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { getCloudId } from '../constants';
|
|
2
|
+
import { getConfluencePageAri } from './ari';
|
|
3
|
+
|
|
4
|
+
// Uncomment for proxy in Atlaskit, which will route to hello.atlassian.net
|
|
5
|
+
// const BASE_URL = 'https://localhost:9876';
|
|
6
|
+
const BASE_URL = `/gateway/api/ex/confluence/${getCloudId()}`;
|
|
7
|
+
const API_BASE_URL = `${BASE_URL}/wiki/api/v2`;
|
|
8
|
+
const COMMON_HEADERS = {
|
|
9
|
+
'Content-Type': 'application/json',
|
|
10
|
+
Accept: 'application/json'
|
|
11
|
+
};
|
|
12
|
+
const getContentPropertiesUrl = ({
|
|
13
|
+
pageId,
|
|
14
|
+
contentPropertyId
|
|
15
|
+
}) => {
|
|
16
|
+
const url = `${API_BASE_URL}/pages/${pageId}/properties`;
|
|
17
|
+
if (contentPropertyId) {
|
|
18
|
+
return `${url}/${contentPropertyId}`;
|
|
19
|
+
}
|
|
20
|
+
return url;
|
|
21
|
+
};
|
|
22
|
+
const getGraphQLPropertiesUrl = () => {
|
|
23
|
+
return `/cgraphql/api/graphql`;
|
|
24
|
+
};
|
|
25
|
+
export const createContentProperty = async ({
|
|
26
|
+
pageId,
|
|
27
|
+
key,
|
|
28
|
+
value
|
|
29
|
+
}) => {
|
|
30
|
+
const url = getContentPropertiesUrl({
|
|
31
|
+
pageId
|
|
32
|
+
});
|
|
33
|
+
const body = JSON.stringify({
|
|
34
|
+
key,
|
|
35
|
+
value
|
|
36
|
+
});
|
|
37
|
+
const response = await fetch(url, {
|
|
38
|
+
method: 'POST',
|
|
39
|
+
headers: COMMON_HEADERS,
|
|
40
|
+
body
|
|
41
|
+
});
|
|
42
|
+
if (!response.ok) {
|
|
43
|
+
throw new Error(`Failed to create content property: ${response.statusText}`);
|
|
44
|
+
}
|
|
45
|
+
const contentProperty = await response.json();
|
|
46
|
+
return contentProperty;
|
|
47
|
+
};
|
|
48
|
+
export const getContentProperty = async ({
|
|
49
|
+
pageId,
|
|
50
|
+
contentPropertyId,
|
|
51
|
+
signal
|
|
52
|
+
}) => {
|
|
53
|
+
const url = getContentPropertiesUrl({
|
|
54
|
+
pageId,
|
|
55
|
+
contentPropertyId
|
|
56
|
+
});
|
|
57
|
+
const response = await fetch(url, {
|
|
58
|
+
method: 'GET',
|
|
59
|
+
headers: COMMON_HEADERS,
|
|
60
|
+
signal
|
|
61
|
+
});
|
|
62
|
+
if (!response.ok) {
|
|
63
|
+
throw new Error(`Failed to get content property: ${response.statusText}`);
|
|
64
|
+
}
|
|
65
|
+
const contentProperty = await response.json();
|
|
66
|
+
return contentProperty;
|
|
67
|
+
};
|
|
68
|
+
const getQuery = (documentARI, key, value) => {
|
|
69
|
+
return `mutation {
|
|
70
|
+
confluence {
|
|
71
|
+
updateValuePageProperty(input: {
|
|
72
|
+
pageId: "${documentARI}",
|
|
73
|
+
key: "${key}",
|
|
74
|
+
value: "${value}",
|
|
75
|
+
useSameVersion: true
|
|
76
|
+
}) {
|
|
77
|
+
pageProperty {
|
|
78
|
+
key,
|
|
79
|
+
value
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}`;
|
|
84
|
+
};
|
|
85
|
+
export const updateContentProperty = async ({
|
|
86
|
+
pageId,
|
|
87
|
+
key,
|
|
88
|
+
value
|
|
89
|
+
}) => {
|
|
90
|
+
const url = getGraphQLPropertiesUrl();
|
|
91
|
+
const documentARI = getConfluencePageAri(pageId);
|
|
92
|
+
|
|
93
|
+
// eslint-disable-next-line require-unicode-regexp
|
|
94
|
+
const query = getQuery(documentARI, key, value.replace(/"/g, '\\"'));
|
|
95
|
+
const bodyData = {
|
|
96
|
+
query
|
|
97
|
+
};
|
|
98
|
+
const response = await fetch(url, {
|
|
99
|
+
method: 'POST',
|
|
100
|
+
headers: COMMON_HEADERS,
|
|
101
|
+
body: JSON.stringify(bodyData)
|
|
102
|
+
});
|
|
103
|
+
if (!response.ok) {
|
|
104
|
+
throw new Error(`Failed to update content property: ${response.statusText}`);
|
|
105
|
+
}
|
|
106
|
+
const contentProperty = await response.json();
|
|
107
|
+
return contentProperty;
|
|
108
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export const SYNCED_BLOCK_EXTENSION_TYPE = 'com.atlassian.platform.extensions';
|
|
2
|
+
export const SYNCED_BLOCK_EXTENSION_KEY = 'synced-block';
|
|
3
|
+
export const SYNCED_BLOCK_SOURCE_NODE = 'source';
|
|
4
|
+
export const SYNCED_BLOCK_SOURCE_KEY = `${SYNCED_BLOCK_EXTENSION_KEY}:${SYNCED_BLOCK_SOURCE_NODE}`;
|
|
5
|
+
export const SYNCED_BLOCK_REFERENCE_NODE = 'reference';
|
|
6
|
+
export const SYNCED_BLOCK_REFERENCE_KEY = `${SYNCED_BLOCK_EXTENSION_KEY}:${SYNCED_BLOCK_REFERENCE_NODE}`;
|
|
7
|
+
export const isSyncedBlockAttributes = attributes => {
|
|
8
|
+
return !!attributes && typeof attributes === 'object' && 'extensionKey' in attributes && (attributes.extensionKey === SYNCED_BLOCK_SOURCE_KEY || attributes.extensionKey === SYNCED_BLOCK_REFERENCE_KEY);
|
|
9
|
+
};
|
|
10
|
+
export const getDefaultSyncedBlockContent = () => {
|
|
11
|
+
const attributes = {
|
|
12
|
+
extensionType: SYNCED_BLOCK_EXTENSION_TYPE,
|
|
13
|
+
extensionKey: SYNCED_BLOCK_SOURCE_KEY,
|
|
14
|
+
parameters: {
|
|
15
|
+
sourceDocumentAri: '',
|
|
16
|
+
contentAri: '',
|
|
17
|
+
contentPropertyKey: ''
|
|
18
|
+
},
|
|
19
|
+
localId: ''
|
|
20
|
+
};
|
|
21
|
+
return {
|
|
22
|
+
type: 'bodiedExtension',
|
|
23
|
+
attrs: attributes,
|
|
24
|
+
content: [{
|
|
25
|
+
type: 'paragraph',
|
|
26
|
+
content: [{
|
|
27
|
+
type: 'text',
|
|
28
|
+
text: 'This is a synced block. Please edit the source document to update the content.'
|
|
29
|
+
}]
|
|
30
|
+
}]
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
export const parseSyncedBlockContentPropertyValue = value => {
|
|
34
|
+
try {
|
|
35
|
+
if (typeof value === 'string') {
|
|
36
|
+
return JSON.parse(value);
|
|
37
|
+
}
|
|
38
|
+
return value;
|
|
39
|
+
} catch (error) {
|
|
40
|
+
// eslint-disable-next-line no-console
|
|
41
|
+
console.error('Failed to parse synced block content:', error);
|
|
42
|
+
return {
|
|
43
|
+
adf: getDefaultSyncedBlockContent()
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
export const stringifySyncedBlockContentPropertyValue = value => {
|
|
48
|
+
try {
|
|
49
|
+
return JSON.stringify(value);
|
|
50
|
+
} catch (error) {
|
|
51
|
+
// eslint-disable-next-line no-console
|
|
52
|
+
console.error('Failed to serialize synced block content:', error);
|
|
53
|
+
return JSON.stringify({
|
|
54
|
+
adf: getDefaultSyncedBlockContent()
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
};
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
|
|
2
|
+
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
|
|
3
|
+
import _createClass from "@babel/runtime/helpers/createClass";
|
|
4
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
5
|
+
import _regeneratorRuntime from "@babel/runtime/regenerator";
|
|
6
|
+
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; }
|
|
7
|
+
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) { _defineProperty(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; }
|
|
8
|
+
import { JSONTransformer } from '@atlaskit/editor-json-transformer';
|
|
9
|
+
import { getContentPropertyIdFromAri, getPageIdFromAri } from '../ui/extensions/synced-block/utils/ari';
|
|
10
|
+
import { getContentProperty, updateContentProperty } from '../ui/extensions/synced-block/utils/content-property';
|
|
11
|
+
import { parseSyncedBlockContentPropertyValue, stringifySyncedBlockContentPropertyValue } from '../ui/extensions/synced-block/utils/synced-block';
|
|
12
|
+
var transformer = new JSONTransformer();
|
|
13
|
+
var toJSON = function toJSON(node) {
|
|
14
|
+
return transformer.encodeNode(node);
|
|
15
|
+
};
|
|
16
|
+
var getCacheKey = function getCacheKey(_ref) {
|
|
17
|
+
var sourceDocumentAri = _ref.sourceDocumentAri,
|
|
18
|
+
contentAri = _ref.contentAri,
|
|
19
|
+
contentPropertyKey = _ref.contentPropertyKey;
|
|
20
|
+
return "".concat(sourceDocumentAri, "-").concat(contentAri, "-").concat(contentPropertyKey);
|
|
21
|
+
};
|
|
22
|
+
export var SyncClient = /*#__PURE__*/function () {
|
|
23
|
+
function SyncClient() {
|
|
24
|
+
_classCallCheck(this, SyncClient);
|
|
25
|
+
_defineProperty(this, "requestMap", new Map());
|
|
26
|
+
this.requestMap = new Map();
|
|
27
|
+
}
|
|
28
|
+
return _createClass(SyncClient, [{
|
|
29
|
+
key: "getRequestState",
|
|
30
|
+
value: function getRequestState(key) {
|
|
31
|
+
return this.requestMap.get(key);
|
|
32
|
+
}
|
|
33
|
+
}, {
|
|
34
|
+
key: "setRequestState",
|
|
35
|
+
value: function setRequestState(key, state) {
|
|
36
|
+
this.requestMap.set(key, state);
|
|
37
|
+
}
|
|
38
|
+
}, {
|
|
39
|
+
key: "sendRequest",
|
|
40
|
+
value: function () {
|
|
41
|
+
var _sendRequest = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(_ref2) {
|
|
42
|
+
var sourceDocumentAri, contentAri, contentPropertyKey, value, pageId, contentPropertyId, contentProperty, updatedValue;
|
|
43
|
+
return _regeneratorRuntime.wrap(function _callee$(_context) {
|
|
44
|
+
while (1) switch (_context.prev = _context.next) {
|
|
45
|
+
case 0:
|
|
46
|
+
sourceDocumentAri = _ref2.sourceDocumentAri, contentAri = _ref2.contentAri, contentPropertyKey = _ref2.contentPropertyKey, value = _ref2.value;
|
|
47
|
+
pageId = getPageIdFromAri(sourceDocumentAri);
|
|
48
|
+
contentPropertyId = getContentPropertyIdFromAri(contentAri);
|
|
49
|
+
_context.prev = 3;
|
|
50
|
+
_context.next = 6;
|
|
51
|
+
return getContentProperty({
|
|
52
|
+
pageId: pageId,
|
|
53
|
+
contentPropertyId: contentPropertyId
|
|
54
|
+
});
|
|
55
|
+
case 6:
|
|
56
|
+
contentProperty = _context.sent;
|
|
57
|
+
updatedValue = stringifySyncedBlockContentPropertyValue(_objectSpread(_objectSpread({}, parseSyncedBlockContentPropertyValue(contentProperty.value)), JSON.parse(value)));
|
|
58
|
+
_context.next = 10;
|
|
59
|
+
return updateContentProperty({
|
|
60
|
+
pageId: pageId,
|
|
61
|
+
key: contentPropertyKey,
|
|
62
|
+
value: updatedValue,
|
|
63
|
+
signal: undefined
|
|
64
|
+
});
|
|
65
|
+
case 10:
|
|
66
|
+
_context.next = 15;
|
|
67
|
+
break;
|
|
68
|
+
case 12:
|
|
69
|
+
_context.prev = 12;
|
|
70
|
+
_context.t0 = _context["catch"](3);
|
|
71
|
+
// eslint-disable-next-line no-console
|
|
72
|
+
console.error('Failed to update content property:', _context.t0);
|
|
73
|
+
case 15:
|
|
74
|
+
case "end":
|
|
75
|
+
return _context.stop();
|
|
76
|
+
}
|
|
77
|
+
}, _callee, null, [[3, 12]]);
|
|
78
|
+
}));
|
|
79
|
+
function sendRequest(_x) {
|
|
80
|
+
return _sendRequest.apply(this, arguments);
|
|
81
|
+
}
|
|
82
|
+
return sendRequest;
|
|
83
|
+
}()
|
|
84
|
+
}, {
|
|
85
|
+
key: "syncContent",
|
|
86
|
+
value: function syncContent(_ref3) {
|
|
87
|
+
var _this = this;
|
|
88
|
+
var sourceDocumentAri = _ref3.sourceDocumentAri,
|
|
89
|
+
contentAri = _ref3.contentAri,
|
|
90
|
+
contentPropertyKey = _ref3.contentPropertyKey,
|
|
91
|
+
node = _ref3.node;
|
|
92
|
+
var nodeAdf = toJSON(node);
|
|
93
|
+
var key = getCacheKey({
|
|
94
|
+
sourceDocumentAri: sourceDocumentAri,
|
|
95
|
+
contentAri: contentAri,
|
|
96
|
+
contentPropertyKey: contentPropertyKey
|
|
97
|
+
});
|
|
98
|
+
var value = stringifySyncedBlockContentPropertyValue({
|
|
99
|
+
adf: nodeAdf
|
|
100
|
+
});
|
|
101
|
+
var requestState = this.getRequestState(key) || {
|
|
102
|
+
timeout: null,
|
|
103
|
+
pendingValue: null,
|
|
104
|
+
isSending: false
|
|
105
|
+
};
|
|
106
|
+
requestState.pendingValue = value;
|
|
107
|
+
if (requestState.isSending) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (requestState.timeout) {
|
|
111
|
+
clearTimeout(requestState.timeout);
|
|
112
|
+
}
|
|
113
|
+
var send = /*#__PURE__*/function () {
|
|
114
|
+
var _ref4 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2() {
|
|
115
|
+
return _regeneratorRuntime.wrap(function _callee2$(_context2) {
|
|
116
|
+
while (1) switch (_context2.prev = _context2.next) {
|
|
117
|
+
case 0:
|
|
118
|
+
if (!requestState.isSending) {
|
|
119
|
+
_context2.next = 2;
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
return _context2.abrupt("return");
|
|
123
|
+
case 2:
|
|
124
|
+
requestState.isSending = true;
|
|
125
|
+
_context2.prev = 3;
|
|
126
|
+
_context2.next = 6;
|
|
127
|
+
return _this.sendRequest({
|
|
128
|
+
sourceDocumentAri: sourceDocumentAri,
|
|
129
|
+
contentAri: contentAri,
|
|
130
|
+
contentPropertyKey: contentPropertyKey,
|
|
131
|
+
value: requestState.pendingValue || ''
|
|
132
|
+
});
|
|
133
|
+
case 6:
|
|
134
|
+
requestState.pendingValue = null;
|
|
135
|
+
_context2.next = 12;
|
|
136
|
+
break;
|
|
137
|
+
case 9:
|
|
138
|
+
_context2.prev = 9;
|
|
139
|
+
_context2.t0 = _context2["catch"](3);
|
|
140
|
+
// eslint-disable-next-line no-console
|
|
141
|
+
console.error('Failed to send synced block content:', _context2.t0);
|
|
142
|
+
case 12:
|
|
143
|
+
_context2.prev = 12;
|
|
144
|
+
requestState.isSending = false;
|
|
145
|
+
return _context2.finish(12);
|
|
146
|
+
case 15:
|
|
147
|
+
case "end":
|
|
148
|
+
return _context2.stop();
|
|
149
|
+
}
|
|
150
|
+
}, _callee2, null, [[3, 9, 12, 15]]);
|
|
151
|
+
}));
|
|
152
|
+
return function send() {
|
|
153
|
+
return _ref4.apply(this, arguments);
|
|
154
|
+
};
|
|
155
|
+
}();
|
|
156
|
+
requestState.timeout = setTimeout(send, 1000);
|
|
157
|
+
this.setRequestState(key, requestState);
|
|
158
|
+
}
|
|
159
|
+
}]);
|
|
160
|
+
}();
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
|
|
2
|
+
import { PluginKey } from '@atlaskit/editor-prosemirror/state';
|
|
3
|
+
import { SyncClient } from './SyncClient';
|
|
4
|
+
import { findSyncedBlockParent } from './utils';
|
|
5
|
+
export var syncedBlockPluginKey = new PluginKey('syncedBlockPlugin');
|
|
6
|
+
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
8
|
+
|
|
9
|
+
export var createPlugin = function createPlugin() {
|
|
10
|
+
var syncClient = new SyncClient();
|
|
11
|
+
return new SafePlugin({
|
|
12
|
+
key: syncedBlockPluginKey,
|
|
13
|
+
state: {
|
|
14
|
+
init: function init() {
|
|
15
|
+
return {};
|
|
16
|
+
},
|
|
17
|
+
apply: function apply(tr, currentPluginState) {
|
|
18
|
+
if (tr.docChanged) {
|
|
19
|
+
var $pos = tr.selection.$from;
|
|
20
|
+
var syncedBlockParent = findSyncedBlockParent($pos);
|
|
21
|
+
if (syncedBlockParent) {
|
|
22
|
+
var node = syncedBlockParent.node,
|
|
23
|
+
attributes = syncedBlockParent.attributes;
|
|
24
|
+
var _attributes$parameter = attributes.parameters,
|
|
25
|
+
sourceDocumentAri = _attributes$parameter.sourceDocumentAri,
|
|
26
|
+
contentAri = _attributes$parameter.contentAri,
|
|
27
|
+
contentPropertyKey = _attributes$parameter.contentPropertyKey;
|
|
28
|
+
syncClient.syncContent({
|
|
29
|
+
sourceDocumentAri: sourceDocumentAri,
|
|
30
|
+
contentAri: contentAri,
|
|
31
|
+
contentPropertyKey: contentPropertyKey,
|
|
32
|
+
node: node
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
var meta = tr.getMeta(syncedBlockPluginKey);
|
|
37
|
+
if (meta) {
|
|
38
|
+
return meta;
|
|
39
|
+
}
|
|
40
|
+
return currentPluginState;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { isSyncedBlockAttributes } from '../ui/extensions/synced-block/utils/synced-block';
|
|
2
|
+
export var findSyncedBlockParent = function findSyncedBlockParent($pos) {
|
|
3
|
+
for (var i = 0; i <= $pos.depth; i++) {
|
|
4
|
+
var node = $pos.node(i);
|
|
5
|
+
if (isSyncedBlockAttributes(node.attrs)) {
|
|
6
|
+
return {
|
|
7
|
+
node: node,
|
|
8
|
+
attributes: node.attrs
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return;
|
|
13
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { createPlugin } from './pm-plugins/main';
|
|
3
|
+
import { GlobalStylesWrapper } from './ui/extensions/synced-block/components/GlobalStyles';
|
|
4
|
+
export var syncedBlockPlugin = function syncedBlockPlugin() {
|
|
5
|
+
return {
|
|
6
|
+
name: 'syncedBlock',
|
|
7
|
+
pmPlugins: function pmPlugins() {
|
|
8
|
+
return [{
|
|
9
|
+
name: 'syncedBlockPlugin',
|
|
10
|
+
plugin: createPlugin
|
|
11
|
+
}];
|
|
12
|
+
},
|
|
13
|
+
contentComponent: function contentComponent() {
|
|
14
|
+
return /*#__PURE__*/React.createElement(GlobalStylesWrapper, null);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
File without changes
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jsxRuntime classic
|
|
3
|
+
* @jsx jsx
|
|
4
|
+
*/
|
|
5
|
+
// eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled, @atlaskit/ui-styling-standard/no-global-styles
|
|
6
|
+
import { css, Global, jsx } from '@emotion/react';
|
|
7
|
+
var extensionStyles = css({
|
|
8
|
+
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors, @atlaskit/ui-styling-standard/no-unsafe-selectors
|
|
9
|
+
'[extensionkey="synced-block:reference"] .ak-renderer-wrapper > div:last-of-type': {
|
|
10
|
+
padding: "var(--ds-space-250, 20px)",
|
|
11
|
+
paddingRight: "var(--ds-space-250, 20px)"
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
export var GlobalStylesWrapper = function GlobalStylesWrapper() {
|
|
15
|
+
return jsx(Global, {
|
|
16
|
+
styles: [extensionStyles]
|
|
17
|
+
});
|
|
18
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useLiveSyncedBlockContent } from '../hooks/useLiveSyncedBlockContent';
|
|
3
|
+
import SyncedBlockRenderer from './SyncedBlockRenderer';
|
|
4
|
+
var SyncedBlockLiveView = function SyncedBlockLiveView(_ref) {
|
|
5
|
+
var sourceDocumentAri = _ref.sourceDocumentAri,
|
|
6
|
+
contentAri = _ref.contentAri;
|
|
7
|
+
var syncedBlockContent = useLiveSyncedBlockContent({
|
|
8
|
+
sourceDocumentAri: sourceDocumentAri,
|
|
9
|
+
contentAri: contentAri
|
|
10
|
+
});
|
|
11
|
+
if (!syncedBlockContent) {
|
|
12
|
+
return /*#__PURE__*/React.createElement("div", null, "Loading...");
|
|
13
|
+
}
|
|
14
|
+
return /*#__PURE__*/React.createElement(SyncedBlockRenderer, {
|
|
15
|
+
syncedBlockContent: syncedBlockContent
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
export default SyncedBlockLiveView;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ReactRenderer } from '@atlaskit/renderer';
|
|
3
|
+
import { RendererActionsContext } from '@atlaskit/renderer/actions';
|
|
4
|
+
var SyncedBlockRenderer = function SyncedBlockRenderer(_ref) {
|
|
5
|
+
var syncedBlockContent = _ref.syncedBlockContent;
|
|
6
|
+
return /*#__PURE__*/React.createElement(RendererActionsContext, null, /*#__PURE__*/React.createElement(ReactRenderer, {
|
|
7
|
+
adfStage: "stage0"
|
|
8
|
+
// @ts-ignore
|
|
9
|
+
,
|
|
10
|
+
document: {
|
|
11
|
+
type: 'doc',
|
|
12
|
+
version: 1,
|
|
13
|
+
content: syncedBlockContent.adf.content
|
|
14
|
+
},
|
|
15
|
+
appearance: "full-page"
|
|
16
|
+
}));
|
|
17
|
+
};
|
|
18
|
+
export default SyncedBlockRenderer;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// hello.atlassian.net cloud id
|
|
2
|
+
var HELLO_CLOUD_ID = 'a436116f-02ce-4520-8fbb-7301462a1674';
|
|
3
|
+
|
|
4
|
+
// spike page https://hello.atlassian.net/wiki/spaces/~7120208ef57ce4d614485e876489301a16b906/pages/5626233808
|
|
5
|
+
var TEST_PAGE_ID = '5626233808';
|
|
6
|
+
export var getPageId = function getPageId() {
|
|
7
|
+
var _window$location$href, _window$location$href2, _window$location$path;
|
|
8
|
+
return (
|
|
9
|
+
// eslint-disable-next-line require-unicode-regexp
|
|
10
|
+
((_window$location$href = window.location.href.match(/pageId=(\d+)/)) === null || _window$location$href === void 0 ? void 0 : _window$location$href[1]) || ( // eslint-disable-next-line require-unicode-regexp
|
|
11
|
+
(_window$location$href2 = window.location.href.match(/pages\/edit-v2\/(\d+)/)) === null || _window$location$href2 === void 0 ? void 0 : _window$location$href2[1]) || ( // eslint-disable-next-line require-unicode-regexp
|
|
12
|
+
(_window$location$path = window.location.pathname.match(/pages\/(\d+)/)) === null || _window$location$path === void 0 ? void 0 : _window$location$path[1]) ||
|
|
13
|
+
// view page or live doc
|
|
14
|
+
TEST_PAGE_ID
|
|
15
|
+
);
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* This by no means is a stable way to get the cloud id, but it works for now.
|
|
19
|
+
* We should switch passing the cloud id from Confluence to a Editor plugin,
|
|
20
|
+
* for instance the user preferences plugin would have a seperate place for user and cloud info
|
|
21
|
+
* @returns the cloud id from the initial state
|
|
22
|
+
*/
|
|
23
|
+
export var getCloudId = function getCloudId() {
|
|
24
|
+
var _INITIAL_STATE__;
|
|
25
|
+
return ((_INITIAL_STATE__ = window.__INITIAL_STATE__) === null || _INITIAL_STATE__ === void 0 || (_INITIAL_STATE__ = _INITIAL_STATE__.meta) === null || _INITIAL_STATE__ === void 0 ? void 0 : _INITIAL_STATE__['cloud-id']) || HELLO_CLOUD_ID;
|
|
26
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { DefaultExtensionProvider } from '@atlaskit/editor-common/extensions';
|
|
2
|
+
import { getSyncedBlockManifest } from './manifest';
|
|
3
|
+
export var getSyncedBlockExtensionProvider = function getSyncedBlockExtensionProvider() {
|
|
4
|
+
return new DefaultExtensionProvider([getSyncedBlockManifest()]);
|
|
5
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import { usePollContentProperty } from '../hooks/usePollContentProperty';
|
|
3
|
+
import { parseSyncedBlockContentPropertyValue } from '../utils/synced-block';
|
|
4
|
+
export var useLiveSyncedBlockContent = function useLiveSyncedBlockContent(_ref) {
|
|
5
|
+
var sourceDocumentAri = _ref.sourceDocumentAri,
|
|
6
|
+
contentAri = _ref.contentAri;
|
|
7
|
+
var contentProperty = usePollContentProperty({
|
|
8
|
+
sourceDocumentAri: sourceDocumentAri,
|
|
9
|
+
contentAri: contentAri
|
|
10
|
+
});
|
|
11
|
+
return useMemo(function () {
|
|
12
|
+
if (!contentProperty) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
return parseSyncedBlockContentPropertyValue(contentProperty.value);
|
|
17
|
+
} catch (error) {
|
|
18
|
+
// eslint-disable-next-line no-console
|
|
19
|
+
console.error('Failed to extract synced block content:', error);
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}, [contentProperty]);
|
|
23
|
+
};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
2
|
+
import { useEffect, useRef, useState } from 'react';
|
|
3
|
+
import { getContentPropertyIdFromAri, getPageIdFromAri } from '../utils/ari';
|
|
4
|
+
import { getContentProperty } from '../utils/content-property';
|
|
5
|
+
var POLLING_INTERVAL = 1000;
|
|
6
|
+
var cache = new Map();
|
|
7
|
+
var inFlightRequests = new Map();
|
|
8
|
+
var subscribers = new Map();
|
|
9
|
+
var pollingTimeouts = new Map();
|
|
10
|
+
var lastRequestTimes = new Map();
|
|
11
|
+
var getRequestKey = function getRequestKey(pageId, contentPropertyId) {
|
|
12
|
+
return "".concat(pageId, ":").concat(contentPropertyId);
|
|
13
|
+
};
|
|
14
|
+
var fetchContentPropertyWithDedup = function fetchContentPropertyWithDedup(pageId, contentPropertyId) {
|
|
15
|
+
var requestKey = getRequestKey(pageId, contentPropertyId);
|
|
16
|
+
lastRequestTimes.set(requestKey, Date.now());
|
|
17
|
+
var inFlightRequest = inFlightRequests.get(requestKey);
|
|
18
|
+
if (inFlightRequest) {
|
|
19
|
+
return inFlightRequest;
|
|
20
|
+
}
|
|
21
|
+
var requestPromise = getContentProperty({
|
|
22
|
+
pageId: pageId,
|
|
23
|
+
contentPropertyId: contentPropertyId
|
|
24
|
+
}).then(function (result) {
|
|
25
|
+
cache.set(requestKey, result);
|
|
26
|
+
var subscribersForKey = subscribers.get(requestKey);
|
|
27
|
+
if (subscribersForKey) {
|
|
28
|
+
subscribersForKey.forEach(function (callback) {
|
|
29
|
+
return callback(result);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
inFlightRequests.delete(requestKey);
|
|
33
|
+
if (subscribersForKey && subscribersForKey.size > 0) {
|
|
34
|
+
scheduleNextPoll(pageId, contentPropertyId);
|
|
35
|
+
}
|
|
36
|
+
return result;
|
|
37
|
+
}).catch(function (error) {
|
|
38
|
+
inFlightRequests.delete(requestKey);
|
|
39
|
+
var subscribersForKey = subscribers.get(requestKey);
|
|
40
|
+
if (subscribersForKey && subscribersForKey.size > 0) {
|
|
41
|
+
scheduleNextPoll(pageId, contentPropertyId);
|
|
42
|
+
}
|
|
43
|
+
throw error;
|
|
44
|
+
});
|
|
45
|
+
inFlightRequests.set(requestKey, requestPromise);
|
|
46
|
+
return requestPromise;
|
|
47
|
+
};
|
|
48
|
+
var scheduleNextPoll = function scheduleNextPoll(pageId, contentPropertyId) {
|
|
49
|
+
var requestKey = getRequestKey(pageId, contentPropertyId);
|
|
50
|
+
var existingTimeout = pollingTimeouts.get(requestKey);
|
|
51
|
+
if (existingTimeout) {
|
|
52
|
+
clearTimeout(existingTimeout);
|
|
53
|
+
}
|
|
54
|
+
var lastRequestTime = lastRequestTimes.get(requestKey) || 0;
|
|
55
|
+
var timeElapsed = Date.now() - lastRequestTime;
|
|
56
|
+
var delay = Math.max(100, POLLING_INTERVAL - timeElapsed);
|
|
57
|
+
var timeout = setTimeout(function () {
|
|
58
|
+
var subscribersForKey = subscribers.get(requestKey);
|
|
59
|
+
if (subscribersForKey && subscribersForKey.size > 0) {
|
|
60
|
+
fetchContentPropertyWithDedup(pageId, contentPropertyId).catch(function (error) {
|
|
61
|
+
// eslint-disable-next-line no-console
|
|
62
|
+
console.error('Failed to fetch content property:', error);
|
|
63
|
+
});
|
|
64
|
+
} else {
|
|
65
|
+
pollingTimeouts.delete(requestKey);
|
|
66
|
+
}
|
|
67
|
+
}, delay);
|
|
68
|
+
pollingTimeouts.set(requestKey, timeout);
|
|
69
|
+
};
|
|
70
|
+
export var usePollContentProperty = function usePollContentProperty(_ref) {
|
|
71
|
+
var sourceDocumentAri = _ref.sourceDocumentAri,
|
|
72
|
+
contentAri = _ref.contentAri;
|
|
73
|
+
var _useState = useState(),
|
|
74
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
75
|
+
contentProperty = _useState2[0],
|
|
76
|
+
setContentProperty = _useState2[1];
|
|
77
|
+
var initializedRef = useRef(false);
|
|
78
|
+
useEffect(function () {
|
|
79
|
+
var pageId = getPageIdFromAri(sourceDocumentAri);
|
|
80
|
+
var contentPropertyId = getContentPropertyIdFromAri(contentAri);
|
|
81
|
+
var requestKey = getRequestKey(pageId, contentPropertyId);
|
|
82
|
+
var subscribersForKey = subscribers.get(requestKey) || new Set();
|
|
83
|
+
if (!subscribers.has(requestKey)) {
|
|
84
|
+
subscribers.set(requestKey, subscribersForKey);
|
|
85
|
+
}
|
|
86
|
+
subscribersForKey.add(setContentProperty);
|
|
87
|
+
var cachedValue = cache.get(requestKey);
|
|
88
|
+
if (cachedValue) {
|
|
89
|
+
setContentProperty(cachedValue);
|
|
90
|
+
}
|
|
91
|
+
if (subscribersForKey.size === 1 || !initializedRef.current) {
|
|
92
|
+
initializedRef.current = true;
|
|
93
|
+
fetchContentPropertyWithDedup(pageId, contentPropertyId).catch(function (error) {
|
|
94
|
+
// eslint-disable-next-line no-console
|
|
95
|
+
console.error('Failed to fetch content property:', error);
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
return function () {
|
|
99
|
+
subscribersForKey.delete(setContentProperty);
|
|
100
|
+
if (subscribersForKey.size === 0) {
|
|
101
|
+
subscribers.delete(requestKey);
|
|
102
|
+
var existingTimeout = pollingTimeouts.get(requestKey);
|
|
103
|
+
if (existingTimeout) {
|
|
104
|
+
clearTimeout(existingTimeout);
|
|
105
|
+
pollingTimeouts.delete(requestKey);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
}, [sourceDocumentAri, contentAri]);
|
|
110
|
+
var pageId = getPageIdFromAri(sourceDocumentAri);
|
|
111
|
+
var contentPropertyId = getContentPropertyIdFromAri(contentAri);
|
|
112
|
+
var requestKey = getRequestKey(pageId, contentPropertyId);
|
|
113
|
+
return contentProperty || cache.get(requestKey);
|
|
114
|
+
};
|