polfan-server-js-client 0.1.0 → 0.1.2

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.
@@ -0,0 +1,46 @@
1
+ import {AbstractRestClient} from "./AbstractRestClient";
2
+
3
+ export interface TokenInterface {
4
+ token: string,
5
+ expiration: string
6
+ }
7
+
8
+ export interface MyAccountInterface {
9
+ id: string;
10
+ nick: string;
11
+ avatar: string;
12
+ }
13
+
14
+ export class AuthClient extends AbstractRestClient {
15
+ protected defaultUrl: string = 'https://polfan.pl/webservice/api';
16
+
17
+ public static async createToken(
18
+ login: string,
19
+ password: string,
20
+ clientName: string = 'pserv-js-client'
21
+ ): Promise<TokenInterface> {
22
+ const response = await new AuthClient({token: null}).send('POST', 'auth/tokens', {
23
+ login, password, client_name: clientName
24
+ });
25
+ if (response.ok) {
26
+ return response.data;
27
+ }
28
+ throw new Error(`Cannot create user token: ${response.data.errors[0]}`);
29
+ }
30
+
31
+ public async deleteToken(token: string): Promise<void> {
32
+ const response = await this.send('DELETE', `auth/tokens/${token}`);
33
+ if (!response.ok) {
34
+ throw new Error(`Cannot delete access token: ${response.data.errors[0]}`);
35
+ }
36
+ }
37
+
38
+ public async getMe(): Promise<MyAccountInterface> {
39
+ const response = await this.send('GET', 'auth/me');
40
+ if (response.ok) {
41
+ response.data.id = response.data.id.toString();
42
+ return response.data;
43
+ }
44
+ throw new Error(`Cannot get current user account: ${response.data.errors[0]}`);
45
+ }
46
+ }
@@ -1,4 +1,4 @@
1
- import {WebSocketClient} from "./WebSocketClient";
1
+ import {WebSocketChatClient} from "./WebSocketChatClient";
2
2
  import {IndexedCollection, ObservableIndexedObjectCollection} from "./IndexedObjectCollection";
