@svar-ui/gantt-data-provider 2.7.2 → 2.8.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/dist/index.js +504 -102
- package/dist/index.js.map +1 -1
- package/dist/types/EventStream.d.ts +31 -0
- package/dist/types/RemoteEvents.d.ts +45 -0
- package/dist/types/RestDataProvider.d.ts +4 -1
- package/dist/types/constants.d.ts +3 -0
- package/dist/types/index.d.ts +4 -2
- package/dist/types/uuid.d.ts +1 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -201,7 +201,7 @@ var ActionQueue = class {
|
|
|
201
201
|
copy = { ...obj };
|
|
202
202
|
}
|
|
203
203
|
copy[key] = hasRealID;
|
|
204
|
-
} else if (
|
|
204
|
+
} else if (test !== ignore) {
|
|
205
205
|
return cantSendYet;
|
|
206
206
|
}
|
|
207
207
|
}
|
|
@@ -316,7 +316,7 @@ var Rest = class extends EventBus {
|
|
|
316
316
|
if (data) {
|
|
317
317
|
req.body = typeof data === "object" ? this.toPayload(data) : data;
|
|
318
318
|
}
|
|
319
|
-
const slash = this._url.
|
|
319
|
+
const slash = !url || this._url.endsWith("/") || url[0] === "/" ? "" : "/";
|
|
320
320
|
return fetch(`${this._url}${slash}${url || ""}`, req).then(
|
|
321
321
|
(res) => res.json()
|
|
322
322
|
);
|
|
@@ -3056,26 +3056,34 @@ function cleanEscapedString(input) {
|
|
|
3056
3056
|
return matched[1].replace(doubleQuoteRegExp, "'");
|
|
3057
3057
|
}
|
|
3058
3058
|
|
|
3059
|
+
const TaskID = 1;
|
|
3060
|
+
const LinkID = 2;
|
|
3061
|
+
const AssignmentID = 3;
|
|
3062
|
+
|
|
3063
|
+
const DATE_KEYS = ["start", "end", "base_start", "base_end", "deadline"];
|
|
3059
3064
|
class RestDataProvider extends Rest {
|
|
3060
3065
|
changeQueue = [];
|
|
3061
3066
|
_batchUrl;
|
|
3062
3067
|
flushTimeout = null;
|
|
3068
|
+
headers = {};
|
|
3063
3069
|
constructor(url, config) {
|
|
3064
3070
|
super(url);
|
|
3065
3071
|
this._batchUrl = config?.batchURL;
|
|
3066
3072
|
}
|
|
3073
|
+
setHeaders(headers) {
|
|
3074
|
+
this.headers = headers;
|
|
3075
|
+
}
|
|
3067
3076
|
getHandlers() {
|
|
3068
3077
|
return {
|
|
3069
3078
|
"add-task": {
|
|
3070
3079
|
ignoreID: true,
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
}
|
|
3080
|
+
kind: TaskID,
|
|
3081
|
+
handler: async (ev) => this.sendBatch(`tasks`, "POST", {
|
|
3082
|
+
task: ev.task,
|
|
3083
|
+
target: ev.target,
|
|
3084
|
+
mode: ev.mode,
|
|
3085
|
+
index: ev.index
|
|
3086
|
+
})
|
|
3079
3087
|
},
|
|
3080
3088
|
"update-task": {
|
|
3081
3089
|
debounce: 500,
|
|
@@ -3090,15 +3098,15 @@ class RestDataProvider extends Rest {
|
|
|
3090
3098
|
},
|
|
3091
3099
|
"copy-task": {
|
|
3092
3100
|
ignoreID: true,
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
}
|
|
3101
|
+
kind: TaskID,
|
|
3102
|
+
handler: async (ev) => this.sendBatch(`tasks/${ev.source}`, "PUT", {
|
|
3103
|
+
operation: "copy",
|
|
3104
|
+
id: ev.id,
|
|
3105
|
+
target: ev.target,
|
|
3106
|
+
mode: ev.mode,
|
|
3107
|
+
index: ev.index,
|
|
3108
|
+
lazy: !!ev.lazy
|
|
3109
|
+
})
|
|
3102
3110
|
},
|
|
3103
3111
|
"move-task": {
|
|
3104
3112
|
handler: async (ev) => {
|
|
@@ -3113,11 +3121,8 @@ class RestDataProvider extends Rest {
|
|
|
3113
3121
|
},
|
|
3114
3122
|
"add-link": {
|
|
3115
3123
|
ignoreID: true,
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
ev.link.target = this.correctID(ev.link.target);
|
|
3119
|
-
return this.sendBatch(`links`, "POST", ev.link);
|
|
3120
|
-
}
|
|
3124
|
+
kind: LinkID,
|
|
3125
|
+
handler: async (ev) => this.sendBatch(`links`, "POST", ev.link)
|
|
3121
3126
|
},
|
|
3122
3127
|
"update-link": {
|
|
3123
3128
|
handler: async (ev) => this.sendBatch(`links/${ev.id}`, "PUT", ev.link)
|
|
@@ -3127,26 +3132,22 @@ class RestDataProvider extends Rest {
|
|
|
3127
3132
|
},
|
|
3128
3133
|
"add-assignment": {
|
|
3129
3134
|
ignoreID: true,
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3135
|
+
kind: AssignmentID,
|
|
3136
|
+
handler: async (ev) => this.sendBatch(
|
|
3137
|
+
`assignments`,
|
|
3138
|
+
"POST",
|
|
3139
|
+
ev.assignment
|
|
3140
|
+
)
|
|
3134
3141
|
},
|
|
3135
3142
|
"delete-assignment": {
|
|
3136
|
-
handler: async (ev) => {
|
|
3137
|
-
this.sendBatch(`assignments/${ev.id}`, "DELETE");
|
|
3138
|
-
}
|
|
3143
|
+
handler: async (ev) => this.sendBatch(`assignments/${ev.id}`, "DELETE")
|
|
3139
3144
|
},
|
|
3140
3145
|
"update-assignment": {
|
|
3141
|
-
handler: async (ev) =>
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
"PUT",
|
|
3147
|
-
ev.assignment
|
|
3148
|
-
);
|
|
3149
|
-
}
|
|
3146
|
+
handler: async (ev) => this.sendBatch(
|
|
3147
|
+
`assignments/${ev.id}`,
|
|
3148
|
+
"PUT",
|
|
3149
|
+
ev.assignment
|
|
3150
|
+
)
|
|
3150
3151
|
}
|
|
3151
3152
|
};
|
|
3152
3153
|
}
|
|
@@ -3163,7 +3164,7 @@ class RestDataProvider extends Rest {
|
|
|
3163
3164
|
) : void 0
|
|
3164
3165
|
]);
|
|
3165
3166
|
const tasks = this.parseDates(tasksResponse).map((t) => {
|
|
3166
|
-
if (t.parent
|
|
3167
|
+
if (t.parent === id) t.parent = tempId;
|
|
3167
3168
|
return t;
|
|
3168
3169
|
});
|
|
3169
3170
|
const result = { tasks, links };
|
|
@@ -3173,10 +3174,16 @@ class RestDataProvider extends Rest {
|
|
|
3173
3174
|
}
|
|
3174
3175
|
parseDates(data) {
|
|
3175
3176
|
data.forEach((item) => {
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
if (item.
|
|
3177
|
+
DATE_KEYS.forEach((key) => {
|
|
3178
|
+
if (item[key]) item[key] = new Date(item[key]);
|
|
3179
|
+
});
|
|
3180
|
+
if (item.constraint?.date)
|
|
3181
|
+
item.constraint.date = new Date(item.constraint.date);
|
|
3182
|
+
item.segments?.forEach((segment) => {
|
|
3183
|
+
DATE_KEYS.forEach((key) => {
|
|
3184
|
+
if (segment[key]) segment[key] = new Date(segment[key]);
|
|
3185
|
+
});
|
|
3186
|
+
});
|
|
3180
3187
|
});
|
|
3181
3188
|
return data;
|
|
3182
3189
|
}
|
|
@@ -3190,46 +3197,72 @@ class RestDataProvider extends Rest {
|
|
|
3190
3197
|
return this.send(url, method, data, customHeaders);
|
|
3191
3198
|
}
|
|
3192
3199
|
}
|
|
3200
|
+
resolveId(id, kind) {
|
|
3201
|
+
return this.getQueue().resolve(id, kind);
|
|
3202
|
+
}
|
|
3193
3203
|
async sendBatchRequest(url, method, data, customHeaders) {
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
url: req.url,
|
|
3204
|
-
method: req.method,
|
|
3205
|
-
data: {
|
|
3206
|
-
...req.data
|
|
3207
|
-
}
|
|
3208
|
-
};
|
|
3209
|
-
});
|
|
3210
|
-
this.changeQueue = [];
|
|
3211
|
-
const result = await this.send(
|
|
3212
|
-
this._batchUrl,
|
|
3213
|
-
"POST",
|
|
3214
|
-
batchData
|
|
3215
|
-
);
|
|
3216
|
-
resolve(result);
|
|
3217
|
-
} else {
|
|
3218
|
-
this.changeQueue = [];
|
|
3219
|
-
const result = await this.send(
|
|
3220
|
-
url,
|
|
3221
|
-
method,
|
|
3222
|
-
data,
|
|
3223
|
-
customHeaders
|
|
3224
|
-
);
|
|
3225
|
-
resolve(result);
|
|
3226
|
-
}
|
|
3227
|
-
}, 10);
|
|
3204
|
+
const request = new Promise((resolve, reject) => {
|
|
3205
|
+
this.changeQueue.push({
|
|
3206
|
+
url,
|
|
3207
|
+
method,
|
|
3208
|
+
data,
|
|
3209
|
+
customHeaders,
|
|
3210
|
+
resolve,
|
|
3211
|
+
reject
|
|
3212
|
+
});
|
|
3228
3213
|
});
|
|
3214
|
+
if (this.flushTimeout) clearTimeout(this.flushTimeout);
|
|
3215
|
+
this.flushTimeout = setTimeout(() => this.flushBatch(), 10);
|
|
3216
|
+
return request;
|
|
3217
|
+
}
|
|
3218
|
+
async flushBatch() {
|
|
3219
|
+
const queue = this.changeQueue;
|
|
3220
|
+
this.changeQueue = [];
|
|
3221
|
+
this.flushTimeout = null;
|
|
3222
|
+
if (!queue.length) return;
|
|
3223
|
+
if (queue.length === 1) {
|
|
3224
|
+
const [req] = queue;
|
|
3225
|
+
try {
|
|
3226
|
+
req.resolve(
|
|
3227
|
+
await this.send(
|
|
3228
|
+
req.url,
|
|
3229
|
+
req.method,
|
|
3230
|
+
req.data,
|
|
3231
|
+
req.customHeaders
|
|
3232
|
+
)
|
|
3233
|
+
);
|
|
3234
|
+
} catch (error) {
|
|
3235
|
+
req.reject(error);
|
|
3236
|
+
}
|
|
3237
|
+
return;
|
|
3238
|
+
}
|
|
3239
|
+
const batchData = queue.map((req) => ({
|
|
3240
|
+
url: req.url,
|
|
3241
|
+
method: req.method,
|
|
3242
|
+
data: req.data ? { ...req.data } : void 0,
|
|
3243
|
+
customHeaders: req.customHeaders
|
|
3244
|
+
}));
|
|
3245
|
+
try {
|
|
3246
|
+
const result = await this.send(
|
|
3247
|
+
this._batchUrl,
|
|
3248
|
+
"POST",
|
|
3249
|
+
batchData
|
|
3250
|
+
);
|
|
3251
|
+
if (!Array.isArray(result))
|
|
3252
|
+
throw new Error("batch endpoint did not return a list");
|
|
3253
|
+
queue.forEach((req, i) => {
|
|
3254
|
+
const response = result[i];
|
|
3255
|
+
if (response?.error) req.reject(new Error(response.error));
|
|
3256
|
+
else req.resolve(response);
|
|
3257
|
+
});
|
|
3258
|
+
} catch (error) {
|
|
3259
|
+
queue.forEach((req) => req.reject(error));
|
|
3260
|
+
}
|
|
3229
3261
|
}
|
|
3230
3262
|
async send(url, method, data, customHeaders = {}) {
|
|
3231
3263
|
const headers = {
|
|
3232
3264
|
"Content-Type": "application/json",
|
|
3265
|
+
...this.headers,
|
|
3233
3266
|
...customHeaders
|
|
3234
3267
|
};
|
|
3235
3268
|
const req = {
|
|
@@ -3239,39 +3272,408 @@ class RestDataProvider extends Rest {
|
|
|
3239
3272
|
if (data) {
|
|
3240
3273
|
req.body = this.getPayload(data);
|
|
3241
3274
|
}
|
|
3242
|
-
return fetch(`${this._url}/${url}`, req).then((res) => {
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3275
|
+
return fetch(`${this._url}/${url}`, req).then(async (res) => {
|
|
3276
|
+
if (!res.ok) {
|
|
3277
|
+
const text = await res.text().catch(() => "");
|
|
3278
|
+
throw new Error(
|
|
3279
|
+
`request failed with status ${res.status}${text ? `: ${text}` : ""}`
|
|
3280
|
+
);
|
|
3281
|
+
}
|
|
3282
|
+
return res.json();
|
|
3246
3283
|
});
|
|
3247
3284
|
}
|
|
3248
3285
|
getPayload(obj) {
|
|
3249
|
-
|
|
3250
|
-
return JSON.stringify(transformedObj);
|
|
3286
|
+
return JSON.stringify(this.transformDates(obj));
|
|
3251
3287
|
}
|
|
3252
3288
|
transformDates = (item) => {
|
|
3253
|
-
if (
|
|
3254
|
-
|
|
3255
|
-
}
|
|
3289
|
+
if (item instanceof Date) return this.formatDate(item);
|
|
3290
|
+
if (Array.isArray(item)) return item.map(this.transformDates);
|
|
3256
3291
|
if (typeof item === "object" && item !== null) {
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
item[key] = this.formatDate(value);
|
|
3261
|
-
} else if (typeof value === "object") {
|
|
3262
|
-
item[key] = this.transformDates(value);
|
|
3263
|
-
}
|
|
3264
|
-
}
|
|
3292
|
+
const copy = {};
|
|
3293
|
+
for (const key in item) copy[key] = this.transformDates(item[key]);
|
|
3294
|
+
return copy;
|
|
3265
3295
|
}
|
|
3266
3296
|
return item;
|
|
3267
3297
|
};
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3298
|
+
}
|
|
3299
|
+
|
|
3300
|
+
const RETRY_MIN = 1e3;
|
|
3301
|
+
const RETRY_MAX = 3e4;
|
|
3302
|
+
class EventStream {
|
|
3303
|
+
config;
|
|
3304
|
+
eventSource = null;
|
|
3305
|
+
connectedSignal = null;
|
|
3306
|
+
// Resolves when eventSource opens
|
|
3307
|
+
wasOpen = false;
|
|
3308
|
+
hasMissedEvents = false;
|
|
3309
|
+
isOnline = false;
|
|
3310
|
+
rejectConnect = null;
|
|
3311
|
+
retryTimer = null;
|
|
3312
|
+
retryDelayMs = RETRY_MIN;
|
|
3313
|
+
isStopped = false;
|
|
3314
|
+
constructor(config) {
|
|
3315
|
+
this.config = config;
|
|
3316
|
+
}
|
|
3317
|
+
connect() {
|
|
3318
|
+
this.isStopped = false;
|
|
3319
|
+
if (this.connectedSignal) return this.connectedSignal;
|
|
3320
|
+
this.connectedSignal = new Promise((resolve, reject) => {
|
|
3321
|
+
this.rejectConnect = reject;
|
|
3322
|
+
this.eventSource = new EventSource(this.getURL(), {
|
|
3323
|
+
withCredentials: this.config.withCredentials
|
|
3324
|
+
});
|
|
3325
|
+
const source = this.eventSource;
|
|
3326
|
+
source.onopen = () => {
|
|
3327
|
+
if (this.eventSource !== source) return;
|
|
3328
|
+
const resync = this.wasOpen || this.hasMissedEvents;
|
|
3329
|
+
this.wasOpen = true;
|
|
3330
|
+
this.hasMissedEvents = false;
|
|
3331
|
+
this.retryDelayMs = RETRY_MIN;
|
|
3332
|
+
this.rejectConnect = null;
|
|
3333
|
+
resolve();
|
|
3334
|
+
this.notify(true);
|
|
3335
|
+
if (resync) this.config.onResync?.();
|
|
3336
|
+
};
|
|
3337
|
+
source.onerror = () => {
|
|
3338
|
+
if (this.eventSource !== source) return;
|
|
3339
|
+
this.notify(false);
|
|
3340
|
+
if (source.readyState !== EventSource.CLOSED) return;
|
|
3341
|
+
this.close();
|
|
3342
|
+
this.hasMissedEvents = true;
|
|
3343
|
+
this.rejectConnect?.(
|
|
3344
|
+
new Error("could not open the event stream")
|
|
3345
|
+
);
|
|
3346
|
+
this.rejectConnect = null;
|
|
3347
|
+
this.scheduleRetry();
|
|
3348
|
+
};
|
|
3349
|
+
source.onmessage = (message) => {
|
|
3350
|
+
if (this.eventSource !== source) return;
|
|
3351
|
+
this.config.onMessage?.(message.data);
|
|
3352
|
+
};
|
|
3353
|
+
});
|
|
3354
|
+
return this.connectedSignal;
|
|
3355
|
+
}
|
|
3356
|
+
disconnect() {
|
|
3357
|
+
this.isStopped = true;
|
|
3358
|
+
this.clearRetry();
|
|
3359
|
+
this.close();
|
|
3360
|
+
this.rejectConnect?.(
|
|
3361
|
+
new Error("disconnected before the stream was open")
|
|
3362
|
+
);
|
|
3363
|
+
this.rejectConnect = null;
|
|
3364
|
+
this.notify(false);
|
|
3365
|
+
}
|
|
3366
|
+
scheduleRetry() {
|
|
3367
|
+
if (this.isStopped || this.retryTimer) return;
|
|
3368
|
+
const delayMs = this.retryDelayMs * (0.5 + Math.random() / 2);
|
|
3369
|
+
this.retryDelayMs = Math.min(this.retryDelayMs * 2, RETRY_MAX);
|
|
3370
|
+
this.retryTimer = setTimeout(() => {
|
|
3371
|
+
this.retryTimer = null;
|
|
3372
|
+
if (this.isStopped) return;
|
|
3373
|
+
this.connect().catch(() => {
|
|
3374
|
+
});
|
|
3375
|
+
}, delayMs);
|
|
3376
|
+
}
|
|
3377
|
+
clearRetry() {
|
|
3378
|
+
if (this.retryTimer) clearTimeout(this.retryTimer);
|
|
3379
|
+
this.retryTimer = null;
|
|
3380
|
+
this.retryDelayMs = RETRY_MIN;
|
|
3381
|
+
}
|
|
3382
|
+
close() {
|
|
3383
|
+
this.eventSource?.close();
|
|
3384
|
+
this.eventSource = null;
|
|
3385
|
+
this.connectedSignal = null;
|
|
3386
|
+
}
|
|
3387
|
+
notify(isOnline) {
|
|
3388
|
+
if (this.isOnline === isOnline) return;
|
|
3389
|
+
this.isOnline = isOnline;
|
|
3390
|
+
try {
|
|
3391
|
+
this.config.onConnectionChange?.(isOnline);
|
|
3392
|
+
} catch (error) {
|
|
3393
|
+
console.error(error);
|
|
3394
|
+
}
|
|
3395
|
+
}
|
|
3396
|
+
getURL() {
|
|
3397
|
+
const { url, params } = this.config;
|
|
3398
|
+
const query = new URLSearchParams(
|
|
3399
|
+
typeof params === "function" ? params() : params
|
|
3400
|
+
).toString();
|
|
3401
|
+
if (!query) return url;
|
|
3402
|
+
return url + (url.includes("?") ? "&" : "?") + query;
|
|
3403
|
+
}
|
|
3404
|
+
}
|
|
3405
|
+
|
|
3406
|
+
function generateUUID() {
|
|
3407
|
+
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
3408
|
+
return crypto.randomUUID();
|
|
3409
|
+
} else {
|
|
3410
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (char) => {
|
|
3411
|
+
const num = Math.random() * 16 | 0;
|
|
3412
|
+
const value = char === "x" ? num : num & 3 | 8;
|
|
3413
|
+
return value.toString(16);
|
|
3414
|
+
});
|
|
3415
|
+
}
|
|
3416
|
+
}
|
|
3417
|
+
|
|
3418
|
+
const PARAMS = {
|
|
3419
|
+
eventSource: "remote"
|
|
3420
|
+
};
|
|
3421
|
+
class RemoteEvents {
|
|
3422
|
+
// the host sends it as X-Client-Id, so the server skips this client's own echo
|
|
3423
|
+
clientId = generateUUID();
|
|
3424
|
+
provider;
|
|
3425
|
+
api;
|
|
3426
|
+
eventStream;
|
|
3427
|
+
constructor(provider, config) {
|
|
3428
|
+
this.provider = provider;
|
|
3429
|
+
const { params } = config;
|
|
3430
|
+
this.eventStream = new EventStream({
|
|
3431
|
+
...config,
|
|
3432
|
+
onMessage: config.onMessage ?? ((msg) => this.processMessage(msg)),
|
|
3433
|
+
params: () => ({
|
|
3434
|
+
...typeof params === "function" ? params() : params,
|
|
3435
|
+
clientId: this.clientId
|
|
3436
|
+
})
|
|
3437
|
+
});
|
|
3438
|
+
}
|
|
3439
|
+
connect(api) {
|
|
3440
|
+
this.api = api;
|
|
3441
|
+
return this.eventStream.connect();
|
|
3442
|
+
}
|
|
3443
|
+
disconnect() {
|
|
3444
|
+
this.eventStream.disconnect();
|
|
3445
|
+
}
|
|
3446
|
+
processMessage(message) {
|
|
3447
|
+
const event = this.parseMessage(message);
|
|
3448
|
+
if (!event || !this.api) return;
|
|
3449
|
+
try {
|
|
3450
|
+
this.applyEvent(event);
|
|
3451
|
+
} catch (error) {
|
|
3452
|
+
console.error("failed to apply a remote event", error);
|
|
3453
|
+
}
|
|
3454
|
+
}
|
|
3455
|
+
applyEvent(event) {
|
|
3456
|
+
switch (event.type) {
|
|
3457
|
+
case "task":
|
|
3458
|
+
return this.applyEventTask(event);
|
|
3459
|
+
case "link":
|
|
3460
|
+
return this.applyEventLink(event);
|
|
3461
|
+
case "assignment":
|
|
3462
|
+
return this.applyEventAssignment(event);
|
|
3463
|
+
}
|
|
3464
|
+
}
|
|
3465
|
+
// ---------- Task events ----------
|
|
3466
|
+
applyEventTask(event) {
|
|
3467
|
+
const id = this.resolveRemoteId(event.id, TaskID);
|
|
3468
|
+
let task;
|
|
3469
|
+
if (event.data) {
|
|
3470
|
+
const data = [event.data];
|
|
3471
|
+
task = (this.provider.parseDates?.(data) ?? data)[0];
|
|
3472
|
+
}
|
|
3473
|
+
const operation = task?.operation;
|
|
3474
|
+
if (task) {
|
|
3475
|
+
task.id = id;
|
|
3476
|
+
task.parent = this.resolveRemoteId(task.parent, TaskID);
|
|
3477
|
+
if ("target" in task)
|
|
3478
|
+
task.target = this.resolveRemoteId(task.target, TaskID);
|
|
3479
|
+
delete task.open;
|
|
3480
|
+
delete task.operation;
|
|
3481
|
+
}
|
|
3482
|
+
switch (event.action) {
|
|
3483
|
+
case "add":
|
|
3484
|
+
if (this.isTaskLoaded(id)) return;
|
|
3485
|
+
return this.applyEventTaskAdd(id, task);
|
|
3486
|
+
case "update":
|
|
3487
|
+
return this.applyEventTaskUpdate(id, task, operation);
|
|
3488
|
+
case "delete":
|
|
3489
|
+
return this.applyEventTaskDelete(id);
|
|
3490
|
+
}
|
|
3491
|
+
}
|
|
3492
|
+
applyEventTaskAdd(id, task) {
|
|
3493
|
+
if (!task) return;
|
|
3494
|
+
if (!this.canAcceptChild(task?.parent)) return;
|
|
3495
|
+
const hasTarget = this.isTaskLoaded(task?.target);
|
|
3496
|
+
const addTask = { ...task };
|
|
3497
|
+
delete addTask.index;
|
|
3498
|
+
delete addTask.target;
|
|
3499
|
+
delete addTask.mode;
|
|
3500
|
+
this.api.exec("add-task", {
|
|
3501
|
+
id,
|
|
3502
|
+
task: addTask,
|
|
3503
|
+
target: hasTarget ? task?.target : void 0,
|
|
3504
|
+
mode: hasTarget ? task?.mode : void 0,
|
|
3505
|
+
index: task?.index,
|
|
3506
|
+
...PARAMS
|
|
3507
|
+
});
|
|
3508
|
+
}
|
|
3509
|
+
applyEventTaskUpdate(id, task, operation) {
|
|
3510
|
+
if (!task) return;
|
|
3511
|
+
if (!this.isTaskLoaded(id)) {
|
|
3512
|
+
return this.applyEventTaskAdd(id, task);
|
|
3513
|
+
}
|
|
3514
|
+
delete task.lazy;
|
|
3515
|
+
if (!operation) {
|
|
3516
|
+
this.api.exec("update-task", {
|
|
3517
|
+
id,
|
|
3518
|
+
task,
|
|
3519
|
+
...PARAMS
|
|
3520
|
+
});
|
|
3521
|
+
} else if (operation === "move") {
|
|
3522
|
+
if (!this.canAcceptChild(task?.parent)) {
|
|
3523
|
+
return this.applyEventTaskDelete(id);
|
|
3524
|
+
}
|
|
3525
|
+
const hasTarget = this.isTaskLoaded(task?.target);
|
|
3526
|
+
this.api.exec("move-task", {
|
|
3527
|
+
id,
|
|
3528
|
+
target: hasTarget ? task?.target : task?.parent,
|
|
3529
|
+
mode: hasTarget ? task?.mode : "child",
|
|
3530
|
+
...PARAMS
|
|
3531
|
+
});
|
|
3532
|
+
}
|
|
3533
|
+
}
|
|
3534
|
+
applyEventTaskDelete(id) {
|
|
3535
|
+
if (!this.isTaskLoaded(id)) return;
|
|
3536
|
+
this.api.exec("delete-task", {
|
|
3537
|
+
id,
|
|
3538
|
+
...PARAMS
|
|
3539
|
+
});
|
|
3540
|
+
}
|
|
3541
|
+
// ---------- Link events ----------
|
|
3542
|
+
applyEventLink(event) {
|
|
3543
|
+
const id = this.resolveRemoteId(event.id, LinkID);
|
|
3544
|
+
const link = event.data ? event.data : void 0;
|
|
3545
|
+
if (link) {
|
|
3546
|
+
link.id = id;
|
|
3547
|
+
link.source = this.resolveRemoteId(link.source, TaskID);
|
|
3548
|
+
link.target = this.resolveRemoteId(link.target, TaskID);
|
|
3549
|
+
}
|
|
3550
|
+
switch (event.action) {
|
|
3551
|
+
case "add":
|
|
3552
|
+
return this.applyEventLinkAdd(id, link);
|
|
3553
|
+
case "update":
|
|
3554
|
+
return this.applyEventLinkUpdate(id, link);
|
|
3555
|
+
case "delete":
|
|
3556
|
+
return this.applyEventLinkDelete(id);
|
|
3557
|
+
}
|
|
3558
|
+
}
|
|
3559
|
+
applyEventLinkAdd(id, link) {
|
|
3560
|
+
if (!link) return;
|
|
3561
|
+
if (this.isLinkLoaded(id)) return;
|
|
3562
|
+
if (!this.isTaskLoaded(link.source) || !this.isTaskLoaded(link.target))
|
|
3563
|
+
return;
|
|
3564
|
+
this.api.exec("add-link", {
|
|
3565
|
+
id,
|
|
3566
|
+
link,
|
|
3567
|
+
...PARAMS
|
|
3568
|
+
});
|
|
3569
|
+
}
|
|
3570
|
+
applyEventLinkUpdate(id, link) {
|
|
3571
|
+
if (!link) return;
|
|
3572
|
+
if (!this.isLinkLoaded(id)) return;
|
|
3573
|
+
this.api.exec("update-link", {
|
|
3574
|
+
id,
|
|
3575
|
+
link,
|
|
3576
|
+
...PARAMS
|
|
3577
|
+
});
|
|
3578
|
+
}
|
|
3579
|
+
applyEventLinkDelete(id) {
|
|
3580
|
+
if (!this.isLinkLoaded(id)) return;
|
|
3581
|
+
this.api.exec("delete-link", {
|
|
3582
|
+
id,
|
|
3583
|
+
...PARAMS
|
|
3584
|
+
});
|
|
3585
|
+
}
|
|
3586
|
+
// ---------- Assignment events ----------
|
|
3587
|
+
applyEventAssignment(event) {
|
|
3588
|
+
const id = this.resolveRemoteId(event.id, AssignmentID);
|
|
3589
|
+
const assignment = event.data ? event.data : void 0;
|
|
3590
|
+
if (assignment) {
|
|
3591
|
+
assignment.id = id;
|
|
3592
|
+
assignment.task = this.resolveRemoteId(assignment.task, TaskID);
|
|
3593
|
+
}
|
|
3594
|
+
switch (event.action) {
|
|
3595
|
+
case "add":
|
|
3596
|
+
return this.applyEventAssignmentAdd(id, assignment);
|
|
3597
|
+
case "update":
|
|
3598
|
+
return this.applyEventAssignmentUpdate(id, assignment);
|
|
3599
|
+
case "delete":
|
|
3600
|
+
return this.applyEventAssignmentDelete(id);
|
|
3601
|
+
}
|
|
3602
|
+
}
|
|
3603
|
+
applyEventAssignmentAdd(id, assignment) {
|
|
3604
|
+
if (!assignment) return;
|
|
3605
|
+
if (this.isAssignmentLoaded(id)) return;
|
|
3606
|
+
if (!this.isTaskLoaded(assignment.task)) return;
|
|
3607
|
+
this.api.exec("add-assignment", {
|
|
3608
|
+
id,
|
|
3609
|
+
assignment,
|
|
3610
|
+
...PARAMS
|
|
3611
|
+
});
|
|
3612
|
+
}
|
|
3613
|
+
applyEventAssignmentUpdate(id, assignment) {
|
|
3614
|
+
if (!assignment) return;
|
|
3615
|
+
if (!this.isAssignmentLoaded(id)) return;
|
|
3616
|
+
this.api.exec("update-assignment", {
|
|
3617
|
+
id,
|
|
3618
|
+
assignment,
|
|
3619
|
+
...PARAMS
|
|
3620
|
+
});
|
|
3621
|
+
}
|
|
3622
|
+
applyEventAssignmentDelete(id) {
|
|
3623
|
+
if (!this.isAssignmentLoaded(id)) return;
|
|
3624
|
+
this.api.exec("delete-assignment", {
|
|
3625
|
+
id,
|
|
3626
|
+
...PARAMS
|
|
3627
|
+
});
|
|
3628
|
+
}
|
|
3629
|
+
// ---------- Helpers ----------
|
|
3630
|
+
resolveRemoteId(id, kind) {
|
|
3631
|
+
if (id === null || typeof id === "undefined") return id;
|
|
3632
|
+
const localId = this.provider.resolveId(id, kind) ?? id;
|
|
3633
|
+
if (localId === id) return id;
|
|
3634
|
+
return this.doesItemExist(localId, kind) ? localId : id;
|
|
3635
|
+
}
|
|
3636
|
+
doesItemExist(id, kind) {
|
|
3637
|
+
switch (kind) {
|
|
3638
|
+
case TaskID:
|
|
3639
|
+
return !!this.api.getTask(id);
|
|
3640
|
+
case LinkID:
|
|
3641
|
+
return this.isLinkLoaded(id);
|
|
3642
|
+
case AssignmentID:
|
|
3643
|
+
return this.isAssignmentLoaded(id);
|
|
3644
|
+
default:
|
|
3645
|
+
return true;
|
|
3646
|
+
}
|
|
3647
|
+
}
|
|
3648
|
+
isTaskLoaded(id) {
|
|
3649
|
+
return !id || !!this.api.getTask(id);
|
|
3650
|
+
}
|
|
3651
|
+
canAcceptChild(id) {
|
|
3652
|
+
if (!id) return true;
|
|
3653
|
+
const parent = this.api.getTask(id);
|
|
3654
|
+
return !!parent && !parent.lazy;
|
|
3655
|
+
}
|
|
3656
|
+
isLinkLoaded(id) {
|
|
3657
|
+
return !!this.api.getState().links.byId(id);
|
|
3658
|
+
}
|
|
3659
|
+
isAssignmentLoaded(id) {
|
|
3660
|
+
return !!this.api.getState().assignments?.byId(id);
|
|
3661
|
+
}
|
|
3662
|
+
// ---------- Parsing ----------
|
|
3663
|
+
parseMessage(message) {
|
|
3664
|
+
const clientId = this.clientId;
|
|
3665
|
+
let data;
|
|
3666
|
+
try {
|
|
3667
|
+
data = JSON.parse(message);
|
|
3668
|
+
} catch {
|
|
3669
|
+
return null;
|
|
3670
|
+
}
|
|
3671
|
+
if (data === null || typeof data !== "object" || !("from" in data) || data.from === clientId || !("type" in data) || typeof data.type !== "string" || !("action" in data) || typeof data.action !== "string" || !("id" in data)) {
|
|
3672
|
+
return null;
|
|
3673
|
+
}
|
|
3674
|
+
return data;
|
|
3273
3675
|
}
|
|
3274
3676
|
}
|
|
3275
3677
|
|
|
3276
|
-
export { RestDataProvider };
|
|
3678
|
+
export { AssignmentID, EventStream, LinkID, RemoteEvents, RestDataProvider, TaskID };
|
|
3277
3679
|
//# sourceMappingURL=index.js.map
|