@atlaskit/editor-synced-block-provider 0.1.3 → 0.3.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 +14 -0
- package/dist/cjs/common/schema.js +18 -0
- package/dist/cjs/common/syncBlockProvider.js +24 -12
- package/dist/cjs/common/syncBlockStoreManager.js +8 -5
- package/dist/cjs/index.js +33 -0
- package/dist/cjs/providers/confluenceContentAPI.js +238 -0
- package/dist/cjs/utils/ari.js +42 -0
- package/dist/cjs/utils/contentProperty.js +192 -0
- package/dist/es2019/common/schema.js +13 -0
- package/dist/es2019/common/syncBlockProvider.js +25 -13
- package/dist/es2019/common/syncBlockStoreManager.js +8 -5
- package/dist/es2019/index.js +4 -1
- package/dist/es2019/providers/confluenceContentAPI.js +150 -0
- package/dist/es2019/utils/ari.js +32 -0
- package/dist/es2019/utils/contentProperty.js +160 -0
- package/dist/esm/common/schema.js +13 -0
- package/dist/esm/common/syncBlockProvider.js +24 -12
- package/dist/esm/common/syncBlockStoreManager.js +8 -5
- package/dist/esm/index.js +4 -1
- package/dist/esm/providers/confluenceContentAPI.js +232 -0
- package/dist/esm/utils/ari.js +36 -0
- package/dist/esm/utils/contentProperty.js +185 -0
- package/dist/types/common/schema.d.ts +6 -0
- package/dist/types/common/syncBlockProvider.d.ts +3 -1
- package/dist/types/common/syncBlockStoreManager.d.ts +2 -1
- package/dist/types/common/types.d.ts +2 -0
- package/dist/types/index.d.ts +4 -1
- package/dist/types/providers/confluenceContentAPI.d.ts +41 -0
- package/dist/types/utils/ari.d.ts +10 -0
- package/dist/types/utils/contentProperty.d.ts +61 -0
- package/dist/types-ts4.5/common/schema.d.ts +6 -0
- package/dist/types-ts4.5/common/syncBlockProvider.d.ts +3 -1
- package/dist/types-ts4.5/common/syncBlockStoreManager.d.ts +2 -1
- package/dist/types-ts4.5/common/types.d.ts +2 -0
- package/dist/types-ts4.5/index.d.ts +4 -1
- package/dist/types-ts4.5/providers/confluenceContentAPI.d.ts +41 -0
- package/dist/types-ts4.5/utils/ari.d.ts +10 -0
- package/dist/types-ts4.5/utils/contentProperty.d.ts +61 -0
- package/package.json +2 -2
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { createSchema } from '@atlaskit/adf-schema';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* We currently do not need any of the new features, like nested tables
|
|
5
|
+
* Otherwise we could import defaultSchemaConfig from '@atlaskit/adf-schema/schema-default';
|
|
6
|
+
* @returns
|
|
7
|
+
*/
|
|
8
|
+
export const getDefaultSyncBlockSchema = () => {
|
|
9
|
+
return createSchema({
|
|
10
|
+
nodes: ['doc', 'paragraph', 'text', 'bulletList', 'orderedList', 'listItem', 'heading', 'blockquote', 'codeBlock', 'panel', 'rule', 'expand', 'nestedExpand', 'table', 'tableCell', 'tableHeader', 'tableRow', 'date', 'status', 'layoutSection', 'layoutColumn', 'unsupportedBlock', 'unsupportedInline'],
|
|
11
|
+
marks: ['link', 'em', 'strong', 'strike', 'subsup', 'underline', 'code', 'textColor', 'backgroundColor', 'alignment', 'indentation', 'border', 'unsupportedMark', 'unsupportedNodeAttribute', 'typeAheadQuery']
|
|
12
|
+
});
|
|
13
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
-
import { useEffect, useState } from 'react';
|
|
2
|
+
import { useEffect, useState, useMemo } from 'react';
|
|
3
3
|
import { convertSyncBlockPMNodeToSyncBlockData } from '../utils/utils';
|
|
4
4
|
import { SyncBlockDataProvider } from './types';
|
|
5
5
|
export class SyncBlockProvider extends SyncBlockDataProvider {
|
|
@@ -32,6 +32,9 @@ export class SyncBlockProvider extends SyncBlockDataProvider {
|
|
|
32
32
|
});
|
|
33
33
|
return Promise.all(resourceIds);
|
|
34
34
|
});
|
|
35
|
+
_defineProperty(this, "getSourceId", () => {
|
|
36
|
+
return this.sourceId;
|
|
37
|
+
});
|
|
35
38
|
this.fetchProvider = fetchProvider;
|
|
36
39
|
this.writeProvider = writeProvider;
|
|
37
40
|
this.sourceId = sourceId;
|
|
@@ -39,26 +42,35 @@ export class SyncBlockProvider extends SyncBlockDataProvider {
|
|
|
39
42
|
}
|
|
40
43
|
export const useFetchDocNode = (editorView, node, defaultDocNode, provider) => {
|
|
41
44
|
const [docNode, setDocNode] = useState(defaultDocNode);
|
|
45
|
+
const fetchNode = (editorView, node, provider) => {
|
|
46
|
+
const nodes = [convertSyncBlockPMNodeToSyncBlockData(node, false)];
|
|
47
|
+
provider === null || provider === void 0 ? void 0 : provider.fetchNodesData(nodes).then(data => {
|
|
48
|
+
var _data$;
|
|
49
|
+
if (data && (_data$ = data[0]) !== null && _data$ !== void 0 && _data$.content) {
|
|
50
|
+
const newNode = editorView.state.schema.nodeFromJSON(data[0].content);
|
|
51
|
+
setDocNode({
|
|
52
|
+
...newNode.toJSON(),
|
|
53
|
+
version: 1
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
};
|
|
42
58
|
useEffect(() => {
|
|
43
59
|
if (!provider) {
|
|
44
60
|
return;
|
|
45
61
|
}
|
|
62
|
+
fetchNode(editorView, node, provider);
|
|
46
63
|
const interval = window.setInterval(() => {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
var _data$;
|
|
50
|
-
if (data && (_data$ = data[0]) !== null && _data$ !== void 0 && _data$.content) {
|
|
51
|
-
const newNode = editorView.state.schema.nodeFromJSON(data[0].content);
|
|
52
|
-
setDocNode({
|
|
53
|
-
...newNode.toJSON(),
|
|
54
|
-
version: 1
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
}, 1000);
|
|
64
|
+
fetchNode(editorView, node, provider);
|
|
65
|
+
}, 3000);
|
|
59
66
|
return () => {
|
|
60
67
|
window.clearInterval(interval);
|
|
61
68
|
};
|
|
62
69
|
}, [editorView, node, provider]);
|
|
63
70
|
return docNode;
|
|
71
|
+
};
|
|
72
|
+
export const useMemoizedSyncedBlockProvider = (fetchProvider, writeProvider, sourceId) => {
|
|
73
|
+
return useMemo(() => {
|
|
74
|
+
return new SyncBlockProvider(fetchProvider, writeProvider, sourceId);
|
|
75
|
+
}, [fetchProvider, writeProvider, sourceId]);
|
|
64
76
|
};
|
|
@@ -9,14 +9,14 @@ import uuid from 'uuid';
|
|
|
9
9
|
// Handles caching, debouncing updates, and publish/subscribe for local changes.
|
|
10
10
|
// Ensures consistency between local and remote state, and can be used in both editor and renderer contexts.
|
|
11
11
|
export class SyncBlockStoreManager {
|
|
12
|
-
constructor(
|
|
12
|
+
constructor(dataProvider) {
|
|
13
13
|
this.syncBlocks = new Map();
|
|
14
|
+
this.dataProvider = dataProvider;
|
|
14
15
|
}
|
|
15
16
|
setEditorView(editorView) {
|
|
16
17
|
this.editorView = editorView;
|
|
17
18
|
}
|
|
18
19
|
isSourceBlock(node) {
|
|
19
|
-
var _this$syncBlocks$get;
|
|
20
20
|
if (node.type.name !== 'syncBlock') {
|
|
21
21
|
return false;
|
|
22
22
|
}
|
|
@@ -24,7 +24,7 @@ export class SyncBlockStoreManager {
|
|
|
24
24
|
resourceId,
|
|
25
25
|
localId
|
|
26
26
|
} = node.attrs;
|
|
27
|
-
return
|
|
27
|
+
return resourceId.includes(localId);
|
|
28
28
|
}
|
|
29
29
|
registerConfirmationCallback(callback) {
|
|
30
30
|
this.confirmationCallback = callback;
|
|
@@ -36,15 +36,18 @@ export class SyncBlockStoreManager {
|
|
|
36
36
|
return !!this.confirmationCallback;
|
|
37
37
|
}
|
|
38
38
|
createSyncBlockNode() {
|
|
39
|
+
var _this$dataProvider;
|
|
39
40
|
// TODO: EDITOR-1644 - properly implement creation of the synced block
|
|
40
41
|
// below is a temporary implementation for the creation of the synced block
|
|
41
42
|
// the resource id needs to have pageId and content property key in it
|
|
43
|
+
// Note: If the data provider is not set, the resource id will be the local id
|
|
42
44
|
|
|
43
|
-
const blockInstanceId = uuid();
|
|
44
45
|
const localId = uuid();
|
|
46
|
+
const sourceId = (_this$dataProvider = this.dataProvider) === null || _this$dataProvider === void 0 ? void 0 : _this$dataProvider.getSourceId();
|
|
47
|
+
const resourceId = sourceId ? `${sourceId}/${localId}` : localId;
|
|
45
48
|
const syncBlockNode = {
|
|
46
49
|
attrs: {
|
|
47
|
-
resourceId
|
|
50
|
+
resourceId,
|
|
48
51
|
localId
|
|
49
52
|
},
|
|
50
53
|
type: 'syncBlock'
|
package/dist/es2019/index.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
/* eslint-disable @atlaskit/editor/no-re-export */
|
|
2
2
|
|
|
3
|
-
export { SyncBlockProvider as SyncedBlockProvider, useFetchDocNode } from './common/syncBlockProvider';
|
|
3
|
+
export { SyncBlockProvider as SyncedBlockProvider, useFetchDocNode, useMemoizedSyncedBlockProvider } from './common/syncBlockProvider';
|
|
4
4
|
export { SyncBlockStoreManager } from './common/syncBlockStoreManager';
|
|
5
5
|
export { inMemoryFetchProvider, inMemoryWriteProvider } from './providers/inMemory';
|
|
6
|
+
export { getDefaultSyncBlockSchema } from './common/schema';
|
|
7
|
+
export { createContentAPIProvidersWithDefaultKey, useMemoizedContentAPIProviders } from './providers/confluenceContentAPI';
|
|
8
|
+
export { getConfluencePageAri } from './utils/ari';
|
|
6
9
|
export { convertSyncBlockPMNodeToSyncBlockData, generateSyncBlockSourceUrl } from './utils/utils';
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
import { useMemo } from 'react';
|
|
3
|
+
import { getLocalIdFromAri, getPageIdFromAri } from '../utils/ari';
|
|
4
|
+
import { getContentProperty, createContentProperty, updateContentProperty } from '../utils/contentProperty';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Configuration for Content API providers
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const getContentPropertyKey = (contentPropertyKey, localId) => {
|
|
11
|
+
return contentPropertyKey + '-' + localId;
|
|
12
|
+
};
|
|
13
|
+
const parseSyncedBlockContentPropertyValue = value => {
|
|
14
|
+
try {
|
|
15
|
+
if (typeof value === 'string') {
|
|
16
|
+
return JSON.parse(value);
|
|
17
|
+
}
|
|
18
|
+
return value;
|
|
19
|
+
} catch (error) {
|
|
20
|
+
// eslint-disable-next-line no-console
|
|
21
|
+
console.error('Failed to parse synced block content:', error);
|
|
22
|
+
return {
|
|
23
|
+
content: undefined
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* ADFFetchProvider implementation that fetches synced block data from Confluence Content API
|
|
30
|
+
*/
|
|
31
|
+
class ConfluenceADFFetchProvider {
|
|
32
|
+
constructor(config) {
|
|
33
|
+
this.config = config;
|
|
34
|
+
}
|
|
35
|
+
async fetchData(resourceId) {
|
|
36
|
+
try {
|
|
37
|
+
var _contentProperty$data, _contentProperty$data2;
|
|
38
|
+
const pageId = getPageIdFromAri(resourceId);
|
|
39
|
+
const localId = getLocalIdFromAri(resourceId);
|
|
40
|
+
const key = getContentPropertyKey(this.config.contentPropertyKey, localId);
|
|
41
|
+
const options = {
|
|
42
|
+
pageId,
|
|
43
|
+
key,
|
|
44
|
+
cloudId: this.config.cloudId
|
|
45
|
+
};
|
|
46
|
+
const contentProperty = await getContentProperty(options);
|
|
47
|
+
const value = (_contentProperty$data = contentProperty.data.confluence.page.properties) === null || _contentProperty$data === void 0 ? void 0 : (_contentProperty$data2 = _contentProperty$data[0]) === null || _contentProperty$data2 === void 0 ? void 0 : _contentProperty$data2.value;
|
|
48
|
+
if (!value) {
|
|
49
|
+
throw new Error('Content property value does not exist');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Parse the synced block content from the property value
|
|
53
|
+
const syncedBlockData = parseSyncedBlockContentPropertyValue(value);
|
|
54
|
+
return {
|
|
55
|
+
content: syncedBlockData.content
|
|
56
|
+
};
|
|
57
|
+
} catch (error) {
|
|
58
|
+
// eslint-disable-next-line no-console
|
|
59
|
+
console.error('Failed to fetch synced block data:', error);
|
|
60
|
+
return {
|
|
61
|
+
content: undefined
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* ADFWriteProvider implementation that writes synced block data to Confluence Content API
|
|
69
|
+
*/
|
|
70
|
+
class ConfluenceADFWriteProvider {
|
|
71
|
+
constructor(config) {
|
|
72
|
+
_defineProperty(this, "createNewContentProperty", async (pageId, key, value) => {
|
|
73
|
+
var _contentProperty$data3;
|
|
74
|
+
const contentProperty = await createContentProperty({
|
|
75
|
+
pageId,
|
|
76
|
+
key,
|
|
77
|
+
value,
|
|
78
|
+
cloudId: this.config.cloudId
|
|
79
|
+
});
|
|
80
|
+
if (((_contentProperty$data3 = contentProperty.data.confluence.createPageProperty.pageProperty) === null || _contentProperty$data3 === void 0 ? void 0 : _contentProperty$data3.key) === key) {
|
|
81
|
+
return key;
|
|
82
|
+
} else {
|
|
83
|
+
throw new Error('Failed to create content property');
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
this.config = config;
|
|
87
|
+
}
|
|
88
|
+
async writeData(sourceId, localId, data, resourceId) {
|
|
89
|
+
try {
|
|
90
|
+
const pageId = getPageIdFromAri(sourceId);
|
|
91
|
+
const syncedBlockValue = JSON.stringify({
|
|
92
|
+
content: data
|
|
93
|
+
});
|
|
94
|
+
if (resourceId) {
|
|
95
|
+
var _contentProperty$data4;
|
|
96
|
+
// Update existing content property
|
|
97
|
+
const localId = getLocalIdFromAri(resourceId);
|
|
98
|
+
const key = getContentPropertyKey(this.config.contentPropertyKey, localId);
|
|
99
|
+
const contentProperty = await updateContentProperty({
|
|
100
|
+
pageId,
|
|
101
|
+
key,
|
|
102
|
+
value: syncedBlockValue,
|
|
103
|
+
cloudId: this.config.cloudId
|
|
104
|
+
});
|
|
105
|
+
if (((_contentProperty$data4 = contentProperty.data.confluence.updateValuePageProperty.pageProperty) === null || _contentProperty$data4 === void 0 ? void 0 : _contentProperty$data4.key) === key) {
|
|
106
|
+
return key;
|
|
107
|
+
} else if (contentProperty.data.confluence.updateValuePageProperty.pageProperty === null) {
|
|
108
|
+
return this.createNewContentProperty(pageId, key, syncedBlockValue);
|
|
109
|
+
} else {
|
|
110
|
+
throw new Error('Failed to update content property');
|
|
111
|
+
}
|
|
112
|
+
} else {
|
|
113
|
+
// Create new content property
|
|
114
|
+
const key = getContentPropertyKey(this.config.contentPropertyKey, localId);
|
|
115
|
+
return this.createNewContentProperty(pageId, key, syncedBlockValue);
|
|
116
|
+
}
|
|
117
|
+
} catch (error) {
|
|
118
|
+
// eslint-disable-next-line no-console
|
|
119
|
+
console.error('Failed to write synced block data:', error);
|
|
120
|
+
return Promise.reject(error);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Factory function to create both providers with shared configuration
|
|
127
|
+
*/
|
|
128
|
+
const createContentAPIProviders = config => {
|
|
129
|
+
const fetchProvider = new ConfluenceADFFetchProvider(config);
|
|
130
|
+
const writeProvider = new ConfluenceADFWriteProvider(config);
|
|
131
|
+
return {
|
|
132
|
+
fetchProvider,
|
|
133
|
+
writeProvider
|
|
134
|
+
};
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Convenience function to create providers with default content property key
|
|
139
|
+
*/
|
|
140
|
+
export const createContentAPIProvidersWithDefaultKey = cloudId => {
|
|
141
|
+
return createContentAPIProviders({
|
|
142
|
+
cloudId,
|
|
143
|
+
contentPropertyKey: 'editor-synced-block'
|
|
144
|
+
});
|
|
145
|
+
};
|
|
146
|
+
export const useMemoizedContentAPIProviders = cloudId => {
|
|
147
|
+
return useMemo(() => {
|
|
148
|
+
return createContentAPIProvidersWithDefaultKey(cloudId);
|
|
149
|
+
}, [cloudId]);
|
|
150
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export const getConfluencePageAri = (pageId, cloudId) => `ari:cloud:confluence:${cloudId}:page/${pageId}`;
|
|
2
|
+
export const getPageIdFromAri = ari => {
|
|
3
|
+
// eslint-disable-next-line require-unicode-regexp
|
|
4
|
+
const match = ari.match(/ari:cloud:confluence:[^:]+:page\/(\d+)/);
|
|
5
|
+
if (match !== null && match !== void 0 && match[1]) {
|
|
6
|
+
return match[1];
|
|
7
|
+
}
|
|
8
|
+
throw new Error(`Invalid page ARI: ${ari}`);
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
*
|
|
13
|
+
* @param ari ari:cloud:confluence:<cloudId>:page/<pageId>/<localId>
|
|
14
|
+
* @returns
|
|
15
|
+
*/
|
|
16
|
+
export const getLocalIdFromAri = ari => {
|
|
17
|
+
// eslint-disable-next-line require-unicode-regexp
|
|
18
|
+
const match = ari.match(/ari:cloud:confluence:[^:]+:page\/\d+\/([a-zA-Z0-9-]+)/);
|
|
19
|
+
if (match !== null && match !== void 0 && match[1]) {
|
|
20
|
+
return match[1];
|
|
21
|
+
}
|
|
22
|
+
throw new Error(`Invalid page ARI: ${ari}`);
|
|
23
|
+
};
|
|
24
|
+
export const getContentPropertyAri = (contentPropertyId, cloudId) => `ari:cloud:confluence:${cloudId}:content/${contentPropertyId}`;
|
|
25
|
+
export const getContentPropertyIdFromAri = ari => {
|
|
26
|
+
// eslint-disable-next-line require-unicode-regexp
|
|
27
|
+
const match = ari.match(/ari:cloud:confluence:[^:]+:content\/([^/]+)/);
|
|
28
|
+
if (match) {
|
|
29
|
+
return match[1];
|
|
30
|
+
}
|
|
31
|
+
throw new Error(`Invalid content property ARI: ${ari}`);
|
|
32
|
+
};
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { getConfluencePageAri } from './ari';
|
|
2
|
+
const COMMON_HEADERS = {
|
|
3
|
+
'Content-Type': 'application/json',
|
|
4
|
+
Accept: 'application/json'
|
|
5
|
+
};
|
|
6
|
+
const AGG_HEADERS = {
|
|
7
|
+
'X-ExperimentalApi': 'confluence-agg-beta'
|
|
8
|
+
};
|
|
9
|
+
const GRAPHQL_ENDPOINT = '/gateway/api/graphql';
|
|
10
|
+
const GET_OPERATION_NAME = 'EDITOR_SYNCED_BLOCK_GET';
|
|
11
|
+
const CREATE_OPERATION_NAME = 'EDITOR_SYNCED_BLOCK_CREATE';
|
|
12
|
+
const UPDATE_OPERATION_NAME = 'EDITOR_SYNCED_BLOCK_UPDATE';
|
|
13
|
+
/**
|
|
14
|
+
* Query to get the page property by key
|
|
15
|
+
* @param documentARI
|
|
16
|
+
* @param key
|
|
17
|
+
* @returns
|
|
18
|
+
*/
|
|
19
|
+
const GET_QUERY = `query ${GET_OPERATION_NAME} ($id: ID!, $keys: [String]!) {
|
|
20
|
+
confluence {
|
|
21
|
+
page (id: $id) {
|
|
22
|
+
properties(keys: $keys) {
|
|
23
|
+
key,
|
|
24
|
+
value
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}`;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Query to create a page property with key and value
|
|
32
|
+
* @param documentARI
|
|
33
|
+
* @param key
|
|
34
|
+
* @param value
|
|
35
|
+
* @returns
|
|
36
|
+
*/
|
|
37
|
+
const CREATE_QUERY = `mutation ${CREATE_OPERATION_NAME} ($input: ConfluenceCreatePagePropertyInput!){
|
|
38
|
+
confluence {
|
|
39
|
+
createPageProperty(input: $input) {
|
|
40
|
+
pageProperty {
|
|
41
|
+
key,
|
|
42
|
+
value
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}`;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Query to update a page property with key and value without bumping the version
|
|
50
|
+
* @param documentARI
|
|
51
|
+
* @param key
|
|
52
|
+
* @param value
|
|
53
|
+
* @returns
|
|
54
|
+
*/
|
|
55
|
+
const UPDATE_QUERY = `mutation ${UPDATE_OPERATION_NAME} ($input: ConfluenceUpdateValuePagePropertyInput!) {
|
|
56
|
+
confluence {
|
|
57
|
+
updateValuePageProperty(input: $input) {
|
|
58
|
+
pageProperty {
|
|
59
|
+
key,
|
|
60
|
+
value
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}`;
|
|
65
|
+
export const getContentProperty = async ({
|
|
66
|
+
pageId,
|
|
67
|
+
key,
|
|
68
|
+
cloudId
|
|
69
|
+
}) => {
|
|
70
|
+
const documentARI = getConfluencePageAri(pageId, cloudId);
|
|
71
|
+
const bodyData = {
|
|
72
|
+
query: GET_QUERY,
|
|
73
|
+
operationName: GET_OPERATION_NAME,
|
|
74
|
+
variables: {
|
|
75
|
+
id: documentARI,
|
|
76
|
+
keys: [key]
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
const response = await fetch(GRAPHQL_ENDPOINT, {
|
|
80
|
+
method: 'POST',
|
|
81
|
+
headers: {
|
|
82
|
+
...COMMON_HEADERS,
|
|
83
|
+
...AGG_HEADERS
|
|
84
|
+
},
|
|
85
|
+
body: JSON.stringify(bodyData)
|
|
86
|
+
});
|
|
87
|
+
if (!response.ok) {
|
|
88
|
+
throw new Error(`Failed to get content property: ${response.statusText}`);
|
|
89
|
+
}
|
|
90
|
+
const contentProperty = await response.json();
|
|
91
|
+
return contentProperty;
|
|
92
|
+
};
|
|
93
|
+
export const updateContentProperty = async ({
|
|
94
|
+
pageId,
|
|
95
|
+
key,
|
|
96
|
+
value,
|
|
97
|
+
cloudId
|
|
98
|
+
}) => {
|
|
99
|
+
const documentARI = getConfluencePageAri(pageId, cloudId);
|
|
100
|
+
const bodyData = {
|
|
101
|
+
query: UPDATE_QUERY,
|
|
102
|
+
operationName: UPDATE_OPERATION_NAME,
|
|
103
|
+
variables: {
|
|
104
|
+
input: {
|
|
105
|
+
pageId: documentARI,
|
|
106
|
+
key,
|
|
107
|
+
value,
|
|
108
|
+
useSameVersion: true
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
const response = await fetch(GRAPHQL_ENDPOINT, {
|
|
113
|
+
method: 'POST',
|
|
114
|
+
headers: {
|
|
115
|
+
...COMMON_HEADERS,
|
|
116
|
+
...AGG_HEADERS
|
|
117
|
+
},
|
|
118
|
+
body: JSON.stringify(bodyData)
|
|
119
|
+
});
|
|
120
|
+
if (!response.ok) {
|
|
121
|
+
throw new Error(`Failed to update content property: ${response.statusText}`);
|
|
122
|
+
}
|
|
123
|
+
const contentProperty = await response.json();
|
|
124
|
+
return contentProperty;
|
|
125
|
+
};
|
|
126
|
+
export const createContentProperty = async ({
|
|
127
|
+
pageId,
|
|
128
|
+
key,
|
|
129
|
+
value,
|
|
130
|
+
cloudId
|
|
131
|
+
}) => {
|
|
132
|
+
const documentARI = getConfluencePageAri(pageId, cloudId);
|
|
133
|
+
|
|
134
|
+
// eslint-disable-next-line require-unicode-regexp
|
|
135
|
+
const escapedValue = value.replace(/"/g, '\\"');
|
|
136
|
+
const bodyData = {
|
|
137
|
+
query: CREATE_QUERY,
|
|
138
|
+
operationName: CREATE_OPERATION_NAME,
|
|
139
|
+
variables: {
|
|
140
|
+
input: {
|
|
141
|
+
pageId: documentARI,
|
|
142
|
+
key,
|
|
143
|
+
value: escapedValue
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
const response = await fetch(GRAPHQL_ENDPOINT, {
|
|
148
|
+
method: 'POST',
|
|
149
|
+
headers: {
|
|
150
|
+
...COMMON_HEADERS,
|
|
151
|
+
...AGG_HEADERS
|
|
152
|
+
},
|
|
153
|
+
body: JSON.stringify(bodyData)
|
|
154
|
+
});
|
|
155
|
+
if (!response.ok) {
|
|
156
|
+
throw new Error(`Failed to create content property: ${response.statusText}`);
|
|
157
|
+
}
|
|
158
|
+
const contentProperty = await response.json();
|
|
159
|
+
return contentProperty;
|
|
160
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { createSchema } from '@atlaskit/adf-schema';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* We currently do not need any of the new features, like nested tables
|
|
5
|
+
* Otherwise we could import defaultSchemaConfig from '@atlaskit/adf-schema/schema-default';
|
|
6
|
+
* @returns
|
|
7
|
+
*/
|
|
8
|
+
export var getDefaultSyncBlockSchema = function getDefaultSyncBlockSchema() {
|
|
9
|
+
return createSchema({
|
|
10
|
+
nodes: ['doc', 'paragraph', 'text', 'bulletList', 'orderedList', 'listItem', 'heading', 'blockquote', 'codeBlock', 'panel', 'rule', 'expand', 'nestedExpand', 'table', 'tableCell', 'tableHeader', 'tableRow', 'date', 'status', 'layoutSection', 'layoutColumn', 'unsupportedBlock', 'unsupportedInline'],
|
|
11
|
+
marks: ['link', 'em', 'strong', 'strike', 'subsup', 'underline', 'code', 'textColor', 'backgroundColor', 'alignment', 'indentation', 'border', 'unsupportedMark', 'unsupportedNodeAttribute', 'typeAheadQuery']
|
|
12
|
+
});
|
|
13
|
+
};
|
|
@@ -9,7 +9,7 @@ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbol
|
|
|
9
9
|
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; }
|
|
10
10
|
function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); }
|
|
11
11
|
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
|
|
12
|
-
import { useEffect, useState } from 'react';
|
|
12
|
+
import { useEffect, useState, useMemo } from 'react';
|
|
13
13
|
import { convertSyncBlockPMNodeToSyncBlockData } from '../utils/utils';
|
|
14
14
|
import { SyncBlockDataProvider } from './types';
|
|
15
15
|
export var SyncBlockProvider = /*#__PURE__*/function (_SyncBlockDataProvide) {
|
|
@@ -48,6 +48,9 @@ export var SyncBlockProvider = /*#__PURE__*/function (_SyncBlockDataProvide) {
|
|
|
48
48
|
});
|
|
49
49
|
return Promise.all(resourceIds);
|
|
50
50
|
});
|
|
51
|
+
_defineProperty(_this, "getSourceId", function () {
|
|
52
|
+
return _this.sourceId;
|
|
53
|
+
});
|
|
51
54
|
_this.fetchProvider = fetchProvider;
|
|
52
55
|
_this.writeProvider = writeProvider;
|
|
53
56
|
_this.sourceId = sourceId;
|
|
@@ -61,25 +64,34 @@ export var useFetchDocNode = function useFetchDocNode(editorView, node, defaultD
|
|
|
61
64
|
_useState2 = _slicedToArray(_useState, 2),
|
|
62
65
|
docNode = _useState2[0],
|
|
63
66
|
setDocNode = _useState2[1];
|
|
67
|
+
var fetchNode = function fetchNode(editorView, node, provider) {
|
|
68
|
+
var nodes = [convertSyncBlockPMNodeToSyncBlockData(node, false)];
|
|
69
|
+
provider === null || provider === void 0 || provider.fetchNodesData(nodes).then(function (data) {
|
|
70
|
+
var _data$;
|
|
71
|
+
if (data && (_data$ = data[0]) !== null && _data$ !== void 0 && _data$.content) {
|
|
72
|
+
var newNode = editorView.state.schema.nodeFromJSON(data[0].content);
|
|
73
|
+
setDocNode(_objectSpread(_objectSpread({}, newNode.toJSON()), {}, {
|
|
74
|
+
version: 1
|
|
75
|
+
}));
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
};
|
|
64
79
|
useEffect(function () {
|
|
65
80
|
if (!provider) {
|
|
66
81
|
return;
|
|
67
82
|
}
|
|
83
|
+
fetchNode(editorView, node, provider);
|
|
68
84
|
var interval = window.setInterval(function () {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
var _data$;
|
|
72
|
-
if (data && (_data$ = data[0]) !== null && _data$ !== void 0 && _data$.content) {
|
|
73
|
-
var newNode = editorView.state.schema.nodeFromJSON(data[0].content);
|
|
74
|
-
setDocNode(_objectSpread(_objectSpread({}, newNode.toJSON()), {}, {
|
|
75
|
-
version: 1
|
|
76
|
-
}));
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
}, 1000);
|
|
85
|
+
fetchNode(editorView, node, provider);
|
|
86
|
+
}, 3000);
|
|
80
87
|
return function () {
|
|
81
88
|
window.clearInterval(interval);
|
|
82
89
|
};
|
|
83
90
|
}, [editorView, node, provider]);
|
|
84
91
|
return docNode;
|
|
92
|
+
};
|
|
93
|
+
export var useMemoizedSyncedBlockProvider = function useMemoizedSyncedBlockProvider(fetchProvider, writeProvider, sourceId) {
|
|
94
|
+
return useMemo(function () {
|
|
95
|
+
return new SyncBlockProvider(fetchProvider, writeProvider, sourceId);
|
|
96
|
+
}, [fetchProvider, writeProvider, sourceId]);
|
|
85
97
|
};
|
|
@@ -13,9 +13,10 @@ import uuid from 'uuid';
|
|
|
13
13
|
// Handles caching, debouncing updates, and publish/subscribe for local changes.
|
|
14
14
|
// Ensures consistency between local and remote state, and can be used in both editor and renderer contexts.
|
|
15
15
|
export var SyncBlockStoreManager = /*#__PURE__*/function () {
|
|
16
|
-
function SyncBlockStoreManager(
|
|
16
|
+
function SyncBlockStoreManager(dataProvider) {
|
|
17
17
|
_classCallCheck(this, SyncBlockStoreManager);
|
|
18
18
|
this.syncBlocks = new Map();
|
|
19
|
+
this.dataProvider = dataProvider;
|
|
19
20
|
}
|
|
20
21
|
return _createClass(SyncBlockStoreManager, [{
|
|
21
22
|
key: "setEditorView",
|
|
@@ -25,14 +26,13 @@ export var SyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
25
26
|
}, {
|
|
26
27
|
key: "isSourceBlock",
|
|
27
28
|
value: function isSourceBlock(node) {
|
|
28
|
-
var _this$syncBlocks$get;
|
|
29
29
|
if (node.type.name !== 'syncBlock') {
|
|
30
30
|
return false;
|
|
31
31
|
}
|
|
32
32
|
var _node$attrs = node.attrs,
|
|
33
33
|
resourceId = _node$attrs.resourceId,
|
|
34
34
|
localId = _node$attrs.localId;
|
|
35
|
-
return
|
|
35
|
+
return resourceId.includes(localId);
|
|
36
36
|
}
|
|
37
37
|
}, {
|
|
38
38
|
key: "registerConfirmationCallback",
|
|
@@ -51,15 +51,18 @@ export var SyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
51
51
|
}, {
|
|
52
52
|
key: "createSyncBlockNode",
|
|
53
53
|
value: function createSyncBlockNode() {
|
|
54
|
+
var _this$dataProvider;
|
|
54
55
|
// TODO: EDITOR-1644 - properly implement creation of the synced block
|
|
55
56
|
// below is a temporary implementation for the creation of the synced block
|
|
56
57
|
// the resource id needs to have pageId and content property key in it
|
|
58
|
+
// Note: If the data provider is not set, the resource id will be the local id
|
|
57
59
|
|
|
58
|
-
var blockInstanceId = uuid();
|
|
59
60
|
var localId = uuid();
|
|
61
|
+
var sourceId = (_this$dataProvider = this.dataProvider) === null || _this$dataProvider === void 0 ? void 0 : _this$dataProvider.getSourceId();
|
|
62
|
+
var resourceId = sourceId ? "".concat(sourceId, "/").concat(localId) : localId;
|
|
60
63
|
var syncBlockNode = {
|
|
61
64
|
attrs: {
|
|
62
|
-
resourceId:
|
|
65
|
+
resourceId: resourceId,
|
|
63
66
|
localId: localId
|
|
64
67
|
},
|
|
65
68
|
type: 'syncBlock'
|
package/dist/esm/index.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
/* eslint-disable @atlaskit/editor/no-re-export */
|
|
2
2
|
|
|
3
|
-
export { SyncBlockProvider as SyncedBlockProvider, useFetchDocNode } from './common/syncBlockProvider';
|
|
3
|
+
export { SyncBlockProvider as SyncedBlockProvider, useFetchDocNode, useMemoizedSyncedBlockProvider } from './common/syncBlockProvider';
|
|
4
4
|
export { SyncBlockStoreManager } from './common/syncBlockStoreManager';
|
|
5
5
|
export { inMemoryFetchProvider, inMemoryWriteProvider } from './providers/inMemory';
|
|
6
|
+
export { getDefaultSyncBlockSchema } from './common/schema';
|
|
7
|
+
export { createContentAPIProvidersWithDefaultKey, useMemoizedContentAPIProviders } from './providers/confluenceContentAPI';
|
|
8
|
+
export { getConfluencePageAri } from './utils/ari';
|
|
6
9
|
export { convertSyncBlockPMNodeToSyncBlockData, generateSyncBlockSourceUrl } from './utils/utils';
|