3
3
  import {
4
4
  Message,
@@ -29,7 +29,7 @@ import {
29
29
 
30
30
  type Deferred = {resolver: () => void, promise: Promise<void>};
31
31
 
32
- export class WebSocketStateTracker {
32
+ export class ChatStateTracker {
33
33
  private readonly joinedSpaces = new ObservableIndexedObjectCollection<Space>('id');
34
34
  private readonly joinedRooms = new ObservableIndexedObjectCollection<Room>('id');
35
35
  private readonly spacesRoles = new IndexedCollection<string, ObservableIndexedObjectCollection<Role>>();
@@ -46,7 +46,8 @@ export class WebSocketStateTracker {
46
46
  private reconnecting: boolean = false;
47
47
  private me: User = null;
48
48
 
49
- public constructor(private readonly client: WebSocketClient) {
49
+ public constructor(private readonly client: WebSocketChatClient) {
50
+ this.createDeferredGetter('session');
50
51
  this.bind();
51
52
  }
52
53
 
@@ -200,6 +201,16 @@ export class WebSocketStateTracker {
200
201
  room.id,
201
202
  new ObservableIndexedObjectCollection<Topic>('id', room.topics)
202
203
  ]));
204
+
205
+ const topicsMessages: [string, ObservableIndexedObjectCollection<Message>][] = [];
206
+ for (const room of rooms) {
207
+ topicsMessages.push(...room.topics.map<[string, ObservableIndexedObjectCollection<Message>]>(topic => [
208
+ topic.id,
209
+ new ObservableIndexedObjectCollection<Message>('id')
210
+ ]));
211
+ }
212
+ this.topicsMessages.set(...topicsMessages);
213
+
203
214
  this.joinedRooms.set(...rooms);
204
215
  }
205
216
 
@@ -235,12 +246,11 @@ export class WebSocketStateTracker {
235
246
  }
236
247
 
237
248
  private handleSession(ev: Session): void {
238
- this.me = ev.user;
239
-
240
249
  if (this.me && !this.reconnecting) {
241
250
  return;
242
251
  }
243
252
 
253
+ this.me = ev.user;
244
254
  this.reconnecting = false;
245
255
 
246
256
  this.joinedRooms.deleteAll();
@@ -4,6 +4,7 @@ type HandlersMap<EventT> = Map<string, EventHandler<EventT>[]>;
4
4
  export interface ObservableInterface<EventT = any> {
5
5
  on(eventName: string, handler: EventHandler<EventT>): this;
6
6
  once(eventName: string, handler: EventHandler<EventT>): this;
7
+ off(eventName: string, handler: EventHandler<EventT>): this;
7
8
  }
8
9
 
9
10
  export class EventTarget<EventT = any> implements ObservableInterface<EventT> {
@@ -20,6 +21,14 @@ export class EventTarget<EventT = any> implements ObservableInterface<EventT> {
20
21
  return this;
21
22
  }
22
23
 
24
+ public off(eventName: string, handler: EventHandler<EventT>): this {
25
+ const index = this.events.get(eventName)?.indexOf(handler);
26
+ if (!index || index < 0) {
27
+ return this;
28
+ }
29
+ this.events.get(eventName).splice(index, 1);
30
+ }
31
+
23
32
  public emit(eventName: string, event?: EventT): this {
24
33
  this.callHandlers(this.events, eventName, event);
25
34
  this.callHandlers(this.onceEvents, eventName, event);
@@ -131,6 +131,12 @@ interface ObservableCollectionEvent<KeyT> {
131
131
  export class ObservableIndexedCollection<KeyT, ValueT> extends IndexedCollection<KeyT, ValueT> implements ObservableInterface {
132
132
  protected eventTarget: EventTarget<ObservableCollectionEvent<KeyT>>;
133
133
 
134
+ public constructor(items: [key: KeyT, value: ValueT][] = []) {
135
+ super();
136
+ this.eventTarget = new EventTarget<ObservableCollectionEvent<KeyT>>();
137
+ this.set(...items);
138
+ }
139
+
134
140
  public set(...items: [KeyT, ValueT][]) {
135
141
  if (items.length) {
136
142
  super.set(...items);
@@ -162,6 +168,11 @@ export class ObservableIndexedCollection<KeyT, ValueT> extends IndexedCollection
162
168
  this.eventTarget.once(eventName, handler);
163
169
  return this;
164
170
  }
171
+
172
+ public off(eventName: string, handler: (ev?: ObservableCollectionEvent<KeyT>) => void): this {
173
+ this.eventTarget.off(eventName, handler);
174
+ return this;
175
+ }
165
176
  }
166
177
 
167
178
  export class ObservableIndexedObjectCollection<T> extends IndexedObjectCollection<T> implements ObservableInterface {
@@ -171,8 +182,9 @@ export class ObservableIndexedObjectCollection<T> extends IndexedObjectCollectio
171
182
  public readonly id: keyof T | ((item: T) => string),
172
183
  items: T[] = [],
173
184
  ) {
174
- super(id, items);
185
+ super(id);
175
186
  this.eventTarget = new EventTarget();
187
+ this.set(...items);
176
188
  }
177
189
 
178
190
  public set(...items: T[]) {
@@ -206,4 +218,9 @@ export class ObservableIndexedObjectCollection<T> extends IndexedObjectCollectio
206
218
  this.eventTarget.once(eventName, handler);
207
219
  return this;
208
220
  }
221
+
222
+ public off(eventName: string, handler: (ev?: ObservableCollectionEvent<string>) => void): this {
223
+ this.eventTarget.off(eventName, handler);
224
+ return this;
225
+ }
209
226
  }
@@ -1,8 +1,8 @@
1
- import {AbstractClient, CommandResult, CommandsMap} from "./AbstractClient";
1
+ import {AbstractChatClient, CommandResult, CommandsMap} from "./AbstractChatClient";
2
2
  import {ObservableInterface} from "./EventTarget";
3
3
  import {Envelope} from "pserv-ts-types";
4
4
 
5
- export interface WebApiClientOptions {
5
+ export interface WebApiChatClientOptions {
6
6
  url: string;
7
7
  token?: string;
8
8
  temporaryNick?: string;
@@ -10,18 +10,18 @@ export interface WebApiClientOptions {
10
10
  attemptDelayMs?: number;
11
11
  }
12
12
 
13
- enum WebApiClientEvent {
13
+ enum WebApiChatClientEvent {
14
14
  message = 'message',
15
15
  error = 'error',
16
16
  destroy = 'destroy',
17
17
  }
18
18
 
19
- export class WebApiClient extends AbstractClient implements ObservableInterface {
20
- public readonly Event = WebApiClientEvent;
19
+ export class WebApiChatClient extends AbstractChatClient implements ObservableInterface {
20
+ public readonly Event = WebApiChatClientEvent;
21
21
 
22
22
  protected sendStack: {data: any, attempts: number, lastTimeoutId: any}[];
23
23
 
24
- public constructor(private readonly options: WebApiClientOptions) {
24
+ public constructor(private readonly options: WebApiChatClientOptions) {
25
25
  super();
26
26
  if (!this.options.token && !this.options.temporaryNick) {
27
27
  throw new Error('Token or temporary nick is required');
@@ -52,7 +52,7 @@ export class WebApiClient extends AbstractClient implements ObservableInterface
52
52
  this.sendStack.splice(reqId, 1);
53
53
  const envelope: Envelope = await response.json();
54
54
  this.handleIncomingEnvelope(envelope);
55
- this.emit(envelope.type, envelope);
55
+ this.emit(envelope.type, envelope.data);
56
56
  this.emit(this.Event.message, envelope);
57
57
  }
58
58
 
@@ -1,7 +1,7 @@
1
1
  import {ObservableInterface} from "./EventTarget";
2
- import {AbstractClient, CommandResult, CommandsMap} from "./AbstractClient";
2
+ import {AbstractChatClient, CommandResult, CommandsMap} from "./AbstractChatClient";
3
3
  import {Envelope} from "pserv-ts-types";
4
- import {WebSocketStateTracker} from "./WebSocketStateTracker";
4
+ import {ChatStateTracker} from "./ChatStateTracker";
5
5
 
6
6
  export interface WebSocketClientOptions {
7
7
  url: string;
@@ -12,16 +12,16 @@ export interface WebSocketClientOptions {
12
12
  stateTracking?: boolean;
13
13
  }
14
14
 
15
- enum WebSocketClientEvent {
15
+ enum WebSocketChatClientEvent {
16
16
  connect = 'connect',
17
17
  disconnect = 'disconnect',
18
18
  message = 'message',
19
19
  error = 'error',
20
20
  }
21
21
 
22
- export class WebSocketClient extends AbstractClient implements ObservableInterface {
23
- public readonly Event = WebSocketClientEvent;
24
- public readonly state?: WebSocketStateTracker;
22
+ export class WebSocketChatClient extends AbstractChatClient implements ObservableInterface {
23
+ public readonly Event = WebSocketChatClientEvent;
24
+ public readonly state?: ChatStateTracker;
25
25
 
26
26
  protected ws: WebSocket|null = null;
27
27
  protected sendQueue: [commandType: keyof CommandsMap, commandData: any][] = [];
@@ -35,7 +35,7 @@ export class WebSocketClient extends AbstractClient implements ObservableInterfa
35
35
  throw new Error('Token or temporary nick is required');
36
36
  }
37
37
  if (this.options.stateTracking ?? true) {
38
- this.state = new WebSocketStateTracker(this);
38
+ this.state = new ChatStateTracker(this);
39
39
  }
40
40
  }
41
41
 
@@ -80,6 +80,10 @@ export class WebSocketClient extends AbstractClient implements ObservableInterfa
80
80
 
81
81
  private onMessage(event: MessageEvent): void {
82
82
  const envelope: Envelope = JSON.parse(event.data);
83
+ this.handleIncomingEnvelope(envelope);
84
+ this.emit(envelope.type, envelope.data);
85
+ this.emit(this.Event.message, envelope);
86
+
83
87
  // Login successfully
84
88
  if (!this.authenticated) {
85
89
  const isAuthenticated = envelope.type !== 'Error';
@@ -92,9 +96,6 @@ export class WebSocketClient extends AbstractClient implements ObservableInterfa
92
96
  this.authenticatedResolvers[1](envelope.data);
93
97
  }
94
98
  }
95
- this.handleIncomingEnvelope(envelope);
96
- this.emit(envelope.type, envelope);
97
- this.emit(this.Event.message, envelope);
98
99
  }
99
100
 
100
101
  private onClose(event: CloseEvent): void {
package/src/index.ts CHANGED
@@ -1,14 +1,16 @@
1
- import {WebSocketClient} from "./WebSocketClient";
2
- import {WebApiClient} from "./WebApiClient";
1
+ import {WebSocketChatClient} from "./WebSocketChatClient";
2
+ import {WebApiChatClient} from "./WebApiChatClient";
3
3
  import {
4
4
  IndexedCollection,
5
5
  IndexedObjectCollection,
6
6
  ObservableIndexedCollection,
7
7
  ObservableIndexedObjectCollection
8
8
  } from "./IndexedObjectCollection";
9
+ import { AuthClient } from "./AuthClient";
9
10
 
10
11
  export {
11
12
  IndexedCollection, ObservableIndexedCollection,
12
13
  IndexedObjectCollection, ObservableIndexedObjectCollection,
13
- WebSocketClient, WebApiClient,
14
+ WebSocketChatClient, WebApiChatClient,
15
+ AuthClient
14
16
  };
package/webpack.config.js CHANGED
@@ -33,7 +33,7 @@ module.exports = {
33
33
  clean: true
34
34
  },
35
35
  optimization: {
36
- minimize: true,
36
+ minimize: false,
37
37
  minimizer: [
38
38
  new TerserPlugin({ extractComments: false }),
39
39
  //new CssMinimizerPlugin()