@univerjs-pro/collaboration-client 1.0.4 → 1.2.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/README.md +2 -0
- package/lib/cjs/facade.js +1 -1
- package/lib/cjs/index.js +1 -1
- package/lib/cjs/locale/ar-SA.js +1 -1
- package/lib/cjs/locale/ca-ES.js +1 -1
- package/lib/cjs/locale/de-DE.js +1 -1
- package/lib/cjs/locale/en-US.js +1 -1
- package/lib/cjs/locale/es-ES.js +1 -1
- package/lib/cjs/locale/fa-IR.js +1 -1
- package/lib/cjs/locale/fr-FR.js +1 -1
- package/lib/cjs/locale/id-ID.js +1 -1
- package/lib/cjs/locale/it-IT.js +1 -1
- package/lib/cjs/locale/ja-JP.js +1 -1
- package/lib/cjs/locale/ko-KR.js +1 -1
- package/lib/cjs/locale/pl-PL.js +1 -1
- package/lib/cjs/locale/pt-BR.js +1 -1
- package/lib/cjs/locale/ru-RU.js +1 -1
- package/lib/cjs/locale/sk-SK.js +1 -1
- package/lib/cjs/locale/vi-VN.js +1 -1
- package/lib/cjs/locale/zh-CN.js +1 -1
- package/lib/cjs/locale/zh-HK.js +1 -1
- package/lib/cjs/locale/zh-TW.js +1 -1
- package/lib/es/facade.js +1 -1
- package/lib/es/index.js +1 -1
- package/lib/es/locale/ar-SA.js +1 -1
- package/lib/es/locale/ca-ES.js +1 -1
- package/lib/es/locale/de-DE.js +1 -1
- package/lib/es/locale/en-US.js +1 -1
- package/lib/es/locale/es-ES.js +1 -1
- package/lib/es/locale/fa-IR.js +1 -1
- package/lib/es/locale/fr-FR.js +1 -1
- package/lib/es/locale/id-ID.js +1 -1
- package/lib/es/locale/it-IT.js +1 -1
- package/lib/es/locale/ja-JP.js +1 -1
- package/lib/es/locale/ko-KR.js +1 -1
- package/lib/es/locale/pl-PL.js +1 -1
- package/lib/es/locale/pt-BR.js +1 -1
- package/lib/es/locale/ru-RU.js +1 -1
- package/lib/es/locale/sk-SK.js +1 -1
- package/lib/es/locale/vi-VN.js +1 -1
- package/lib/es/locale/zh-CN.js +1 -1
- package/lib/es/locale/zh-HK.js +1 -1
- package/lib/es/locale/zh-TW.js +1 -1
- package/lib/facade.js +1 -1
- package/lib/index.js +1 -1
- package/lib/locale/ar-SA.js +1 -1
- package/lib/locale/ca-ES.js +1 -1
- package/lib/locale/de-DE.js +1 -1
- package/lib/locale/en-US.js +1 -1
- package/lib/locale/es-ES.js +1 -1
- package/lib/locale/fa-IR.js +1 -1
- package/lib/locale/fr-FR.js +1 -1
- package/lib/locale/id-ID.js +1 -1
- package/lib/locale/it-IT.js +1 -1
- package/lib/locale/ja-JP.js +1 -1
- package/lib/locale/ko-KR.js +1 -1
- package/lib/locale/pl-PL.js +1 -1
- package/lib/locale/pt-BR.js +1 -1
- package/lib/locale/ru-RU.js +1 -1
- package/lib/locale/sk-SK.js +1 -1
- package/lib/locale/vi-VN.js +1 -1
- package/lib/locale/zh-CN.js +1 -1
- package/lib/locale/zh-HK.js +1 -1
- package/lib/locale/zh-TW.js +1 -1
- package/lib/types/config/config.d.ts +19 -0
- package/lib/types/controllers/collaboration/changeset-submitter.d.ts +132 -0
- package/lib/types/controllers/collaboration/collaboration-entity.d.ts +28 -1
- package/lib/types/controllers/collaboration/collaboration-state.d.ts +175 -43
- package/lib/types/controllers/collaboration/collaboration.controller.d.ts +7 -0
- package/lib/types/controllers/collaboration/utils/changeset-utils.d.ts +7 -3
- package/lib/types/facade/f-collaboration.d.ts +20 -4
- package/lib/types/facade/f-event.d.ts +87 -2
- package/lib/types/index.d.ts +5 -3
- package/lib/types/services/collaboration-session/collaboration-session.d.ts +4 -8
- package/lib/types/services/collaboration-session/collaboration-session.service.d.ts +4 -4
- package/lib/types/services/collaboration-ui-event/collaboration-ui-event.d.ts +9 -0
- package/lib/types/services/data-loader/data-loader.service.d.ts +9 -2
- package/lib/types/services/local-cache/local-cache.service.d.ts +11 -6
- package/lib/types/services/socket/collaboration-socket.service.d.ts +10 -2
- package/lib/types/services/undoredo/collaborative-undoredo.service.d.ts +5 -0
- package/lib/umd/facade.js +1 -1
- package/lib/umd/index.js +1 -1
- package/lib/umd/locale/ar-SA.js +1 -1
- package/lib/umd/locale/ca-ES.js +1 -1
- package/lib/umd/locale/de-DE.js +1 -1
- package/lib/umd/locale/en-US.js +1 -1
- package/lib/umd/locale/es-ES.js +1 -1
- package/lib/umd/locale/fa-IR.js +1 -1
- package/lib/umd/locale/fr-FR.js +1 -1
- package/lib/umd/locale/id-ID.js +1 -1
- package/lib/umd/locale/it-IT.js +1 -1
- package/lib/umd/locale/ja-JP.js +1 -1
- package/lib/umd/locale/ko-KR.js +1 -1
- package/lib/umd/locale/pl-PL.js +1 -1
- package/lib/umd/locale/pt-BR.js +1 -1
- package/lib/umd/locale/ru-RU.js +1 -1
- package/lib/umd/locale/sk-SK.js +1 -1
- package/lib/umd/locale/vi-VN.js +1 -1
- package/lib/umd/locale/zh-CN.js +1 -1
- package/lib/umd/locale/zh-HK.js +1 -1
- package/lib/umd/locale/zh-TW.js +1 -1
- package/package.json +16 -16
|
@@ -28,7 +28,26 @@ export interface IUniverCollaborationClientConfig {
|
|
|
28
28
|
* Override internal modules with custom implementations.
|
|
29
29
|
*/
|
|
30
30
|
override?: DependencyOverride;
|
|
31
|
+
/**
|
|
32
|
+
* How long local edits are batched before they are sent as one changeset, in milliseconds. Edits made within
|
|
33
|
+
* this window after the first one go into the same changeset. This is a batching delay, not a network timeout;
|
|
34
|
+
* for the acknowledgement timeout see `changesetAckTimeout`.
|
|
35
|
+
* @default 2000
|
|
36
|
+
*/
|
|
31
37
|
sendChangesetTimeout?: number;
|
|
38
|
+
/**
|
|
39
|
+
* How long to wait for the server's acknowledgement of a changeset before sending it again, in milliseconds.
|
|
40
|
+
* Later resends wait twice as long each time, up to `maxChangesetAckTimeout`. The request that submits the
|
|
41
|
+
* changeset is given the same timeout.
|
|
42
|
+
* @default 20000
|
|
43
|
+
*/
|
|
44
|
+
changesetAckTimeout?: number;
|
|
45
|
+
/**
|
|
46
|
+
* The longest wait between two resends of an unacknowledged changeset, in milliseconds. Set it equal to
|
|
47
|
+
* `changesetAckTimeout` for a constant interval.
|
|
48
|
+
* @default 120000
|
|
49
|
+
*/
|
|
50
|
+
maxChangesetAckTimeout?: number;
|
|
32
51
|
snapshotServerUrl?: string;
|
|
33
52
|
collabSubmitChangesetUrl?: string;
|
|
34
53
|
collabWebSocketUrl?: string;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import type { IChangeset } from '@univerjs-pro/collaboration';
|
|
2
|
+
import type { ICollaborationSendOptions } from '../../services/socket/collaboration-socket.service';
|
|
3
|
+
/** Why the awaiting changeset was sent again. */
|
|
4
|
+
export type ChangesetResendReason = 'ack_timeout' | 'should_retry' | 'reconnect' | 'request_failed';
|
|
5
|
+
/** Emitted every time an awaiting changeset is sent again. The first send is not an event. */
|
|
6
|
+
export interface IChangesetResendEvent {
|
|
7
|
+
unitID: string;
|
|
8
|
+
reqId: number;
|
|
9
|
+
reason: ChangesetResendReason;
|
|
10
|
+
/** How many times the changeset has been resent, counting this one. */
|
|
11
|
+
attempt: number;
|
|
12
|
+
/** Milliseconds since the changeset was first sent. */
|
|
13
|
+
elapsedMs: number;
|
|
14
|
+
/** Mutations in the awaiting changeset plus pending mutations not yet sent. */
|
|
15
|
+
unsyncedMutationCount: number;
|
|
16
|
+
/** Milliseconds until the next resend if nothing is heard from the server. */
|
|
17
|
+
nextDelayMs: number;
|
|
18
|
+
}
|
|
19
|
+
export type ChangesetResendInfo = Omit<IChangesetResendEvent, 'unitID'>;
|
|
20
|
+
/** The first wait for an acknowledgement before the awaiting changeset is resent. */
|
|
21
|
+
export declare const DEFAULT_ACK_TIMEOUT = 20000;
|
|
22
|
+
/** The longest wait for an acknowledgement between two resends. */
|
|
23
|
+
export declare const MAX_ACK_TIMEOUT = 120000;
|
|
24
|
+
/** The longest backoff between two resends requested by the server or caused by a failed request. */
|
|
25
|
+
export declare const MAX_RETRY_TIMEOUT = 20000;
|
|
26
|
+
/**
|
|
27
|
+
* Hands a changeset to the transport. `options.timeout` is how long the transport may wait for the server to
|
|
28
|
+
* answer: it equals the wait for the acknowledgement, so a request that is still unanswered when the changeset is
|
|
29
|
+
* sent again is given up at the same moment. The returned promise, when there is one, rejects when the request
|
|
30
|
+
* failed; its resolution is not used.
|
|
31
|
+
*/
|
|
32
|
+
export type SendChangeset = (changeset: IChangeset, options: ICollaborationSendOptions) => void | Promise<void>;
|
|
33
|
+
/** What the bound state provides for a resend: the awaiting changeset as it currently is, and the unsynced count. */
|
|
34
|
+
export interface IChangesetSubmitterSource {
|
|
35
|
+
changeset: IChangeset;
|
|
36
|
+
/** Mutations in the awaiting changeset plus pending mutations not yet sent. */
|
|
37
|
+
unsyncedMutationCount: number;
|
|
38
|
+
}
|
|
39
|
+
export interface IChangesetSubmitterOptions {
|
|
40
|
+
/** The first wait for an acknowledgement. Defaults to `DEFAULT_ACK_TIMEOUT`. */
|
|
41
|
+
ackTimeout?: number;
|
|
42
|
+
/** The longest wait between two resends. Defaults to `MAX_ACK_TIMEOUT`, and never less than `ackTimeout`. */
|
|
43
|
+
maxAckTimeout?: number;
|
|
44
|
+
onResend?: (info: ChangesetResendInfo) => void;
|
|
45
|
+
now?: () => number;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Submits one changeset and sends it again until the server acknowledges it.
|
|
49
|
+
*
|
|
50
|
+
* It is created when the changeset is first sent and handed from one state to the next while the
|
|
51
|
+
* changeset is kept, so the pending timer, the intervals and the attempt count survive state transitions.
|
|
52
|
+
* The states only tell it to `submit`, `requestRetry` and `dispose`; everything about delivery (timers, backoff,
|
|
53
|
+
* attempt counting) stays here. There is at most one timer at a time: every trigger replaces the previous one.
|
|
54
|
+
*
|
|
55
|
+
* - After every send, an acknowledgement timeout is armed (`ackTimeout`, then doubled up to `maxAckTimeout`).
|
|
56
|
+
* The request itself is given the same timeout, so an unanswered request is cancelled when the changeset is
|
|
57
|
+
* sent again.
|
|
58
|
+
* - A `changeset_should_retry` from the server replaces the timer with a short backoff (0, 1s, 2s, ... up to
|
|
59
|
+
* `MAX_RETRY_TIMEOUT`). A request that fails for a transient reason (network error, 408, 429, 5xx) does the
|
|
60
|
+
* same; other failures and the request timeout leave the timer alone. The two intervals are counted
|
|
61
|
+
* independently.
|
|
62
|
+
* - A timer that fires while no state is bound (e.g. while missing changesets are fetched) is remembered and
|
|
63
|
+
* runs as soon as the next awaiting state binds.
|
|
64
|
+
*/
|
|
65
|
+
export declare class ChangesetSubmitter {
|
|
66
|
+
private readonly _send;
|
|
67
|
+
private readonly _options;
|
|
68
|
+
private _attempt;
|
|
69
|
+
private readonly _firstSentAt;
|
|
70
|
+
private _ackTimeout;
|
|
71
|
+
private readonly _maxAckTimeout;
|
|
72
|
+
private _retryBackoff;
|
|
73
|
+
private _nextDelay;
|
|
74
|
+
private _timer;
|
|
75
|
+
private _pendingReason;
|
|
76
|
+
private _source;
|
|
77
|
+
/** Identifies the latest send; the outcome of an earlier send is ignored. */
|
|
78
|
+
private _sendToken;
|
|
79
|
+
private _disposed;
|
|
80
|
+
private readonly _now;
|
|
81
|
+
private _reqId;
|
|
82
|
+
constructor(reqId: number, _send: SendChangeset, _options?: IChangesetSubmitterOptions);
|
|
83
|
+
/** The request id of the changeset. It is assigned by the transport on the first send and read back then. */
|
|
84
|
+
get reqId(): number;
|
|
85
|
+
get attempt(): number;
|
|
86
|
+
get nextDelayMs(): number;
|
|
87
|
+
get elapsedMs(): number;
|
|
88
|
+
/**
|
|
89
|
+
* Bind the state that owns the awaiting changeset. `source` returns the changeset as it currently is (it may
|
|
90
|
+
* have been transformed since the last send) and the number of unsynced mutations. A resend that came due
|
|
91
|
+
* while nothing was bound runs now.
|
|
92
|
+
*/
|
|
93
|
+
bind(source: () => IChangesetSubmitterSource): void;
|
|
94
|
+
/** Detach the current state while the changeset is handed to the next one. Timers keep running. */
|
|
95
|
+
unbind(): void;
|
|
96
|
+
/**
|
|
97
|
+
* Send the changeset and wait for its acknowledgement.
|
|
98
|
+
*
|
|
99
|
+
* Without `reason` this is the first send of the changeset, which is not reported as a resend. With `reason`
|
|
100
|
+
* it is a resend triggered outside this submitter (e.g. after a reconnect): it counts as an attempt and
|
|
101
|
+
* notifies the listener.
|
|
102
|
+
*/
|
|
103
|
+
submit(changeset: IChangeset, unsyncedMutationCount: number, reason?: ChangesetResendReason): void;
|
|
104
|
+
/**
|
|
105
|
+
* Wait for the acknowledgement of a send that happened outside this submitter, e.g. a changeset restored
|
|
106
|
+
* from the cache that the entity resends itself.
|
|
107
|
+
*/
|
|
108
|
+
armAckTimeout(): void;
|
|
109
|
+
/** The server asked for the changeset to be resent: replace the current timer with the short backoff. */
|
|
110
|
+
requestRetry(): void;
|
|
111
|
+
/** The changeset was acknowledged, rejected or dropped. */
|
|
112
|
+
dispose(): void;
|
|
113
|
+
private _deliver;
|
|
114
|
+
/**
|
|
115
|
+
* The request did not reach the server. A transient failure brings the next send forward to the short
|
|
116
|
+
* backoff; the acknowledgement timeout keeps its own count. A request that ran into its timeout is ignored:
|
|
117
|
+
* the changeset is being sent again at that very moment. So is the failure of a request that a later send
|
|
118
|
+
* superseded, e.g. when the server asked for a retry before the request was answered.
|
|
119
|
+
*/
|
|
120
|
+
private _onRequestFailed;
|
|
121
|
+
private _schedule;
|
|
122
|
+
private _resend;
|
|
123
|
+
/** Count a resend and move the interval it belongs to; the request timeout follows the new interval. */
|
|
124
|
+
private _countResend;
|
|
125
|
+
private _notifyResend;
|
|
126
|
+
private _clearTimer;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* How long to wait before sending again after a failed request, or `null` when the failure should not bring
|
|
130
|
+
* the next send forward.
|
|
131
|
+
*/
|
|
132
|
+
export declare function getRetryDelay(error: unknown, backoff: number): number | null;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { IDisposable, IMutationInfo, Nullable, UniverInstanceType } from '@univerjs/core';
|
|
2
2
|
import type { Observable } from 'rxjs';
|
|
3
3
|
import type { CollaborationSession } from '../../services/collaboration-session/collaboration-session';
|
|
4
|
-
import type {
|
|
4
|
+
import type { IUnitCacheData } from '../../services/local-cache/local-cache.service';
|
|
5
|
+
import type { IChangesetResendEvent } from './changeset-submitter';
|
|
6
|
+
import type { CollaborationState, ICollaborationStateHandler, IConflictInfo, PrepareTransformSelectionsHandler, SyncEditingCollabCursorHandler, TransformIMEHandler, TransformRemoteChangesetByIMECache, TransformRemoteChangesetByStateCache, TransformSelectionsHandler, TransformStateHandler } from './collaboration-state';
|
|
5
7
|
import { CompressMutationService, RevisionService } from '@univerjs-pro/collaboration';
|
|
6
8
|
import { ICommandService, ILogService, Injector, IPermissionService, IUniverInstanceService, LocaleService, RxDisposable } from '@univerjs/core';
|
|
7
9
|
import { DocStateChangeManagerService } from '@univerjs/docs';
|
|
@@ -11,6 +13,20 @@ import { ILocalCacheService } from '../../services/local-cache/local-cache.servi
|
|
|
11
13
|
import { SheetTransformSelectionsService } from '../../services/range-selection/sheet-transform-selections.service';
|
|
12
14
|
import { ISingleActiveUnitService } from '../../services/single-active-unit/single-active-unit.service';
|
|
13
15
|
import { CollaborationStatus } from './collaboration-state';
|
|
16
|
+
/**
|
|
17
|
+
* Emitted once when a unit enters the conflict state, after its offline cache has been cleared. Carries
|
|
18
|
+
* `IConflictInfo` and the local edits the server has not acknowledged.
|
|
19
|
+
*/
|
|
20
|
+
export interface ICollaborationConflictEvent extends IConflictInfo {
|
|
21
|
+
unitID: string;
|
|
22
|
+
/**
|
|
23
|
+
* The local edits the server has not acknowledged, in the format of the offline cache, or `null` when there are
|
|
24
|
+
* none. `awaitingChangeset` has been sent; whether the server applied it is unknown.
|
|
25
|
+
*/
|
|
26
|
+
localChanges: IUnitCacheData | null;
|
|
27
|
+
/** Whether the offline cache of the unit was cleared. When `false`, a page reload replays the cached edits. */
|
|
28
|
+
cacheCleared: boolean;
|
|
29
|
+
}
|
|
14
30
|
/**
|
|
15
31
|
* Each univer document instance would map to an `CollaborationEntity` to handle collaborated editing events.
|
|
16
32
|
*/
|
|
@@ -37,6 +53,12 @@ export declare abstract class CollaborationEntity extends RxDisposable {
|
|
|
37
53
|
protected _changesetSessionId: string;
|
|
38
54
|
protected _changesetReqId: number;
|
|
39
55
|
readonly status$: Observable<CollaborationStatus>;
|
|
56
|
+
private readonly _changesetResend$;
|
|
57
|
+
/** Emits every time the awaiting changeset is sent again because it was not acknowledged. */
|
|
58
|
+
readonly changesetResend$: Observable<IChangesetResendEvent>;
|
|
59
|
+
private readonly _conflict$;
|
|
60
|
+
/** Emits once when the unit enters the conflict state, after its offline cache has been cleared. */
|
|
61
|
+
readonly conflict$: Observable<ICollaborationConflictEvent>;
|
|
40
62
|
constructor(unitID: string, session: CollaborationSession, _type: UniverInstanceType, _injector: Injector, _compressMutationService: CompressMutationService, _localeService: LocaleService, _revisionService: RevisionService, _eventService: CollaborationUIEventService, _univerInstanceService: IUniverInstanceService, _logService: ILogService, _commandService: ICommandService, _permissionService: IPermissionService, _singleActiveUnitService?: ISingleActiveUnitService | undefined, _localCacheService?: ILocalCacheService | undefined);
|
|
41
63
|
init(): Promise<void>;
|
|
42
64
|
/**
|
|
@@ -45,6 +67,9 @@ export declare abstract class CollaborationEntity extends RxDisposable {
|
|
|
45
67
|
*/
|
|
46
68
|
pauseCollaboration(): IDisposable;
|
|
47
69
|
onLocalMutation(command: IMutationInfo): void;
|
|
70
|
+
/** Whether there are local mutations that the server has not acknowledged yet. */
|
|
71
|
+
hasWritePending(): boolean;
|
|
72
|
+
dispose(): void;
|
|
48
73
|
private _updateState;
|
|
49
74
|
protected _init(): Promise<CollaborationState>;
|
|
50
75
|
/**
|
|
@@ -53,6 +78,8 @@ export declare abstract class CollaborationEntity extends RxDisposable {
|
|
|
53
78
|
private _transitionLocked;
|
|
54
79
|
private _unlockTransition;
|
|
55
80
|
private _lockTransition;
|
|
81
|
+
/** Run one transition under the lock. The lock is released even when the state throws, so the next event can be handled. */
|
|
82
|
+
private _transit;
|
|
56
83
|
protected _onLocalMutation(command: IMutationInfo): void;
|
|
57
84
|
private _remoteChangesetQueue;
|
|
58
85
|
private _onRemoteChangeset;
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import type { IAcknowledgedChangeset, IChangeset } from '@univerjs-pro/collaboration';
|
|
2
2
|
import type { IMutationInfo, Nullable } from '@univerjs/core';
|
|
3
|
-
import type {
|
|
3
|
+
import type { IUnitCacheData } from '../../services/local-cache/local-cache.service';
|
|
4
4
|
import type { ICollaborativeUndoRedoService } from '../../services/undoredo/collaborative-undoredo.service';
|
|
5
|
+
import type { ChangesetResendInfo, ChangesetResendReason } from './changeset-submitter';
|
|
6
|
+
import type { ICollaborationSendOptions } from '../../services/socket/collaboration-socket.service';
|
|
5
7
|
import { ITransformService, RevisionService } from '@univerjs-pro/collaboration';
|
|
6
|
-
import { ICommandService, IConfigService, ILogService, Injector, IPermissionService, IUniverInstanceService,
|
|
8
|
+
import { ICommandService, IConfigService, ILogService, Injector, IPermissionService, IUniverInstanceService, UniverInstanceType, UserManagerService } from '@univerjs/core';
|
|
7
9
|
import { CollaborationUIEventService } from '../../services/collaboration-ui-event/collaboration-ui-event';
|
|
8
10
|
import { ILocalCacheService } from '../../services/local-cache/local-cache.service';
|
|
11
|
+
import { ChangesetSubmitter } from './changeset-submitter';
|
|
9
12
|
/** Provide both states when and after changing, so the parent could examine if state transition is correct. */
|
|
10
13
|
export type StateChangeHandler = (before: CollaborationState, after: CollaborationState) => void;
|
|
11
14
|
/** A network interface to send changeset and */
|
|
12
|
-
export type SendChangesetHandler = (changeset: IChangeset) => void
|
|
15
|
+
export type SendChangesetHandler = (changeset: IChangeset, options: ICollaborationSendOptions) => void | Promise<void>;
|
|
13
16
|
export type MissingChangesetsHandler = (params: {
|
|
14
17
|
from: number;
|
|
15
18
|
to: number;
|
|
@@ -36,7 +39,57 @@ export interface ICollaborationStateHandler {
|
|
|
36
39
|
onSyncEditingCollabCursor?: SyncEditingCollabCursorHandler;
|
|
37
40
|
onTransformRemoteChangesetByIMECache?: TransformRemoteChangesetByIMECache;
|
|
38
41
|
onTransformRemoteChangesetByStateCache?: TransformRemoteChangesetByStateCache;
|
|
42
|
+
/** The awaiting changeset was sent again. */
|
|
43
|
+
onChangesetResend?: (info: ChangesetResendInfo) => void;
|
|
44
|
+
/**
|
|
45
|
+
* The unit entered `ConflictState` and its recovery preparation is done: the unacknowledged local edits are backed
|
|
46
|
+
* up and the offline cache is cleared (`cacheCleared` tells whether that succeeded). Called once per conflict.
|
|
47
|
+
*/
|
|
48
|
+
onConflict?: (info: IConflictInfo, localChanges: IUnitCacheData | null, cacheCleared: boolean) => void;
|
|
49
|
+
/** How long to wait for an acknowledgement before resending; see `IUniverCollaborationClientConfig`. */
|
|
50
|
+
resendTimeouts?: {
|
|
51
|
+
ackTimeout?: number;
|
|
52
|
+
maxAckTimeout?: number;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/** Why a unit entered `ConflictState`. */
|
|
56
|
+
export type ConflictReason =
|
|
57
|
+
/** A remote changeset could not be transformed against the local edits. */
|
|
58
|
+
'transform_failed'
|
|
59
|
+
/** A remote changeset was transformed but one of its mutations failed to execute. */
|
|
60
|
+
| 'apply_failed'
|
|
61
|
+
/** The server rejected the local changeset. */
|
|
62
|
+
| 'changeset_rejected'
|
|
63
|
+
/** The server rejected the local changeset for lack of permission. */
|
|
64
|
+
| 'permission_rejected'
|
|
65
|
+
/** Another collaborator reverted the unit to a historical revision. */
|
|
66
|
+
| 'remote_revert';
|
|
67
|
+
/** The remote revert that caused a `remote_revert` conflict. */
|
|
68
|
+
export interface IRemoteRevertInfo {
|
|
69
|
+
/** The revision that was restored. */
|
|
70
|
+
revision: number;
|
|
71
|
+
/** The member ID of the collaborator who restored it. */
|
|
72
|
+
memberId: string;
|
|
73
|
+
/** The user ID of the collaborator who restored it. */
|
|
74
|
+
userId: string;
|
|
75
|
+
}
|
|
76
|
+
/** Why a unit entered `ConflictState`. */
|
|
77
|
+
export interface IConflictInfo {
|
|
78
|
+
reason: ConflictReason;
|
|
79
|
+
/** The error message, when the conflict was caused by an error. */
|
|
80
|
+
message?: string;
|
|
81
|
+
/** Present when `reason` is `remote_revert`. */
|
|
82
|
+
revert?: IRemoteRevertInfo;
|
|
83
|
+
}
|
|
84
|
+
/** A remote changeset was transformed successfully but executing its mutations failed. */
|
|
85
|
+
export declare class RemoteChangesetApplyError extends Error {
|
|
86
|
+
readonly applyError: unknown;
|
|
87
|
+
constructor(applyError: unknown);
|
|
39
88
|
}
|
|
89
|
+
/** How a remote changeset relates to the local copy, see `CollaborationState._classifyRemoteChangeset`. */
|
|
90
|
+
export type RemoteChangesetKind = 'echo' | 'remote_revert' | 'applied' | 'missing' | 'apply';
|
|
91
|
+
/** How an acknowledgement relates to the local copy, see `CollaborationState._classifyAck`. */
|
|
92
|
+
export type RemoteAckKind = 'ack_of_awaiting' | 'stale' | 'missing' | 'next';
|
|
40
93
|
export declare enum CollaborationStatus {
|
|
41
94
|
NOT_COLLAB = "not_collab",
|
|
42
95
|
SYNCED = "synced",
|
|
@@ -68,7 +121,18 @@ export declare abstract class CollaborationState {
|
|
|
68
121
|
constructor(unitID: string, type: UniverInstanceType, awaitingChangeset: Nullable<IChangeset>, pendingMutations: IMutationInfo[],
|
|
69
122
|
/** State may change internally. Parent module should provide a callback to handle this event. */
|
|
70
123
|
_handler: ICollaborationStateHandler, _commandService: ICommandService, _undoRedoService: ICollaborativeUndoRedoService, _revisionService: RevisionService, _localCacheService?: ILocalCacheService | undefined);
|
|
71
|
-
|
|
124
|
+
/**
|
|
125
|
+
* Send the awaiting changeset again. Only states that keep an awaiting changeset implement it.
|
|
126
|
+
* @param reason Why the changeset is resent. Defaults to `reconnect`.
|
|
127
|
+
*/
|
|
128
|
+
abstract resend(reason?: ChangesetResendReason): void;
|
|
129
|
+
/** Whether there are local mutations that the server has not acknowledged yet. */
|
|
130
|
+
hasLocalChanges(): boolean;
|
|
131
|
+
/** Release timers held by the state. Called when the entity is disposed while the state is current. */
|
|
132
|
+
dispose(): void;
|
|
133
|
+
/** Mutations in the awaiting changeset plus the pending mutations. */
|
|
134
|
+
protected _unsyncedMutationCount(): number;
|
|
135
|
+
protected _createChangesetSubmitter(changeset: IChangeset): ChangesetSubmitter;
|
|
72
136
|
/**
|
|
73
137
|
* Append a location mutation to collaboration queue.
|
|
74
138
|
*
|
|
@@ -97,13 +161,48 @@ export declare abstract class CollaborationState {
|
|
|
97
161
|
abstract toggleOffline(): CollaborationState;
|
|
98
162
|
abstract toggleOnline(): CollaborationState;
|
|
99
163
|
/**
|
|
100
|
-
*
|
|
101
|
-
*
|
|
164
|
+
* Classify a remote changeset against the local copy. Pure: nothing is requested from the server.
|
|
165
|
+
*
|
|
166
|
+
* - `echo`: the awaiting changeset came back as a broadcast, e.g. when the request was committed after the client
|
|
167
|
+
* reconnected with another member id. It acknowledges the awaiting changeset even when the loaded snapshot
|
|
168
|
+
* already contains its revision, so it is checked first.
|
|
169
|
+
* - `applied`: servers may deliver a changeset more than once or out of order, e.g. when several server nodes
|
|
170
|
+
* relay changesets, or after the snapshot used to join already contains it. Applied revisions are ignored.
|
|
171
|
+
* - `missing`: there is a gap before this changeset. The missing changesets have to be fetched first.
|
|
172
|
+
* - `apply`: the changeset is the next revision and can be transformed and applied.
|
|
173
|
+
*/
|
|
174
|
+
protected _classifyRemoteChangeset(changeset: IChangeset): RemoteChangesetKind;
|
|
175
|
+
/**
|
|
176
|
+
* Classify an acknowledgement against the local copy. Pure: nothing is requested from the server.
|
|
102
177
|
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
178
|
+
* - `ack_of_awaiting`: the revision is already applied but sid/reqId match the awaiting changeset, i.e. the
|
|
179
|
+
* changeset was committed before the loaded snapshot, which already contains it.
|
|
180
|
+
* - `stale`: the revision is already applied and belongs to an earlier changeset.
|
|
181
|
+
* - `missing`: there is a gap before the acknowledged revision. The missing changesets have to be fetched first.
|
|
182
|
+
* - `next`: the acknowledged revision is the next one.
|
|
105
183
|
*/
|
|
106
|
-
protected
|
|
184
|
+
protected _classifyAck(ack: IAcknowledgedChangeset): RemoteAckKind;
|
|
185
|
+
/**
|
|
186
|
+
* Handle a remote changeset the way every online state does: an echo acknowledges the awaiting changeset, an
|
|
187
|
+
* applied revision is ignored, a gap is requested from the server before `onMissing` transits to
|
|
188
|
+
* `FetchingMissState`, and the next revision is handed to `onApply`.
|
|
189
|
+
*/
|
|
190
|
+
protected _dispatchRemoteChangeset(changeset: IChangeset, handlers: {
|
|
191
|
+
onMissing: (changeset: IChangeset) => CollaborationState;
|
|
192
|
+
onApply: (changeset: IChangeset) => CollaborationState;
|
|
193
|
+
/** The unit was reverted on the server: enter `ConflictState` with `_remoteRevertInfo(changeset)`. */
|
|
194
|
+
onRemoteRevert: (changeset: IChangeset) => CollaborationState;
|
|
195
|
+
}): CollaborationState;
|
|
196
|
+
/** The conflict info of a remote changeset that reverts this unit. */
|
|
197
|
+
protected _remoteRevertInfo(changeset: IChangeset): IConflictInfo;
|
|
198
|
+
/** The conflict info of an error thrown while transforming or applying a remote changeset. */
|
|
199
|
+
protected _conflictInfoFromError(error: unknown): IConflictInfo;
|
|
200
|
+
/** The conflict info of a rejection from the server. */
|
|
201
|
+
protected _rejectionInfo(config?: {
|
|
202
|
+
isPermissionRej: boolean;
|
|
203
|
+
}): IConflictInfo;
|
|
204
|
+
/** Request the changesets after the current revision up to `revision`: left open, right closed. */
|
|
205
|
+
protected _requestMissingChangesets(revision: number): void;
|
|
107
206
|
/** Transform undo redo mutations in the undo redo stack. */
|
|
108
207
|
protected _transformUndoredo(changeset: IChangeset): void;
|
|
109
208
|
/** Transform selections by calling the selection manager service. */
|
|
@@ -115,6 +214,14 @@ export declare abstract class CollaborationState {
|
|
|
115
214
|
protected _transformRemoteChangesetByStateCache(changeset: IChangeset): IChangeset;
|
|
116
215
|
protected _syncEditingCollabCursor(changeset: IChangeset): void;
|
|
117
216
|
protected _getCurrentRevision(): number;
|
|
217
|
+
/** The awaiting changeset came back as a broadcast instead of an acknowledgement. */
|
|
218
|
+
protected _isEchoedAwaitingChangeset(remoteChangeset: IChangeset): boolean;
|
|
219
|
+
/**
|
|
220
|
+
* The acknowledgement echoes the committed changeset, so it can be matched to the awaiting changeset by sid/reqId
|
|
221
|
+
* even when its revision is already contained in the loaded snapshot, e.g. the ack was lost and the awaiting
|
|
222
|
+
* changeset was resent after a reload.
|
|
223
|
+
*/
|
|
224
|
+
protected _isAckOfAwaitingChangeset(ack: IAcknowledgedChangeset): boolean;
|
|
118
225
|
protected _incrementRevisionNumber(): void;
|
|
119
226
|
protected _executeRemoteChangeset(changeset: IChangeset): void;
|
|
120
227
|
}
|
|
@@ -130,6 +237,7 @@ export declare class SyncedState extends CollaborationState {
|
|
|
130
237
|
constructor(unitID: string, type: UniverInstanceType, handler: ICollaborationStateHandler, revisionService: RevisionService, _injector: Injector, undoRedoService: ICollaborativeUndoRedoService, commandService: ICommandService, _logService: ILogService, _transformService: ITransformService, localCacheService?: ILocalCacheService | undefined);
|
|
131
238
|
appendMutation(mutation: IMutationInfo): CollaborationState;
|
|
132
239
|
onRemoteChangeset(changeset: IChangeset): CollaborationState;
|
|
240
|
+
private _transformAndApply;
|
|
133
241
|
private _onConflict;
|
|
134
242
|
onRemoteAck(changeset: IAcknowledgedChangeset): CollaborationState;
|
|
135
243
|
onRemoteRej(): CollaborationState;
|
|
@@ -157,6 +265,7 @@ export declare class PendingState extends CollaborationState {
|
|
|
157
265
|
constructor(unitID: string, type: UniverInstanceType, pendingMutations: IMutationInfo[], handler: ICollaborationStateHandler, _injector: Injector, _revisionService: RevisionService, _userManagerService: UserManagerService, _logService: ILogService, commandService: ICommandService, _configService: IConfigService, _transformService: ITransformService, _univerInstanceService: IUniverInstanceService, _undoRedoService: ICollaborativeUndoRedoService, _localCacheService?: ILocalCacheService);
|
|
158
266
|
appendMutation(mutation: IMutationInfo): CollaborationState;
|
|
159
267
|
onRemoteChangeset(changeset: IChangeset): CollaborationState;
|
|
268
|
+
private _transformAndApply;
|
|
160
269
|
onRemoteAck(changeset: IAcknowledgedChangeset): CollaborationState;
|
|
161
270
|
onRemoteRej(): CollaborationState;
|
|
162
271
|
onRemoteRetry(): CollaborationState;
|
|
@@ -165,6 +274,7 @@ export declare class PendingState extends CollaborationState {
|
|
|
165
274
|
private _getSendChangesetTimeout;
|
|
166
275
|
resend(): void;
|
|
167
276
|
private _clearScheduledTask;
|
|
277
|
+
dispose(): void;
|
|
168
278
|
private _onConflict;
|
|
169
279
|
}
|
|
170
280
|
/**
|
|
@@ -175,24 +285,25 @@ export declare class AwaitingState extends CollaborationState {
|
|
|
175
285
|
private readonly _logService;
|
|
176
286
|
private readonly _transformService;
|
|
177
287
|
readonly status = CollaborationStatus.AWAITING;
|
|
178
|
-
|
|
179
|
-
private
|
|
180
|
-
|
|
181
|
-
private _sender;
|
|
182
|
-
constructor(unitID: string, type: UniverInstanceType, awaitingChangeset: IChangeset, handler: ICollaborationStateHandler, _injector: Injector, revisionService: RevisionService, commandService: ICommandService, _logService: ILogService, _transformService: ITransformService, undoRedoService: ICollaborativeUndoRedoService, localCacheService?: ILocalCacheService);
|
|
288
|
+
/** Resends the awaiting changeset until it is acknowledged. Handed to the next state that keeps the changeset. */
|
|
289
|
+
private readonly _submitter;
|
|
290
|
+
constructor(unitID: string, type: UniverInstanceType, awaitingChangeset: IChangeset, handler: ICollaborationStateHandler, submitter: ChangesetSubmitter | undefined, _injector: Injector, revisionService: RevisionService, commandService: ICommandService, _logService: ILogService, _transformService: ITransformService, undoRedoService: ICollaborativeUndoRedoService, localCacheService?: ILocalCacheService);
|
|
183
291
|
appendMutation(mutation: IMutationInfo): CollaborationState;
|
|
184
292
|
onRemoteChangeset(changeset: IChangeset): CollaborationState;
|
|
293
|
+
private _transformAndApply;
|
|
185
294
|
onRemoteAck(changeset: IAcknowledgedChangeset): CollaborationState;
|
|
295
|
+
private _toSynced;
|
|
186
296
|
onRemoteRej(config?: {
|
|
187
297
|
isPermissionRej: boolean;
|
|
188
298
|
}): CollaborationState;
|
|
189
299
|
onRemoteRetry(changeset: IChangeset): CollaborationState;
|
|
190
300
|
toggleOffline(): CollaborationState;
|
|
191
301
|
toggleOnline(): CollaborationState;
|
|
192
|
-
resend(): void;
|
|
302
|
+
resend(reason?: ChangesetResendReason): void;
|
|
193
303
|
private _onConflict;
|
|
194
|
-
|
|
195
|
-
|
|
304
|
+
dispose(): void;
|
|
305
|
+
/** Detach from the submitter before the awaiting changeset is handed to the next state. */
|
|
306
|
+
private _detachSubmitter;
|
|
196
307
|
}
|
|
197
308
|
/**
|
|
198
309
|
* Some local mutations are sent to the server and waiting for acknowledgement, yet still some local mutations
|
|
@@ -204,42 +315,51 @@ export declare class AwaitingWithPendingState extends CollaborationState {
|
|
|
204
315
|
private readonly _transformService;
|
|
205
316
|
private readonly localCacheService?;
|
|
206
317
|
readonly status = CollaborationStatus.AWAITING_WITH_PENDING;
|
|
207
|
-
|
|
208
|
-
private
|
|
209
|
-
|
|
210
|
-
private _sender;
|
|
211
|
-
constructor(unitID: string, type: UniverInstanceType, awaitingChangeset: IChangeset, pendingMutations: IMutationInfo[], handler: ICollaborationStateHandler, timer: Observable<{
|
|
212
|
-
timeout: number;
|
|
213
|
-
reqId: number;
|
|
214
|
-
}> | undefined, _injector: Injector, revisionService: RevisionService, commandService: ICommandService, _logService: ILogService, _transformService: ITransformService, undoRedoService: ICollaborativeUndoRedoService, localCacheService?: ILocalCacheService | undefined);
|
|
318
|
+
/** Resends the awaiting changeset until it is acknowledged. Handed to the next state that keeps the changeset. */
|
|
319
|
+
private readonly _submitter;
|
|
320
|
+
constructor(unitID: string, type: UniverInstanceType, awaitingChangeset: IChangeset, pendingMutations: IMutationInfo[], handler: ICollaborationStateHandler, submitter: ChangesetSubmitter | undefined, _injector: Injector, revisionService: RevisionService, commandService: ICommandService, _logService: ILogService, _transformService: ITransformService, undoRedoService: ICollaborativeUndoRedoService, localCacheService?: ILocalCacheService | undefined);
|
|
215
321
|
appendMutation(mutation: IMutationInfo): CollaborationState;
|
|
216
322
|
onRemoteChangeset(changeset: IChangeset): CollaborationState;
|
|
323
|
+
private _transformAndApply;
|
|
217
324
|
onRemoteAck(changeset: IAcknowledgedChangeset): CollaborationState;
|
|
325
|
+
/** Transit to `PendingState` to schedule another sending task. */
|
|
326
|
+
private _toPending;
|
|
218
327
|
onRemoteRej(config?: {
|
|
219
328
|
isPermissionRej: boolean;
|
|
220
329
|
}): CollaborationState;
|
|
221
330
|
onRemoteRetry(changeset: IChangeset): CollaborationState;
|
|
222
331
|
toggleOffline(): CollaborationState;
|
|
223
332
|
toggleOnline(): CollaborationState;
|
|
224
|
-
resend(): void;
|
|
333
|
+
resend(reason?: ChangesetResendReason): void;
|
|
225
334
|
private _onConflict;
|
|
226
|
-
|
|
227
|
-
|
|
335
|
+
dispose(): void;
|
|
336
|
+
/** Detach from the submitter before the awaiting changeset is handed to the next state. */
|
|
337
|
+
private _detachSubmitter;
|
|
228
338
|
}
|
|
229
339
|
/**
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
* `ConflictState` is a dead end state. It cannot transit to any other state. User could only reload the page to
|
|
233
|
-
* reload the document.
|
|
340
|
+
* A dead end state: the unit can no longer follow the server. Editing is disabled. `info` says why and `localChanges`
|
|
341
|
+
* holds the local edits the server has not acknowledged. Reported once through `CollaborationEntity.conflict$`.
|
|
234
342
|
*/
|
|
235
343
|
export declare class ConflictState extends CollaborationState {
|
|
236
|
-
|
|
344
|
+
readonly info: IConflictInfo;
|
|
237
345
|
private readonly _permissionService;
|
|
238
|
-
private readonly
|
|
346
|
+
private readonly _userManagerService;
|
|
347
|
+
private readonly _logService;
|
|
239
348
|
private readonly _collaborationUIEventService;
|
|
240
349
|
private readonly localCacheService?;
|
|
241
350
|
readonly status = CollaborationStatus.CONFLICT;
|
|
242
|
-
|
|
351
|
+
/**
|
|
352
|
+
* The local edits the server has not acknowledged, in the format of the offline cache, or `null` when there are
|
|
353
|
+
* none. `awaitingChangeset` has been sent; whether the server applied it is unknown.
|
|
354
|
+
*/
|
|
355
|
+
readonly localChanges: IUnitCacheData | null;
|
|
356
|
+
constructor(unitID: string, type: UniverInstanceType, awaitingChangeset: Nullable<IChangeset>, pendingMutations: IMutationInfo[], handler: ICollaborationStateHandler, info: IConflictInfo, _permissionService: IPermissionService, commandService: ICommandService, undoRedoService: ICollaborativeUndoRedoService, revisionService: RevisionService, _userManagerService: UserManagerService, _logService: ILogService, _collaborationUIEventService: CollaborationUIEventService, localCacheService?: ILocalCacheService | undefined);
|
|
357
|
+
/**
|
|
358
|
+
* Back up the unacknowledged local edits, then clear the offline cache so that the next load does not replay them
|
|
359
|
+
* (a cache with pending edits would also make the loader fetch the cached revision). Only then is the conflict
|
|
360
|
+
* reported, so that the handler may reload the page safely.
|
|
361
|
+
*/
|
|
362
|
+
private _prepareConflictRecovery;
|
|
243
363
|
appendMutation(): CollaborationState;
|
|
244
364
|
onRemoteChangeset(): CollaborationState;
|
|
245
365
|
onRemoteAck(): CollaborationState;
|
|
@@ -248,18 +368,17 @@ export declare class ConflictState extends CollaborationState {
|
|
|
248
368
|
toggleOffline(): CollaborationState;
|
|
249
369
|
toggleOnline(): CollaborationState;
|
|
250
370
|
resend(): void;
|
|
251
|
-
private _prepareConflictRecovery;
|
|
252
371
|
private _showConflictProcessingNotification;
|
|
253
|
-
private _showConflictNotification;
|
|
254
372
|
private _disableEditing;
|
|
255
373
|
}
|
|
256
374
|
export declare class OfflineState extends CollaborationState {
|
|
257
375
|
private readonly _injector;
|
|
376
|
+
private readonly _logService;
|
|
258
377
|
readonly status = CollaborationStatus.OFFLINE;
|
|
259
|
-
constructor(unitID: string, type: UniverInstanceType, awaitingChangeset: Nullable<IChangeset>, pendingMutations: IMutationInfo[], handler: ICollaborationStateHandler, _injector: Injector, revisionService: RevisionService, commandService: ICommandService, undoRedoService: ICollaborativeUndoRedoService, localCacheService?: ILocalCacheService);
|
|
378
|
+
constructor(unitID: string, type: UniverInstanceType, awaitingChangeset: Nullable<IChangeset>, pendingMutations: IMutationInfo[], handler: ICollaborationStateHandler, _injector: Injector, revisionService: RevisionService, commandService: ICommandService, undoRedoService: ICollaborativeUndoRedoService, _logService: ILogService, localCacheService?: ILocalCacheService);
|
|
260
379
|
appendMutation(mutation: IMutationInfo): CollaborationState;
|
|
261
|
-
onRemoteChangeset(
|
|
262
|
-
onRemoteAck(): CollaborationState;
|
|
380
|
+
onRemoteChangeset(changeset: IChangeset): CollaborationState;
|
|
381
|
+
onRemoteAck(changeset: IAcknowledgedChangeset): CollaborationState;
|
|
263
382
|
onRemoteRej(): CollaborationState;
|
|
264
383
|
onRemoteRetry(): CollaborationState;
|
|
265
384
|
toggleOffline(): CollaborationState;
|
|
@@ -280,6 +399,11 @@ export declare class FetchingMissState extends CollaborationState {
|
|
|
280
399
|
*/
|
|
281
400
|
private _acknowledgedAwaitingRevision;
|
|
282
401
|
private _queuedRemoteChangesets;
|
|
402
|
+
/**
|
|
403
|
+
* The resends of the awaiting changeset. It is not bound while the gap is fetched: a resend that comes due
|
|
404
|
+
* meanwhile runs once the next awaiting state binds it.
|
|
405
|
+
*/
|
|
406
|
+
private _submitter;
|
|
283
407
|
private readonly _injector;
|
|
284
408
|
private readonly _logService;
|
|
285
409
|
private readonly _transformService;
|
|
@@ -291,13 +415,21 @@ export declare class FetchingMissState extends CollaborationState {
|
|
|
291
415
|
* Keep the changeset in `_awaitingChangeset` for transform and sid/reqId de-duplication, and store only
|
|
292
416
|
* the acknowledged server revision here.
|
|
293
417
|
*/
|
|
294
|
-
_acknowledgedAwaitingRevision: Nullable<number>, _queuedRemoteChangesets: IChangeset[], handler: ICollaborationStateHandler,
|
|
418
|
+
_acknowledgedAwaitingRevision: Nullable<number>, _queuedRemoteChangesets: IChangeset[], handler: ICollaborationStateHandler,
|
|
419
|
+
/**
|
|
420
|
+
* The resends of the awaiting changeset. It is not bound while the gap is fetched: a resend that comes due
|
|
421
|
+
* meanwhile runs once the next awaiting state binds it.
|
|
422
|
+
*/
|
|
423
|
+
_submitter: ChangesetSubmitter | undefined, _injector: Injector, revisionService: RevisionService, _logService: ILogService, commandService: ICommandService, undoRedoService: ICollaborativeUndoRedoService, _transformService: ITransformService, localCacheService?: ILocalCacheService | undefined);
|
|
295
424
|
onMissedChangesetFetched(changesets: IChangeset[]): CollaborationState;
|
|
296
425
|
private _handleRemoteChangesets;
|
|
297
426
|
private _transformAndApplyRemoteChangeset;
|
|
298
427
|
private _acknowledgeAwaitingIfReady;
|
|
299
428
|
private _acknowledgeAwaiting;
|
|
300
|
-
|
|
429
|
+
/** The awaiting changeset was committed before the loaded snapshot, so there is no new revision to count. */
|
|
430
|
+
private _dropAcknowledgedAwaiting;
|
|
431
|
+
private _disposeSubmitter;
|
|
432
|
+
dispose(): void;
|
|
301
433
|
resend(): void;
|
|
302
434
|
appendMutation(mutation: IMutationInfo<object>): CollaborationState;
|
|
303
435
|
onRemoteChangeset(changeset: IChangeset): CollaborationState;
|
|
@@ -305,7 +437,7 @@ export declare class FetchingMissState extends CollaborationState {
|
|
|
305
437
|
onRemoteRej(config?: {
|
|
306
438
|
isPermissionRej: boolean;
|
|
307
439
|
}): CollaborationState;
|
|
308
|
-
onRemoteRetry(): CollaborationState;
|
|
440
|
+
onRemoteRetry(changeset: IChangeset): CollaborationState;
|
|
309
441
|
toggleOffline(): CollaborationState;
|
|
310
442
|
toggleOnline(): CollaborationState;
|
|
311
443
|
private _onConflict;
|
|
@@ -14,8 +14,15 @@ export declare class CollaborationController extends RxDisposable {
|
|
|
14
14
|
constructor(_injector: Injector, _collabSessionService: CollaborationSessionService, _configService: IConfigService, _univerInstanceService: IUniverInstanceService);
|
|
15
15
|
dispose(): void;
|
|
16
16
|
getCollabEntity(id: string): Nullable<CollaborationEntity>;
|
|
17
|
+
/** The entities of every unit that is collaborating now. */
|
|
18
|
+
getCollabEntities(): CollaborationEntity[];
|
|
17
19
|
getCollabEntity$(id: string): Observable<CollaborationEntity>;
|
|
18
20
|
readyForCollab(id: string): Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* Whether a unit has local mutations that the server has not acknowledged yet.
|
|
23
|
+
* @param unitId The unit to check. When omitted, every collaborating unit is checked.
|
|
24
|
+
*/
|
|
25
|
+
hasWritePending(unitId?: string): boolean;
|
|
19
26
|
private _init;
|
|
20
27
|
/**
|
|
21
28
|
* Start collaboration on a document.
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import type { IChangeset, RevisionService } from '@univerjs-pro/collaboration';
|
|
1
|
+
import type { IChangeset, IRevertRevisionMutationParams, RevisionService } from '@univerjs-pro/collaboration';
|
|
2
2
|
import type { IMutationInfo, IUniverInstanceService, UserManagerService } from '@univerjs/core';
|
|
3
|
-
import type { Observable } from 'rxjs';
|
|
4
3
|
export interface IAssembledChangeset {
|
|
5
4
|
changeset: IChangeset;
|
|
6
5
|
pendingMutations: IMutationInfo[];
|
|
@@ -23,4 +22,9 @@ export declare const assemblePdfChangeset: typeof assembleMutationChangeset;
|
|
|
23
22
|
export declare function assembleBaseChangeset(pendingMutations: IMutationInfo[], unitID: string, univerInstanceService: IUniverInstanceService, revisionService: RevisionService, userManagerService: UserManagerService): IAssembledChangeset;
|
|
24
23
|
export declare function assembleSlideChangeset(pendingMutations: IMutationInfo[], unitID: string, univerInstanceService: IUniverInstanceService, revisionService: RevisionService, userManagerService: UserManagerService): IAssembledChangeset;
|
|
25
24
|
export declare function assembleDocChangeset(pendingMutations: IMutationInfo[], unitID: string, univerInstanceService: IUniverInstanceService, revisionService: RevisionService, userManagerService: UserManagerService): IAssembledChangeset;
|
|
26
|
-
|
|
25
|
+
/**
|
|
26
|
+
* The revert of `unitID` carried by a remote changeset, if any. A revert is executed on the server: the next revision
|
|
27
|
+
* is the restored snapshot, so the local copy cannot transform it and has to be reloaded. Reverts of other units are
|
|
28
|
+
* ordinary changesets.
|
|
29
|
+
*/
|
|
30
|
+
export declare function findRemoteRevert(changeset: IChangeset, unitID: string): IRevertRevisionMutationParams | null;
|