polfan-server-js-client 0.1.993 → 0.1.995
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/build/index.js +61 -69
- package/build/index.js.map +1 -1
- package/build/types/state-tracker/TopicHistoryWindow.d.ts +19 -4
- package/build/types/types/src/schemes/commands/GetTopics.d.ts +1 -1
- package/package.json +1 -1
- package/src/state-tracker/RoomsManager.ts +6 -4
- package/src/state-tracker/TopicHistoryWindow.ts +39 -19
- package/src/types/src/schemes/commands/GetTopics.ts +1 -1
- package/tests/history-window.test.ts +34 -10
|
@@ -2,9 +2,23 @@ import { Message, MessageReference } from "../types/src";
|
|
|
2
2
|
import { ChatStateTracker } from "./ChatStateTracker";
|
|
3
3
|
import { ObservableIndexedObjectCollection } from "../IndexedObjectCollection";
|
|
4
4
|
export declare enum WindowState {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
/**
|
|
6
|
+
* No messages exist in the history window.
|
|
7
|
+
*/
|
|
8
|
+
EMPTY = 0,
|
|
9
|
+
/**
|
|
10
|
+
* The latest messages (those received live) are available in the history window, history has not been fetched.
|
|
11
|
+
*/
|
|
12
|
+
LATEST_LIVE = 1,
|
|
13
|
+
/**
|
|
14
|
+
* The latest messages has been fetched and are available in the history window.
|
|
15
|
+
*/
|
|
16
|
+
LATEST_FETCHED = 2,
|
|
17
|
+
/**
|
|
18
|
+
* The historical messages have been fetched and are available in the history window.
|
|
19
|
+
* Latest messages are not available and will not be available.
|
|
20
|
+
*/
|
|
21
|
+
PAST_FETCHED = 3
|
|
8
22
|
}
|
|
9
23
|
export declare abstract class TraversableRemoteCollection<T> extends ObservableIndexedObjectCollection<T> {
|
|
10
24
|
/**
|
|
@@ -25,7 +39,8 @@ export declare abstract class TraversableRemoteCollection<T> extends ObservableI
|
|
|
25
39
|
protected abstract fetchItemsBefore(): Promise<T[] | null>;
|
|
26
40
|
protected abstract fetchItemsAfter(): Promise<T[] | null>;
|
|
27
41
|
protected abstract isLatestItemLoaded(): Promise<boolean>;
|
|
28
|
-
protected
|
|
42
|
+
protected refreshFetchedState(): Promise<void>;
|
|
43
|
+
protected refreshLiveState(): void;
|
|
29
44
|
private throwIfFetchingInProgress;
|
|
30
45
|
private addItems;
|
|
31
46
|
/**
|
package/package.json
CHANGED
|
@@ -96,15 +96,17 @@ export class RoomsManager {
|
|
|
96
96
|
await this.deferredSession.promise;
|
|
97
97
|
|
|
98
98
|
if (tryToFetchTopicIds?.length) {
|
|
99
|
-
|
|
99
|
+
// Topic can be fetched if it isn't already cached and fetch is not already in progress
|
|
100
|
+
const canFetch = (topicId: string) => ! this.topics.get(roomId)?.has(topicId) && ! this.topicsPromises.has(roomId + topicId);
|
|
101
|
+
const idsToFetch = tryToFetchTopicIds.filter(canFetch);
|
|
100
102
|
|
|
101
|
-
if (
|
|
103
|
+
if (idsToFetch.length) {
|
|
102
104
|
const promise = this.tracker
|
|
103
105
|
.client
|
|
104
|
-
.send('GetTopics', {roomId,
|
|
106
|
+
.send('GetTopics', {roomId, topicIds: idsToFetch})
|
|
105
107
|
.then(result => this.topics.get(result.data.location.roomId)?.set(...result.data.topics));
|
|
106
108
|
|
|
107
|
-
|
|
109
|
+
idsToFetch.forEach(topicId => this.topicsPromises.register(promise, roomId + topicId));
|
|
108
110
|
}
|
|
109
111
|
|
|
110
112
|
for (const topicId of tryToFetchTopicIds) {
|
|
@@ -1,11 +1,28 @@
|
|
|
1
|
-
import {Message, MessageReference, NewMessage,
|
|
1
|
+
import {Message, MessageReference, NewMessage, Session, Topic} from "../types/src";
|
|
2
2
|
import {ChatStateTracker} from "./ChatStateTracker";
|
|
3
3
|
import {ObservableIndexedObjectCollection} from "../IndexedObjectCollection";
|
|
4
4
|
|
|
5
5
|
export enum WindowState {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
/**
|
|
7
|
+
* No messages exist in the history window.
|
|
8
|
+
*/
|
|
9
|
+
EMPTY,
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The latest messages (those received live) are available in the history window, history has not been fetched.
|
|
13
|
+
*/
|
|
14
|
+
LATEST_LIVE,
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The latest messages has been fetched and are available in the history window.
|
|
18
|
+
*/
|
|
19
|
+
LATEST_FETCHED,
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The historical messages have been fetched and are available in the history window.
|
|
23
|
+
* Latest messages are not available and will not be available.
|
|
24
|
+
*/
|
|
25
|
+
PAST_FETCHED,
|
|
9
26
|
}
|
|
10
27
|
|
|
11
28
|
export abstract class TraversableRemoteCollection<T> extends ObservableIndexedObjectCollection<T> {
|
|
@@ -22,17 +39,17 @@ export abstract class TraversableRemoteCollection<T> extends ObservableIndexedOb
|
|
|
22
39
|
*/
|
|
23
40
|
public limit: number | null = 50;
|
|
24
41
|
|
|
25
|
-
private currentState: WindowState = WindowState.
|
|
42
|
+
private currentState: WindowState = WindowState.EMPTY;
|
|
26
43
|
private fetchingState: WindowState = undefined;
|
|
27
44
|
|
|
28
45
|
public async resetToLatest(): Promise<void> {
|
|
29
46
|
this.throwIfFetchingInProgress();
|
|
30
47
|
|
|
31
|
-
if (this.currentState === WindowState.
|
|
48
|
+
if (this.currentState === WindowState.LATEST_FETCHED) {
|
|
32
49
|
return;
|
|
33
50
|
}
|
|
34
51
|
|
|
35
|
-
this.fetchingState = WindowState.
|
|
52
|
+
this.fetchingState = WindowState.LATEST_FETCHED;
|
|
36
53
|
|
|
37
54
|
let result;
|
|
38
55
|
|
|
@@ -44,13 +61,13 @@ export abstract class TraversableRemoteCollection<T> extends ObservableIndexedOb
|
|
|
44
61
|
|
|
45
62
|
this.deleteAll();
|
|
46
63
|
this.addItems(result, 'tail');
|
|
47
|
-
this.currentState = WindowState.
|
|
64
|
+
this.currentState = WindowState.LATEST_FETCHED;
|
|
48
65
|
}
|
|
49
66
|
|
|
50
67
|
public async fetchPrevious(): Promise<void> {
|
|
51
68
|
this.throwIfFetchingInProgress();
|
|
52
69
|
|
|
53
|
-
this.fetchingState = WindowState.
|
|
70
|
+
this.fetchingState = WindowState.PAST_FETCHED;
|
|
54
71
|
|
|
55
72
|
let result;
|
|
56
73
|
|
|
@@ -66,7 +83,7 @@ export abstract class TraversableRemoteCollection<T> extends ObservableIndexedOb
|
|
|
66
83
|
|
|
67
84
|
if (result.length) {
|
|
68
85
|
this.addItems(result, 'head');
|
|
69
|
-
|
|
86
|
+
await this.refreshFetchedState();
|
|
70
87
|
}
|
|
71
88
|
}
|
|
72
89
|
|
|
@@ -88,7 +105,7 @@ export abstract class TraversableRemoteCollection<T> extends ObservableIndexedOb
|
|
|
88
105
|
|
|
89
106
|
if (result.length) {
|
|
90
107
|
this.addItems(result, 'tail');
|
|
91
|
-
await this.
|
|
108
|
+
await this.refreshFetchedState();
|
|
92
109
|
}
|
|
93
110
|
}
|
|
94
111
|
|
|
@@ -100,8 +117,14 @@ export abstract class TraversableRemoteCollection<T> extends ObservableIndexedOb
|
|
|
100
117
|
|
|
101
118
|
protected abstract isLatestItemLoaded(): Promise<boolean>;
|
|
102
119
|
|
|
103
|
-
protected async
|
|
104
|
-
this.currentState = (await this.isLatestItemLoaded()) ? WindowState.
|
|
120
|
+
protected async refreshFetchedState(): Promise<void> {
|
|
121
|
+
this.currentState = (await this.isLatestItemLoaded()) ? WindowState.LATEST_FETCHED : WindowState.PAST_FETCHED;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
protected refreshLiveState(): void {
|
|
125
|
+
if (this.currentState === WindowState.EMPTY && this.length) {
|
|
126
|
+
this.currentState = WindowState.LATEST_LIVE;
|
|
127
|
+
}
|
|
105
128
|
}
|
|
106
129
|
|
|
107
130
|
private throwIfFetchingInProgress(): void {
|
|
@@ -174,15 +197,12 @@ export class TopicHistoryWindow extends TraversableRemoteCollection<Message> {
|
|
|
174
197
|
|
|
175
198
|
private async handleNewMessage(ev: NewMessage): Promise<void> {
|
|
176
199
|
if (
|
|
177
|
-
[WindowState.
|
|
200
|
+
[WindowState.LATEST_FETCHED, WindowState.LATEST_LIVE, WindowState.EMPTY].includes(this.state)
|
|
178
201
|
&& ev.location.roomId === this.roomId
|
|
179
202
|
&& ev.location.topicId === this.topicId
|
|
180
203
|
) {
|
|
181
204
|
this.set(ev.message);
|
|
182
|
-
|
|
183
|
-
if (this.state === WindowState.UNINITIALIZED) {
|
|
184
|
-
await this.refreshMode();
|
|
185
|
-
}
|
|
205
|
+
this.refreshLiveState();
|
|
186
206
|
}
|
|
187
207
|
}
|
|
188
208
|
|
|
@@ -252,6 +272,6 @@ export class TopicHistoryWindow extends TraversableRemoteCollection<Message> {
|
|
|
252
272
|
|
|
253
273
|
protected async isLatestItemLoaded(): Promise<boolean> {
|
|
254
274
|
const lastMessageId = await this.getLatestMessageId();
|
|
255
|
-
return lastMessageId ? this.has(lastMessageId) :
|
|
275
|
+
return lastMessageId ? this.has(lastMessageId) : true;
|
|
256
276
|
}
|
|
257
277
|
}
|
|
@@ -22,6 +22,11 @@ class TestableHistoryWindow extends TraversableRemoteCollection<SimpleMessage> {
|
|
|
22
22
|
super('id');
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
public simulateNewMessageReceived(): void {
|
|
26
|
+
this.set(messages[messages.length - 1]);
|
|
27
|
+
this.refreshLiveState();
|
|
28
|
+
}
|
|
29
|
+
|
|
25
30
|
protected async fetchItemsAfter(): Promise<SimpleMessage[]> {
|
|
26
31
|
const after = this.getAt(this.length - 1)?.id;
|
|
27
32
|
if (after === undefined) {
|
|
@@ -52,7 +57,26 @@ test('history window - fresh instance', async () => {
|
|
|
52
57
|
const window = new TestableHistoryWindow();
|
|
53
58
|
expect(window.items).toHaveLength(0);
|
|
54
59
|
expect(window.limit).toEqual(50);
|
|
55
|
-
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('history window - states change', async () => {
|
|
63
|
+
const window = new TestableHistoryWindow();
|
|
64
|
+
window.limit = 5;
|
|
65
|
+
|
|
66
|
+
expect(window.state).toEqual(WindowState.EMPTY);
|
|
67
|
+
|
|
68
|
+
window.simulateNewMessageReceived(); // [9]
|
|
69
|
+
|
|
70
|
+
expect(window.state).toEqual(WindowState.LATEST_LIVE);
|
|
71
|
+
|
|
72
|
+
await window.fetchPrevious(); // [6,7,8,9]
|
|
73
|
+
await window.fetchPrevious(); // [3,4,5,6,7]
|
|
74
|
+
|
|
75
|
+
expect(window.state).toEqual(WindowState.PAST_FETCHED);
|
|
76
|
+
|
|
77
|
+
await window.resetToLatest(); // [5,6,7,8,9]
|
|
78
|
+
|
|
79
|
+
expect(window.state).toEqual(WindowState.LATEST_FETCHED);
|
|
56
80
|
});
|
|
57
81
|
|
|
58
82
|
test('history window - traverse back', async () => {
|
|
@@ -61,13 +85,13 @@ test('history window - traverse back', async () => {
|
|
|
61
85
|
|
|
62
86
|
await window.fetchPrevious(); // 7,8,9
|
|
63
87
|
|
|
64
|
-
expect(window.state).toEqual(WindowState.
|
|
88
|
+
expect(window.state).toEqual(WindowState.LATEST_FETCHED);
|
|
65
89
|
expect(window.items).toHaveLength(3);
|
|
66
90
|
[7,8,9].forEach(id => expect(window.items.map(item => item.id)).toContain(id));
|
|
67
91
|
|
|
68
92
|
await window.fetchPrevious(); // 4,5,6,7,8
|
|
69
93
|
|
|
70
|
-
expect(window.state).toEqual(WindowState.
|
|
94
|
+
expect(window.state).toEqual(WindowState.PAST_FETCHED);
|
|
71
95
|
expect(window.items).toHaveLength(5);
|
|
72
96
|
[4,5,6,7,8].forEach(id => expect(window.items.map(item => item.id)).toContain(id));
|
|
73
97
|
|
|
@@ -75,7 +99,7 @@ test('history window - traverse back', async () => {
|
|
|
75
99
|
await window.fetchPrevious(); // 0,1,2,3,4
|
|
76
100
|
await window.fetchPrevious(); // 0,1,2,3,4
|
|
77
101
|
|
|
78
|
-
expect(window.state).toEqual(WindowState.
|
|
102
|
+
expect(window.state).toEqual(WindowState.PAST_FETCHED);
|
|
79
103
|
expect(window.items).toHaveLength(5);
|
|
80
104
|
[0,1,2,3,4].forEach(id => expect(window.items.map(item => item.id)).toContain(id));
|
|
81
105
|
});
|
|
@@ -86,7 +110,7 @@ test('history window - traverse forward', async () => {
|
|
|
86
110
|
|
|
87
111
|
await window.fetchNext();
|
|
88
112
|
|
|
89
|
-
expect(window.state).toEqual(WindowState.
|
|
113
|
+
expect(window.state).toEqual(WindowState.LATEST_FETCHED);
|
|
90
114
|
expect(window.items).toHaveLength(3);
|
|
91
115
|
[7,8,9].forEach(id => expect(window.items.map(item => item.id)).toContain(id));
|
|
92
116
|
|
|
@@ -94,7 +118,7 @@ test('history window - traverse forward', async () => {
|
|
|
94
118
|
await window.fetchPrevious(); // [1,2,3,4,5]
|
|
95
119
|
await window.fetchNext(); // [4,5,6,7,8]
|
|
96
120
|
|
|
97
|
-
expect(window.state).toEqual(WindowState.
|
|
121
|
+
expect(window.state).toEqual(WindowState.PAST_FETCHED);
|
|
98
122
|
expect(window.items).toHaveLength(5);
|
|
99
123
|
[4,5,6,7,8].forEach(id => expect(window.items.map(item => item.id)).toContain(id));
|
|
100
124
|
|
|
@@ -102,7 +126,7 @@ test('history window - traverse forward', async () => {
|
|
|
102
126
|
await window.fetchNext();
|
|
103
127
|
await window.fetchNext(); // move to latest
|
|
104
128
|
|
|
105
|
-
expect(window.state).toEqual(WindowState.
|
|
129
|
+
expect(window.state).toEqual(WindowState.LATEST_FETCHED);
|
|
106
130
|
expect(window.items).toHaveLength(5);
|
|
107
131
|
[5,6,7,8,9].forEach(id => expect(window.items.map(item => item.id)).toContain(id));
|
|
108
132
|
});
|
|
@@ -113,17 +137,17 @@ test('history window - reset to latest', async () => {
|
|
|
113
137
|
|
|
114
138
|
await window.fetchPrevious();
|
|
115
139
|
|
|
116
|
-
expect(window.state).toEqual(WindowState.
|
|
140
|
+
expect(window.state).toEqual(WindowState.LATEST_FETCHED);
|
|
117
141
|
|
|
118
142
|
await window.fetchPrevious();
|
|
119
143
|
await window.fetchPrevious();
|
|
120
144
|
await window.fetchPrevious(); // Move to start
|
|
121
145
|
|
|
122
|
-
expect(window.state).toEqual(WindowState.
|
|
146
|
+
expect(window.state).toEqual(WindowState.PAST_FETCHED);
|
|
123
147
|
|
|
124
148
|
await window.resetToLatest();
|
|
125
149
|
|
|
126
|
-
expect(window.state).toEqual(WindowState.
|
|
150
|
+
expect(window.state).toEqual(WindowState.LATEST_FETCHED);
|
|
127
151
|
expect(window.items).toHaveLength(3);
|
|
128
152
|
[7,8,9].forEach(id => expect(window.items.map(item => item.id)).toContain(id));
|
|
129
153
|
});
|