@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 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 _ydriveFileChanged;
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
- const key = `${options.format}:${options.contentType}:${options.path}`;
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._ydriveFileChanged.emit({
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(options.path);
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
- this._ydriveFileChanged = new Signal(this);
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._ydriveFileChanged;
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.0",
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.0",
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",