/** * This file is part of Threema Web. * * Threema Web is free software: you can redistribute it and/or modify it * under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero * General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with Threema Web. If not, see . */ import {AsyncEvent} from 'ts-events'; export class StateService { private logTag: string = '[StateService]'; // Angular services private $log: ng.ILogService; private $interval: ng.IIntervalService; // Events public evtConnectionBuildupStateChange = new AsyncEvent(); // WebRTC states public signalingConnectionState: saltyrtc.SignalingState; public rtcConnectionState: threema.RTCConnectionState; // Connection buildup state public connectionBuildupState: threema.ConnectionBuildupState = 'connecting'; public progress = 0; private progressInterval: ng.IPromise = null; public slowConnect = false; // Global connection state public stage: 'signaling' | 'rtc'; public state: threema.GlobalConnectionState; public wasConnected: boolean; public static $inject = ['$log', '$interval']; constructor($log: ng.ILogService, $interval: ng.IIntervalService) { this.$log = $log; this.$interval = $interval; this.reset(); } /** * Signaling connection state. */ public updateSignalingConnectionState(state: saltyrtc.SignalingState): void { const prevState = this.signalingConnectionState; this.signalingConnectionState = state; if (this.stage === 'signaling') { this.$log.debug(this.logTag, 'Signaling connection state:', prevState, '=>', state); switch (state) { case 'new': case 'ws-connecting': case 'server-handshake': case 'peer-handshake': this.state = 'warning'; break; case 'task': this.state = 'warning'; this.stage = 'rtc'; break; case 'closing': case 'closed': this.state = 'error'; break; default: this.$log.warn(this.logTag, 'Ignored signaling connection state change to', state); } } else { this.$log.debug(this.logTag, 'Ignored signaling connection state to "' + state + '"'); } } /** * RTC connection state. */ public updateRtcConnectionState(state: threema.RTCConnectionState): void { const prevState = this.rtcConnectionState; this.rtcConnectionState = state; if (this.stage === 'rtc') { this.$log.debug(this.logTag, 'RTC connection state:', prevState, '=>', state); switch (state) { case 'new': case 'connecting': this.state = 'warning'; break; case 'connected': this.state = 'ok'; this.wasConnected = true; break; case 'disconnected': this.state = 'error'; break; default: this.$log.warn(this.logTag, 'Ignored RTC connection state change to', state); } } else { this.$log.debug(this.logTag, 'Ignored RTC connection state change to "' + state + '"'); } } /** * Connection buildup state. */ public updateConnectionBuildupState(state: threema.ConnectionBuildupState): void { if (this.connectionBuildupState === state) { return; } const prevState = this.connectionBuildupState; this.$log.debug(this.logTag, 'Connection buildup state:', prevState, '=>', state); // Update state this.connectionBuildupState = state; this.evtConnectionBuildupStateChange.post({state: state, prevState: prevState}); // Cancel progress interval if present if (this.progressInterval !== null) { this.$interval.cancel(this.progressInterval); this.progressInterval = null; } // Reset slow connect state this.slowConnect = false; // Update progress switch (state) { case 'new': this.progress = 0; break; case 'push': this.progress = 0; this.progressInterval = this.$interval(() => { if (this.progress < 12) { this.progress += 1; } else { this.slowConnect = true; } }, 800); break; case 'manual_start': this.progress = 0; break; case 'connecting': this.progress = 13; break; case 'waiting': this.progress = 14; break; case 'peer_handshake': this.progress = 15; this.progressInterval = this.$interval(() => { if (this.progress < 40) { this.progress += 5; } else if (this.progress < 55) { this.progress += 3; } else if (this.progress < 60) { this.progress += 1; } else { this.slowConnect = true; } }, 500); break; case 'loading': this.progress = 60; this.progressInterval = this.$interval(() => { if (this.progress < 80) { this.progress += 5; } else if (this.progress < 90) { this.progress += 2; } else if (this.progress < 99) { this.progress += 1; } else { this.slowConnect = true; } }, 500); break; case 'done': this.progress = 100; break; default: this.progress = 0; break; } } /** * Reset all states. */ public reset(): void { this.$log.debug(this.logTag, 'Reset'); // Reset state this.signalingConnectionState = 'new'; this.rtcConnectionState = 'new'; this.stage = 'signaling'; this.state = 'error'; this.wasConnected = false; this.connectionBuildupState = 'connecting'; } }