state.ts 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. /**
  2. * This file is part of Threema Web.
  3. *
  4. * Threema Web is free software: you can redistribute it and/or modify it
  5. * under the terms of the GNU Affero General Public License as published by
  6. * the Free Software Foundation, either version 3 of the License, or (at
  7. * your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful, but
  10. * WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
  12. * General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU Affero General Public License
  15. * along with Threema Web. If not, see <http://www.gnu.org/licenses/>.
  16. */
  17. import {AsyncEvent} from 'ts-events';
  18. export class StateService {
  19. private logTag: string = '[StateService]';
  20. // Angular services
  21. private $log: ng.ILogService;
  22. private $interval: ng.IIntervalService;
  23. // Events
  24. public evtConnectionBuildupStateChange = new AsyncEvent<threema.ConnectionBuildupStateChange>();
  25. // WebRTC states
  26. public signalingConnectionState: saltyrtc.SignalingState;
  27. public rtcConnectionState: threema.RTCConnectionState;
  28. // Connection buildup state
  29. public connectionBuildupState: threema.ConnectionBuildupState = 'connecting';
  30. public progress = 0;
  31. private progressInterval: ng.IPromise<any> = null;
  32. public slowConnect = false;
  33. // Global connection state
  34. public stage: 'signaling' | 'rtc';
  35. public state: threema.GlobalConnectionState;
  36. public wasConnected: boolean;
  37. public static $inject = ['$log', '$interval'];
  38. constructor($log: ng.ILogService, $interval: ng.IIntervalService) {
  39. this.$log = $log;
  40. this.$interval = $interval;
  41. this.reset();
  42. }
  43. /**
  44. * Signaling connection state.
  45. */
  46. public updateSignalingConnectionState(state: saltyrtc.SignalingState): void {
  47. const prevState = this.signalingConnectionState;
  48. this.signalingConnectionState = state;
  49. if (this.stage === 'signaling') {
  50. this.$log.debug(this.logTag, 'Signaling connection state:', prevState, '=>', state);
  51. switch (state) {
  52. case 'new':
  53. case 'ws-connecting':
  54. case 'server-handshake':
  55. case 'peer-handshake':
  56. this.state = 'warning';
  57. break;
  58. case 'task':
  59. this.state = 'warning';
  60. this.stage = 'rtc';
  61. break;
  62. case 'closing':
  63. case 'closed':
  64. this.state = 'error';
  65. break;
  66. default:
  67. this.$log.warn(this.logTag, 'Ignored signaling connection state change to', state);
  68. }
  69. } else {
  70. this.$log.debug(this.logTag, 'Ignored signaling connection state to "' + state + '"');
  71. }
  72. }
  73. /**
  74. * RTC connection state.
  75. */
  76. public updateRtcConnectionState(state: threema.RTCConnectionState): void {
  77. const prevState = this.rtcConnectionState;
  78. this.rtcConnectionState = state;
  79. if (this.stage === 'rtc') {
  80. this.$log.debug(this.logTag, 'RTC connection state:', prevState, '=>', state);
  81. switch (state) {
  82. case 'new':
  83. case 'connecting':
  84. this.state = 'warning';
  85. break;
  86. case 'connected':
  87. this.state = 'ok';
  88. this.wasConnected = true;
  89. break;
  90. case 'disconnected':
  91. this.state = 'error';
  92. break;
  93. default:
  94. this.$log.warn(this.logTag, 'Ignored RTC connection state change to', state);
  95. }
  96. } else {
  97. this.$log.debug(this.logTag, 'Ignored RTC connection state change to "' + state + '"');
  98. }
  99. }
  100. /**
  101. * Connection buildup state.
  102. */
  103. public updateConnectionBuildupState(state: threema.ConnectionBuildupState): void {
  104. if (this.connectionBuildupState === state) {
  105. return;
  106. }
  107. const prevState = this.connectionBuildupState;
  108. this.$log.debug(this.logTag, 'Connection buildup state:', prevState, '=>', state);
  109. // Update state
  110. this.connectionBuildupState = state;
  111. this.evtConnectionBuildupStateChange.post({state: state, prevState: prevState});
  112. // Cancel progress interval if present
  113. if (this.progressInterval !== null) {
  114. this.$interval.cancel(this.progressInterval);
  115. this.progressInterval = null;
  116. }
  117. // Reset slow connect state
  118. this.slowConnect = false;
  119. // Update progress
  120. switch (state) {
  121. case 'new':
  122. this.progress = 0;
  123. break;
  124. case 'push':
  125. this.progress = 0;
  126. this.progressInterval = this.$interval(() => {
  127. if (this.progress < 12) {
  128. this.progress += 1;
  129. } else {
  130. this.slowConnect = true;
  131. }
  132. }, 800);
  133. break;
  134. case 'manual_start':
  135. this.progress = 0;
  136. break;
  137. case 'connecting':
  138. this.progress = 13;
  139. break;
  140. case 'waiting':
  141. this.progress = 14;
  142. break;
  143. case 'peer_handshake':
  144. this.progress = 15;
  145. this.progressInterval = this.$interval(() => {
  146. if (this.progress < 40) {
  147. this.progress += 5;
  148. } else if (this.progress < 55) {
  149. this.progress += 3;
  150. } else if (this.progress < 60) {
  151. this.progress += 1;
  152. } else {
  153. this.slowConnect = true;
  154. }
  155. }, 500);
  156. break;
  157. case 'loading':
  158. this.progress = 60;
  159. this.progressInterval = this.$interval(() => {
  160. if (this.progress < 80) {
  161. this.progress += 5;
  162. } else if (this.progress < 90) {
  163. this.progress += 2;
  164. } else if (this.progress < 99) {
  165. this.progress += 1;
  166. } else {
  167. this.slowConnect = true;
  168. }
  169. }, 500);
  170. break;
  171. case 'done':
  172. this.progress = 100;
  173. break;
  174. default:
  175. this.progress = 0;
  176. break;
  177. }
  178. }
  179. /**
  180. * Reset all states.
  181. */
  182. public reset(): void {
  183. this.$log.debug(this.logTag, 'Reset');
  184. // Reset state
  185. this.signalingConnectionState = 'new';
  186. this.rtcConnectionState = 'new';
  187. this.stage = 'signaling';
  188. this.state = 'error';
  189. this.wasConnected = false;
  190. this.connectionBuildupState = 'connecting';
  191. }
  192. }