polfan-server-js-client 0.2.101 → 0.2.103
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/.idea/workspace.xml +25 -26
- package/build/index.cjs.js +75 -30
- package/build/index.cjs.js.map +1 -1
- package/build/index.umd.js +1 -1
- package/build/index.umd.js.map +1 -1
- package/build/types/WebApiChatClient.d.ts +10 -8
- package/build/types/state-tracker/FollowedTopicsManager.d.ts +1 -0
- package/package.json +1 -1
- package/src/WebApiChatClient.ts +62 -23
- package/src/state-tracker/FollowedTopicsManager.ts +7 -1
|
@@ -18,19 +18,21 @@ type WebApiEventMap = EventsMap & {
|
|
|
18
18
|
[WebApiChatClientEvent.error]: Error;
|
|
19
19
|
[WebApiChatClientEvent.destroy]: boolean;
|
|
20
20
|
};
|
|
21
|
+
interface SendStackItem {
|
|
22
|
+
data: Envelope;
|
|
23
|
+
attempts: number;
|
|
24
|
+
lastTimeoutId: any;
|
|
25
|
+
}
|
|
21
26
|
export declare class WebApiChatClient extends AbstractChatClient<Pick<WebApiEventMap, keyof WebApiEventMap>> implements ObservableInterface {
|
|
22
27
|
private readonly options;
|
|
23
28
|
readonly Event: typeof WebApiChatClientEvent;
|
|
24
|
-
protected sendStack:
|
|
25
|
-
data: any;
|
|
26
|
-
attempts: number;
|
|
27
|
-
lastTimeoutId: any;
|
|
28
|
-
}[];
|
|
29
|
+
protected sendStack: SendStackItem[];
|
|
29
30
|
constructor(options: WebApiChatClientOptions);
|
|
30
31
|
send<CommandType extends keyof CommandsMap>(commandType: CommandType, commandData: CommandRequest<CommandType>): Promise<CommandResult<CommandResponse<CommandType>>>;
|
|
31
32
|
destroy(): void;
|
|
32
|
-
protected onMessage(
|
|
33
|
-
protected onError(
|
|
34
|
-
protected makeApiCall(
|
|
33
|
+
protected onMessage(item: SendStackItem, response: Response): Promise<void>;
|
|
34
|
+
protected onError(item: SendStackItem, body: string): void;
|
|
35
|
+
protected makeApiCall(item: SendStackItem): void;
|
|
36
|
+
private removeFromStack;
|
|
35
37
|
}
|
|
36
38
|
export {};
|
|
@@ -46,6 +46,7 @@ export declare class FollowedTopicsManager extends EventTarget<EventMap> {
|
|
|
46
46
|
private handleNewMessage;
|
|
47
47
|
private handleFollowedTopicUpdated;
|
|
48
48
|
private handleTopicFollowed;
|
|
49
|
+
private handleTopicUnfollowed;
|
|
49
50
|
private handleRoomDeleted;
|
|
50
51
|
private handleRoomJoin;
|
|
51
52
|
private handleRoomLeft;
|
package/package.json
CHANGED
package/src/WebApiChatClient.ts
CHANGED
|
@@ -22,10 +22,16 @@ type WebApiEventMap = EventsMap & {
|
|
|
22
22
|
[WebApiChatClientEvent.destroy]: boolean;
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
+
interface SendStackItem {
|
|
26
|
+
data: Envelope;
|
|
27
|
+
attempts: number;
|
|
28
|
+
lastTimeoutId: any;
|
|
29
|
+
}
|
|
30
|
+
|
|
25
31
|
export class WebApiChatClient extends AbstractChatClient<Pick<WebApiEventMap, keyof WebApiEventMap>> implements ObservableInterface {
|
|
26
32
|
public readonly Event = WebApiChatClientEvent;
|
|
27
33
|
|
|
28
|
-
protected sendStack:
|
|
34
|
+
protected sendStack: SendStackItem[] = [];
|
|
29
35
|
|
|
30
36
|
public constructor(private readonly options: WebApiChatClientOptions) {
|
|
31
37
|
super();
|
|
@@ -34,48 +40,72 @@ export class WebApiChatClient extends AbstractChatClient<Pick<WebApiEventMap, ke
|
|
|
34
40
|
public async send<CommandType extends keyof CommandsMap>(commandType: CommandType, commandData: CommandRequest<CommandType>):
|
|
35
41
|
Promise<CommandResult<CommandResponse<CommandType>>> {
|
|
36
42
|
const envelope = this.createEnvelope(commandType, commandData);
|
|
37
|
-
|
|
38
|
-
this.
|
|
43
|
+
const item: SendStackItem = {data: envelope, attempts: 0, lastTimeoutId: null};
|
|
44
|
+
this.sendStack.push(item);
|
|
45
|
+
this.makeApiCall(item);
|
|
39
46
|
return this.createPromiseFromCommandEnvelope(envelope);
|
|
40
47
|
}
|
|
41
48
|
|
|
42
49
|
public destroy(): void {
|
|
43
|
-
// Cancel all awaiting requests
|
|
44
|
-
|
|
50
|
+
// Cancel all awaiting requests, rejecting their promises so no caller
|
|
51
|
+
// is left awaiting forever
|
|
52
|
+
const pending = this.sendStack;
|
|
53
|
+
this.sendStack = [];
|
|
54
|
+
pending.forEach(item => {
|
|
45
55
|
if (item.lastTimeoutId) {
|
|
46
56
|
clearTimeout(item.lastTimeoutId);
|
|
47
57
|
}
|
|
48
|
-
this.
|
|
58
|
+
this.handleEnvelopeSendError(item.data, new Error('Client destroyed'));
|
|
49
59
|
});
|
|
50
|
-
this.sendStack = [];
|
|
51
60
|
this.emit(this.Event.destroy, false);
|
|
52
61
|
}
|
|
53
62
|
|
|
54
|
-
protected async onMessage(
|
|
55
|
-
|
|
56
|
-
|
|
63
|
+
protected async onMessage(item: SendStackItem, response: Response): Promise<void> {
|
|
64
|
+
let envelope: Envelope;
|
|
65
|
+
try {
|
|
66
|
+
envelope = await response.json();
|
|
67
|
+
} catch (e) {
|
|
68
|
+
// Non-envelope response (e.g. a proxy error page) - failed attempt
|
|
69
|
+
this.onError(item, `envelope ${item.data.ref} (HTTP ${response.status}, unparsable body)`);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (!this.removeFromStack(item)) {
|
|
74
|
+
return; // Destroyed in the meantime - the promise is already rejected
|
|
75
|
+
}
|
|
76
|
+
|
|
57
77
|
this.handleIncomingEnvelope(envelope);
|
|
58
78
|
this.emit(envelope.type, envelope.data);
|
|
59
79
|
this.emit(this.Event.message, envelope);
|
|
60
80
|
}
|
|
61
81
|
|
|
62
|
-
protected onError(
|
|
63
|
-
if (this.sendStack
|
|
64
|
-
|
|
65
|
-
|
|
82
|
+
protected onError(item: SendStackItem, body: string): void {
|
|
83
|
+
if (!this.sendStack.includes(item)) {
|
|
84
|
+
return; // Destroyed in the meantime
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (item.attempts >= (this.options.attemptsToSend ?? 10)) {
|
|
88
|
+
this.removeFromStack(item);
|
|
89
|
+
this.handleEnvelopeSendError(item.data, new Error(
|
|
66
90
|
`Cannot send ${body}; aborting after reaching the maximum connection errors`
|
|
67
91
|
));
|
|
68
92
|
return;
|
|
69
93
|
}
|
|
70
|
-
|
|
71
|
-
|
|
94
|
+
|
|
95
|
+
item.lastTimeoutId = setTimeout(
|
|
96
|
+
() => this.makeApiCall(item),
|
|
72
97
|
this.options.attemptDelayMs ?? 3000
|
|
73
98
|
);
|
|
74
99
|
}
|
|
75
100
|
|
|
76
|
-
protected makeApiCall(
|
|
77
|
-
this.sendStack
|
|
78
|
-
|
|
101
|
+
protected makeApiCall(item: SendStackItem): void {
|
|
102
|
+
if (!this.sendStack.includes(item)) {
|
|
103
|
+
return; // Destroyed before this (re)try fired
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
item.attempts++;
|
|
107
|
+
item.lastTimeoutId = null;
|
|
108
|
+
const bodyJson = JSON.stringify(item.data);
|
|
79
109
|
const headers: any = {
|
|
80
110
|
'Content-Type': 'application/json',
|
|
81
111
|
Accept: 'application/json'
|
|
@@ -83,15 +113,24 @@ export class WebApiChatClient extends AbstractChatClient<Pick<WebApiEventMap, ke
|
|
|
83
113
|
|
|
84
114
|
headers.Authorization = `Bearer ${this.options.token}`;
|
|
85
115
|
|
|
86
|
-
const
|
|
87
|
-
const url = `${this.options.url}
|
|
116
|
+
const query = new URLSearchParams(this.options.queryParams ?? {}).toString();
|
|
117
|
+
const url = query ? `${this.options.url}?${query}` : this.options.url;
|
|
88
118
|
|
|
89
119
|
fetch(url, {
|
|
90
120
|
headers,
|
|
91
121
|
body: bodyJson,
|
|
92
122
|
method: 'POST',
|
|
93
123
|
})
|
|
94
|
-
.then(response => this.onMessage(
|
|
95
|
-
.catch(() => this.onError(
|
|
124
|
+
.then(response => this.onMessage(item, response))
|
|
125
|
+
.catch(() => this.onError(item, bodyJson));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private removeFromStack(item: SendStackItem): boolean {
|
|
129
|
+
const index = this.sendStack.indexOf(item);
|
|
130
|
+
if (index === -1) {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
this.sendStack.splice(index, 1);
|
|
134
|
+
return true;
|
|
96
135
|
}
|
|
97
136
|
}
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
RoomDeleted,
|
|
11
11
|
RoomJoined, RoomLeft,
|
|
12
12
|
Session, TopicDeleted,
|
|
13
|
-
TopicFollowed
|
|
13
|
+
TopicFollowed, TopicUnfollowed
|
|
14
14
|
} from "../types/src";
|
|
15
15
|
|
|
16
16
|
interface EventMap {
|
|
@@ -31,6 +31,7 @@ export class FollowedTopicsManager extends EventTarget<EventMap> {
|
|
|
31
31
|
this.tracker.client.on('NewTopic', ev => this.handleNewTopic(ev));
|
|
32
32
|
this.tracker.client.on('FollowedTopicUpdated', ev => this.handleFollowedTopicUpdated(ev));
|
|
33
33
|
this.tracker.client.on('TopicFollowed', ev => this.handleTopicFollowed(ev));
|
|
34
|
+
this.tracker.client.on('TopicUnfollowed', ev => this.handleTopicUnfollowed(ev));
|
|
34
35
|
this.tracker.client.on('NewMessage', ev => this.handleNewMessage(ev));
|
|
35
36
|
this.tracker.client.on('RoomDeleted', ev => this.handleRoomDeleted(ev));
|
|
36
37
|
this.tracker.client.on('RoomLeft', ev => this.handleRoomLeft(ev));
|
|
@@ -218,6 +219,11 @@ export class FollowedTopicsManager extends EventTarget<EventMap> {
|
|
|
218
219
|
this.invalidateUnreadSummaries(ev.followedTopic.location.roomId, ev.followedTopic.location.topicId);
|
|
219
220
|
}
|
|
220
221
|
|
|
222
|
+
private handleTopicUnfollowed(ev: TopicUnfollowed): void {
|
|
223
|
+
this.followedTopics.get(ev.location.roomId)?.delete(ev.location.topicId);
|
|
224
|
+
this.invalidateUnreadSummaries(ev.location.roomId, ev.location.topicId);
|
|
225
|
+
}
|
|
226
|
+
|
|
221
227
|
private handleRoomDeleted(ev: RoomDeleted): void {
|
|
222
228
|
this.clearRoomFollowedTopicsStructures(ev.id);
|
|
223
229
|
}
|