aboutsummaryrefslogtreecommitdiffstats
path: root/src/features/settingsWS/store.js
blob: 0f1feebb9d5875fd254e15b4cbff561dd8de50ae (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import { observable } from 'mobx';
import WebSocket from 'ws';
import ms from 'ms';

import Store from '../../stores/lib/Store';
import { WS_API } from '../../environment';

const debug = require('debug')('Franz:feature:settingsWS:store');

export class SettingsWSStore extends Store {
  ws = null;

  @observable connected = false;

  pingTimeout = null;

  reconnectTimeout = null;

  constructor(stores, api, actions, state) {
    super(stores, api, actions);
    this.state = state;

    this.registerReactions([
      this._initialize.bind(this),
      this._reconnect.bind(this),
      this._close.bind(this),
    ]);
  }

  connect() {
    try {
      const wsURL = `${WS_API}/ws/${this.stores.user.data.id}`;
      debug('Setting up WebSocket to', wsURL);

      this.ws = new WebSocket(wsURL);

      this.ws.on('open', () => {
        debug('Opened WebSocket');
        this.send({
          action: 'authorize',
          token: this.stores.user.authToken,
        });

        this.connected = true;

        this.heartbeat();
      });

      this.ws.on('message', (data) => {
        const resp = JSON.parse(data);
        debug('Received message', resp);

        if (resp.id) {
          this.stores.user.getUserInfoRequest.patch((result) => {
            if (!result) return;

            debug('Patching user object with new values');
            Object.assign(result, resp);
          });
        }
      });

      this.ws.on('ping', this.heartbeat.bind(this));
    } catch (err) {
      console.err(err);
    }
  }

  heartbeat() {
    debug('Heartbeat');
    clearTimeout(this.pingTimeout);

    this.pingTimeout = setTimeout(() => {
      debug('Terminating connection, reconnecting in 35');
      this.ws.terminate();

      this.connected = false;
    }, ms('35s'));
  }

  send(data) {
    if (this.ws) {
      this.ws.send(JSON.stringify(data));
      debug('Sending data', data);
    } else {
      debug('WebSocket is not initialized');
    }
  }

  // Reactions

  _initialize() {
    if (this.stores.user.data.id) {
      this.connect();
    }
  }

  _reconnect() {
    if (!this.connected) {
      debug('Trying to reconnect in 30s');
      this.reconnectTimeout = setInterval(() => {
        debug('Trying to reconnect');
        this.connect();
      }, ms('30s'));
    } else {
      debug('Clearing reconnect interval');
      clearInterval(this.reconnectTimeout);
    }
  }

  _close() {
    if (!this.stores.user.isLoggedIn && this.ws) {
      debug('Terminating connection');
      this.ws.terminate();
      this.ws = null;
    }
  }
}

export default SettingsWSStore;