polfan-server-js-client 0.1.994 → 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.
@@ -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
- UNINITIALIZED = 0,
6
- LATEST = 1,
7
- PAST = 2
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 refreshMode(): Promise<void>;
42
+ protected refreshFetchedState(): Promise<void>;
43
+ protected refreshLiveState(): void;
29
44
  private throwIfFetchingInProgress;
30
45
  private addItems;
31
46
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polfan-server-js-client",
3
- "version": "0.1.994",
3
+ "version": "0.1.995",
4
4
  "description": "JavaScript client library for handling communication with Polfan chat server.",
5
5
  "author": "Jarosław Żak",
6
6
  "license": "MIT",
@@ -1,11 +1,28 @@
1
- import {Message, MessageReference, NewMessage, Room, Session, Topic} from "../types/src";
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
- UNINITIALIZED,
7
- LATEST,
8
- PAST,
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.UNINITIALIZED;
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.LATEST) {
48
+ if (this.currentState === WindowState.LATEST_FETCHED) {
32
49
  return;
33
50
  }
34
51
 
35
- this.fetchingState = WindowState.LATEST;
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.LATEST;
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.PAST;
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
- this.currentState = (await this.isLatestItemLoaded()) ? WindowState.LATEST : WindowState.PAST;
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.refreshMode();
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 refreshMode(): Promise<void> {
104
- this.currentState = (await this.isLatestItemLoaded()) ? WindowState.LATEST : WindowState.PAST;
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.LATEST, WindowState.UNINITIALIZED].includes(this.state)
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) : false;
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
- expect(window.state).toEqual(WindowState.UNINITIALIZED);
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.LATEST);
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.PAST);
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.PAST);
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.LATEST);
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.PAST);
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.LATEST);
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.LATEST);
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.PAST);
146
+ expect(window.state).toEqual(WindowState.PAST_FETCHED);
123
147
 
124
148
  await window.resetToLatest();
125
149
 
126
- expect(window.state).toEqual(WindowState.LATEST);
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
  });