@jupyter/docprovider 4.1.0 → 4.1.2
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/lib/ydrive.d.ts +3 -1
- package/lib/ydrive.js +81 -7
- package/package.json +2 -2
package/lib/ydrive.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ declare namespace RtcContentProvider {
|
|
|
16
16
|
trans: TranslationBundle;
|
|
17
17
|
globalAwareness: Awareness | null;
|
|
18
18
|
docmanagerSettings: ISettingRegistry.ISettings | null;
|
|
19
|
+
fileChanged?: ISignal<Contents.IDrive, Contents.IChangedArgs>;
|
|
19
20
|
}
|
|
20
21
|
}
|
|
21
22
|
export declare class RtcContentProvider extends RestContentProvider implements IContentProvider {
|
|
@@ -56,7 +57,8 @@ export declare class RtcContentProvider extends RestContentProvider implements I
|
|
|
56
57
|
private _trans;
|
|
57
58
|
private _globalAwareness;
|
|
58
59
|
private _providers;
|
|
59
|
-
private
|
|
60
|
+
private _providerFileChanged;
|
|
61
|
+
private _driveFileChanged?;
|
|
60
62
|
private _serverSettings;
|
|
61
63
|
private _docmanagerSettings;
|
|
62
64
|
}
|
package/lib/ydrive.js
CHANGED
|
@@ -17,7 +17,7 @@ export class RtcContentProvider extends RestContentProvider {
|
|
|
17
17
|
constructor(options) {
|
|
18
18
|
super(options);
|
|
19
19
|
this._onCreate = (options, sharedModel) => {
|
|
20
|
-
var _a, _b, _c, _d, _e, _f;
|
|
20
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
21
21
|
if (typeof options.format !== 'string') {
|
|
22
22
|
return;
|
|
23
23
|
}
|
|
@@ -47,13 +47,80 @@ export class RtcContentProvider extends RestContentProvider {
|
|
|
47
47
|
documents.push(options.path);
|
|
48
48
|
(_f = this._globalAwareness) === null || _f === void 0 ? void 0 : _f.setLocalStateField('documents', documents);
|
|
49
49
|
}
|
|
50
|
-
|
|
50
|
+
let path = options.path;
|
|
51
|
+
let key = `${options.format}:${options.contentType}:${path}`;
|
|
51
52
|
this._providers.set(key, provider);
|
|
53
|
+
const handlePathChange = (pathChange) => {
|
|
54
|
+
var _a, _b;
|
|
55
|
+
const oldPath = pathChange.oldValue;
|
|
56
|
+
const newPath = pathChange.newValue;
|
|
57
|
+
if (!oldPath || !newPath) {
|
|
58
|
+
// This is expected when shared model initializes and the path is first populated
|
|
59
|
+
console.debug('New or old path not given', pathChange);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const oldKey = `${options.format}:${options.contentType}:${oldPath}`;
|
|
63
|
+
if (oldKey !== key) {
|
|
64
|
+
console.error('The computed old provider key is different from the current key');
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const newKey = `${options.format}:${options.contentType}:${newPath}`;
|
|
68
|
+
// Check if the provider is still registered (it may have been disposed if document was closed)
|
|
69
|
+
const provider = this._providers.get(oldKey);
|
|
70
|
+
if (!provider) {
|
|
71
|
+
console.warn(`Could not find a provider to update after rename ${oldKey}, ${newKey}`);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
// Re-register the provider under the new key
|
|
75
|
+
this._providers.set(newKey, provider);
|
|
76
|
+
this._providers.delete(oldKey);
|
|
77
|
+
// Update the provider key so that it can be disposed correctly when shared document closes
|
|
78
|
+
key = newKey;
|
|
79
|
+
path = newPath;
|
|
80
|
+
// Update the documents field
|
|
81
|
+
const state = ((_a = this._globalAwareness) === null || _a === void 0 ? void 0 : _a.getLocalState()) || {};
|
|
82
|
+
const documents = state.documents || [];
|
|
83
|
+
const oldPathIndex = documents.indexOf(oldPath);
|
|
84
|
+
if (documents.includes(oldPath) && !documents.includes(newPath)) {
|
|
85
|
+
documents.splice(oldPathIndex, 1);
|
|
86
|
+
documents.push(newPath);
|
|
87
|
+
(_b = this._globalAwareness) === null || _b === void 0 ? void 0 : _b.setLocalStateField('documents', documents);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
// The information about file being renamed can come from two places:
|
|
91
|
+
// - from the sharedModel via changed signal with documentChange
|
|
92
|
+
// - from the fileChanged signal of the drive
|
|
93
|
+
// Neither method is foolproof:
|
|
94
|
+
// - the shared model approach can be delayed as the change needs to be
|
|
95
|
+
// reflected by the server and come back, in which case we get a race condition
|
|
96
|
+
// - the fileChanged signal is emitted with a larger delay for renames of collaborators
|
|
97
|
+
// Thus we need both.
|
|
98
|
+
const handleFileChangedSignal = (_, change) => {
|
|
99
|
+
var _a, _b;
|
|
100
|
+
if (change.type !== 'rename') {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const oldPath = (_a = change.oldValue) === null || _a === void 0 ? void 0 : _a.path;
|
|
104
|
+
const newPath = (_b = change.newValue) === null || _b === void 0 ? void 0 : _b.path;
|
|
105
|
+
if (oldPath !== path) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
handlePathChange({
|
|
109
|
+
oldValue: oldPath,
|
|
110
|
+
newValue: newPath,
|
|
111
|
+
name: 'path'
|
|
112
|
+
});
|
|
113
|
+
};
|
|
114
|
+
(_g = this._driveFileChanged) === null || _g === void 0 ? void 0 : _g.connect(handleFileChangedSignal);
|
|
52
115
|
sharedModel.changed.connect(async (_, change) => {
|
|
53
116
|
var _a;
|
|
54
117
|
if (!change.stateChange) {
|
|
55
118
|
return;
|
|
56
119
|
}
|
|
120
|
+
const pathChanges = change.stateChange.filter(change => change.name === 'path');
|
|
121
|
+
for (const pathChange of pathChanges) {
|
|
122
|
+
handlePathChange(pathChange);
|
|
123
|
+
}
|
|
57
124
|
const hashChanges = change.stateChange.filter(change => change.name === 'hash');
|
|
58
125
|
if (hashChanges.length === 0) {
|
|
59
126
|
return;
|
|
@@ -67,7 +134,7 @@ export class RtcContentProvider extends RestContentProvider {
|
|
|
67
134
|
// observers about this change so that they can store the new hash value.
|
|
68
135
|
const newPath = (_a = sharedModel.state.path) !== null && _a !== void 0 ? _a : options.path;
|
|
69
136
|
const model = await this.get(newPath, { content: false });
|
|
70
|
-
this.
|
|
137
|
+
this._providerFileChanged.emit({
|
|
71
138
|
type: 'save',
|
|
72
139
|
newValue: { ...model, hash: hashChange.newValue },
|
|
73
140
|
// we do not have the old model because it was discarded when server made the change,
|
|
@@ -76,7 +143,7 @@ export class RtcContentProvider extends RestContentProvider {
|
|
|
76
143
|
});
|
|
77
144
|
});
|
|
78
145
|
sharedModel.disposed.connect(() => {
|
|
79
|
-
var _a, _b;
|
|
146
|
+
var _a, _b, _c;
|
|
80
147
|
const provider = this._providers.get(key);
|
|
81
148
|
if (provider) {
|
|
82
149
|
provider.dispose();
|
|
@@ -85,11 +152,13 @@ export class RtcContentProvider extends RestContentProvider {
|
|
|
85
152
|
// Remove the document path from the list of opened ones for this user.
|
|
86
153
|
const state = ((_a = this._globalAwareness) === null || _a === void 0 ? void 0 : _a.getLocalState()) || {};
|
|
87
154
|
const documents = state.documents || [];
|
|
88
|
-
const index = documents.indexOf(
|
|
155
|
+
const index = documents.indexOf(path);
|
|
89
156
|
if (index > -1) {
|
|
90
157
|
documents.splice(index, 1);
|
|
91
158
|
}
|
|
92
159
|
(_b = this._globalAwareness) === null || _b === void 0 ? void 0 : _b.setLocalStateField('documents', documents);
|
|
160
|
+
// Disconnect signal
|
|
161
|
+
(_c = this._driveFileChanged) === null || _c === void 0 ? void 0 : _c.disconnect(handleFileChangedSignal);
|
|
93
162
|
});
|
|
94
163
|
}
|
|
95
164
|
catch (error) {
|
|
@@ -99,7 +168,8 @@ export class RtcContentProvider extends RestContentProvider {
|
|
|
99
168
|
}
|
|
100
169
|
};
|
|
101
170
|
this._saveCounter = 0;
|
|
102
|
-
|
|
171
|
+
// This is for emitting signals to be proxied to `Drive.fileChanged`
|
|
172
|
+
this._providerFileChanged = new Signal(this);
|
|
103
173
|
this._user = options.user;
|
|
104
174
|
this._trans = options.trans;
|
|
105
175
|
this._globalAwareness = options.globalAwareness;
|
|
@@ -107,6 +177,7 @@ export class RtcContentProvider extends RestContentProvider {
|
|
|
107
177
|
this.sharedModelFactory = new SharedModelFactory(this._onCreate);
|
|
108
178
|
this._providers = new Map();
|
|
109
179
|
this._docmanagerSettings = options.docmanagerSettings;
|
|
180
|
+
this._driveFileChanged = options.fileChanged;
|
|
110
181
|
}
|
|
111
182
|
get providers() {
|
|
112
183
|
return this._providers;
|
|
@@ -215,6 +286,9 @@ export class RtcContentProvider extends RestContentProvider {
|
|
|
215
286
|
};
|
|
216
287
|
return this.get(localPath, fetchOptions);
|
|
217
288
|
}
|
|
289
|
+
else {
|
|
290
|
+
console.warn(`Could not find a provider for ${localPath}, falling back to REST API save`);
|
|
291
|
+
}
|
|
218
292
|
}
|
|
219
293
|
return super.save(localPath, options);
|
|
220
294
|
}
|
|
@@ -222,7 +296,7 @@ export class RtcContentProvider extends RestContentProvider {
|
|
|
222
296
|
* A signal emitted when a file operation takes place.
|
|
223
297
|
*/
|
|
224
298
|
get fileChanged() {
|
|
225
|
-
return this.
|
|
299
|
+
return this._providerFileChanged;
|
|
226
300
|
}
|
|
227
301
|
}
|
|
228
302
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jupyter/docprovider",
|
|
3
|
-
"version": "4.1.
|
|
3
|
+
"version": "4.1.2",
|
|
4
4
|
"description": "JupyterLab - Document Provider",
|
|
5
5
|
"homepage": "https://github.com/jupyterlab/jupyter-collaboration",
|
|
6
6
|
"bugs": {
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"watch": "tsc -b --watch"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@jupyter/collaborative-drive": "^4.1.
|
|
44
|
+
"@jupyter/collaborative-drive": "^4.1.2",
|
|
45
45
|
"@jupyter/ydoc": "^2.1.3 || ^3.0.0",
|
|
46
46
|
"@jupyterlab/apputils": "^4.4.0",
|
|
47
47
|
"@jupyterlab/cells": "^4.4.0",
|