Просмотр исходного кода

Merge pull request #226 from threema-ch/issue-183

Version checking

Fixes #183
Danilo Bargen 8 лет назад
Родитель
Сommit
f3a707f4c5

+ 1 - 1
dist/package.sh

@@ -83,7 +83,7 @@ for target in "${targets[@]}"; do
 done
 
 echo "+ Update version number..."
-sed -i "s/\[\[VERSION\]\]/${VERSION}/g" $DIR/index.html $DIR/troubleshoot/index.html $DIR/dist/app.js
+sed -i "s/\[\[VERSION\]\]/${VERSION}/g" $DIR/index.html $DIR/troubleshoot/index.html $DIR/dist/app.js $DIR/version.txt
 
 echo "+ Update permissions..."
 find $DIR/ -type f -exec chmod 644 {} \;

+ 3 - 0
karma.conf.js

@@ -6,6 +6,9 @@ module.exports = function(config) {
             'node_modules/angular/angular.js',
             'node_modules/angular-mocks/angular-mocks.js',
             'node_modules/angular-translate/dist/angular-translate.min.js',
+            'node_modules/angular-aria/angular-aria.min.js',
+            'node_modules/angular-animate/angular-animate.min.js',
+            'node_modules/angular-material/angular-material.min.js',
             'dist/app.js',
             'tests/filters.js',
             'tests/service/message.js',

+ 4 - 0
public/i18n/de.json

@@ -217,5 +217,9 @@
             "SHOW_PREVIEW": "Nachrichteninhalt in Benachrichtigungen anzeigen",
             "PLAY_SOUND": "Ton abspielen"
         }
+    },
+    "version": {
+        "NEW_VERSION": "Neue Version Verfügbar",
+        "NEW_VERSION_BODY": "Eine neue Version von Threema Web ({version}) ist verfügbar. Drücken Sie \"OK\" um die Seite neu zu laden."
     }
 }

+ 5 - 1
public/i18n/en.json

@@ -212,12 +212,16 @@
         "CHANGELOG_LINK_AFTER": "."
     },
     "settings": {
-        "SETTINGS":"Settings",
+        "SETTINGS": "Settings",
         "notifications": {
             "NOTIFICATIONS": "Notifications",
             "SHOW_NOTIFICATIONS": "Show desktop notifications",
             "SHOW_PREVIEW": "Show message contents in notifications",
             "PLAY_SOUND": "Play sound"
         }
+    },
+    "version": {
+        "NEW_VERSION": "New Version Available",
+        "NEW_VERSION_BODY": "A new version of Threema Web ({version}) is available. Click \"OK\" to reload the page."
     }
 }

+ 1 - 0
public/version.txt

@@ -0,0 +1 @@
+[[VERSION]]

+ 8 - 3
src/partials/messenger.ts

@@ -28,6 +28,7 @@ import {NotificationService} from '../services/notification';
 import {ReceiverService} from '../services/receiver';
 import {SettingsService} from '../services/settings';
 import {StateService} from '../services/state';
+import {VersionService} from '../services/version';
 import {WebClientService} from '../services/webclient';
 import {ControllerModelMode} from '../types/enums';
 
@@ -208,7 +209,7 @@ class ConversationController {
     public static $inject = [
         '$stateParams', '$state', '$timeout', '$log', '$scope', '$rootScope',
         '$mdDialog', '$mdToast', '$location', '$translate',
-        'WebClientService', 'StateService', 'ReceiverService', 'MimeService',
+        'WebClientService', 'StateService', 'ReceiverService', 'MimeService', 'VersionService',
     ];
     constructor($stateParams: threema.ConversationStateParams,
                 $state: ng.ui.IStateService,
@@ -223,7 +224,8 @@ class ConversationController {
                 webClientService: WebClientService,
                 stateService: StateService,
                 receiverService: ReceiverService,
-                mimeService: MimeService) {
+                mimeService: MimeService,
+                versionService: VersionService) {
         this.$stateParams = $stateParams;
         this.$timeout = $timeout;
         this.$log = $log;
@@ -249,6 +251,9 @@ class ConversationController {
         // replace with transition hooks.
         $rootScope.$on('$stateChangeStart', () => this.$mdDialog.cancel());
 
+        // Check for version updates
+        versionService.checkForUpdate();
+
         // Redirect to welcome if necessary
         if (stateService.state === 'error') {
             $log.debug('ConversationController: WebClient not yet running, redirecting to welcome screen');
@@ -909,7 +914,7 @@ class ReceiverDetailController {
 
         this.receiver = webClientService.receivers.getData($stateParams);
 
-        // append members
+        // Append members
         if (this.receiver.type === 'contact') {
             let contactReceiver = (<threema.ContactReceiver> this.receiver);
 

+ 6 - 1
src/partials/welcome.ts

@@ -20,6 +20,7 @@ import {ControllerService} from '../services/controller';
 import {TrustedKeyStoreService} from '../services/keystore';
 import {PushService} from '../services/push';
 import {StateService} from '../services/state';
+import {VersionService} from '../services/version';
 import {WebClientService} from '../services/webclient';
 
 class DialogController {
@@ -68,7 +69,7 @@ class WelcomeController {
 
     public static $inject = [
         '$scope', '$state', '$stateParams', '$timeout', '$interval', '$log', '$window', '$mdDialog', '$translate',
-        'WebClientService', 'TrustedKeyStore', 'StateService', 'PushService', 'BrowserService',
+        'WebClientService', 'TrustedKeyStore', 'StateService', 'PushService', 'BrowserService', 'VersionService',
         'BROWSER_MIN_VERSIONS', 'ControllerService',
     ];
     constructor($scope: ng.IScope, $state: ng.ui.IStateService, $stateParams: threema.WelcomeStateParams,
@@ -78,6 +79,7 @@ class WelcomeController {
                 webClientService: WebClientService, TrustedKeyStore: TrustedKeyStoreService,
                 stateService: StateService, pushService: PushService,
                 browserService: BrowserService,
+                versionService: VersionService,
                 minVersions: threema.BrowserMinVersions,
                 controllerService: ControllerService) {
         controllerService.setControllerName('welcome');
@@ -130,6 +132,9 @@ class WelcomeController {
             this.showLocalStorageWarning();
         }
 
+        // Determine current version
+        versionService.initVersion();
+
         // Clear cache
         this.webClientService.clearCache();
 

+ 2 - 0
src/services.ts

@@ -33,6 +33,7 @@ import {StateService} from './services/state';
 import {StringService} from './services/string';
 import {TitleService} from './services/title';
 import {UriService} from './services/uri';
+import {VersionService} from './services/version';
 import {WebClientService} from './services/webclient';
 
 // Create services for the controller
@@ -58,4 +59,5 @@ angular.module('3ema.services', [])
 .service('SettingsService', SettingsService)
 .service('MediaboxService', MediaboxService)
 .service('UriService', UriService)
+.service('VersionService', VersionService)
 ;

+ 128 - 0
src/services/version.ts

@@ -0,0 +1,128 @@
+/**
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+export class VersionService {
+    public static $inject = ['$log', '$http', '$mdDialog', '$translate', '$window'];
+
+    private logTag: string = '[VersionService]';
+
+    private $log: ng.ILogService;
+    private $http: ng.IHttpService;
+    private $mdDialog: ng.material.IDialogService;
+    private $translate: ng.translate.ITranslateService;
+    private $window: ng.IWindowService;
+
+    private version: string;
+
+    constructor($log: ng.ILogService,
+                $http: ng.IHttpService,
+                $mdDialog: ng.material.IDialogService,
+                $translate: ng.translate.ITranslateService,
+                $window: ng.IWindowService) {
+        this.$log = $log;
+        this.$http = $http;
+        this.$mdDialog = $mdDialog;
+        this.$translate = $translate;
+        this.$window = $window;
+    }
+
+    /**
+     * Set the version by fetching the version.txt file.
+     */
+    public initVersion(): void {
+        if (this.version !== undefined) {
+            this.checkForUpdate();
+            return;
+        }
+
+        this.fetchVersion()
+            .then((version: string) => {
+                this.version = version;
+                this.$log.info(this.logTag, 'Using Threema Web version', this.version);
+            })
+            .catch((error: string) => {
+                this.$log.error(this.logTag, 'Could not fetch version.txt:', error);
+            });
+    }
+
+    /**
+     * Fetch the version.txt file.
+     */
+    private fetchVersion(): Promise<string> {
+        return new Promise((resolve, reject) => {
+            const cacheBust = Math.floor(Math.random() * 1000000000);
+            this.$http({
+                method: 'GET',
+                url: 'version.txt?' + cacheBust,
+                cache: false,
+                responseType: 'text',
+                transformResponse: (data: string, headersGetter, statusCode) => {
+                    if (statusCode !== 200) {
+                        return reject('HTTP ' + statusCode);
+                    }
+                    return data.trim();
+                },
+            }).then(
+                (successResponse: ng.IHttpPromiseCallbackArg<string>) => {
+                    return resolve(successResponse.data);
+                },
+                (error: Error) => {
+                    return reject(error);
+                },
+            );
+        });
+    }
+
+    /**
+     * Check for a version update. If the version was updated, show a dialog.
+     */
+    public checkForUpdate(): void {
+        this.$log.debug(this.logTag, 'Checking for version update...');
+        if (this.version === undefined) {
+            this.$log.error(this.logTag, 'Cannot check for update, version is not initialized');
+            return;
+        }
+        this.fetchVersion()
+            .then((version: string) => {
+                if (version !== this.version) {
+                    this.$log.warn(this.logTag,
+                        'A new version of Threema Web is available:',
+                        this.version, '->', version);
+                    this.notifyNewVersion(version);
+                }
+            })
+            .catch((error: string) => {
+                this.$log.error('Could not fetch version.txt:', error);
+            });
+    }
+
+    /**
+     * A new version is available!
+     */
+    private notifyNewVersion(version: string): void {
+        const confirm = this.$mdDialog.alert()
+            .title(this.$translate.instant('version.NEW_VERSION', {version: version}))
+            .textContent(this.$translate.instant('version.NEW_VERSION_BODY', {version: version}))
+            .ok(this.$translate.instant('common.OK'));
+        this.$mdDialog.show(confirm).then(() => {
+            this.$window.location.reload();
+        }, () => {
+            this.$window.location.reload();
+        });
+    }
+
+}

+ 13 - 4
src/services/webclient.ts

@@ -29,6 +29,7 @@ import {QrCodeService} from './qrcode';
 import {ReceiverService} from './receiver';
 import {StateService} from './state';
 import {TitleService} from './title';
+import {VersionService} from './version';
 
 class WebClientDefault {
     private avatar: threema.AvatarRegistry = {
@@ -143,6 +144,7 @@ export class WebClientService {
     private qrCodeService: QrCodeService;
     private mimeService: MimeService;
     private receiverService: ReceiverService;
+    private versionService: VersionService;
 
     // State handling
     private startupPromise: ng.IDeferred<{}> = null; // TODO: deferred type
@@ -175,6 +177,7 @@ export class WebClientService {
     private pcHelper: PeerConnectionHelper = null;
     private deviceInfo: string = null;
     private trustedKeyStore: TrustedKeyStoreService;
+    public version = null;
 
     private blobCache = new Map<String, ArrayBuffer>();
     private loadingMessages = new Map<String, boolean>();
@@ -194,6 +197,7 @@ export class WebClientService {
         'Container', 'TrustedKeyStore',
         'StateService', 'NotificationService', 'MessageService', 'PushService', 'BrowserService',
         'TitleService', 'FingerPrintService', 'QrCodeService', 'MimeService', 'ReceiverService',
+        'VersionService',
         'CONFIG',
     ];
     constructor($log: ng.ILogService,
@@ -216,6 +220,7 @@ export class WebClientService {
                 qrCodeService: QrCodeService,
                 mimeService: MimeService,
                 receiverService: ReceiverService,
+                VersionService: VersionService,
                 CONFIG: threema.Config) {
 
         // Angular services
@@ -238,6 +243,7 @@ export class WebClientService {
         this.qrCodeService = qrCodeService;
         this.mimeService = mimeService;
         this.receiverService = receiverService;
+        this.versionService = VersionService;
 
         // Configuration object
         this.config = CONFIG;
@@ -423,18 +429,18 @@ export class WebClientService {
 
         // Wait for handover to be finished
         this.salty.on('handover', () => {
-            this.$log.debug('Handover done');
+            this.$log.debug(this.logTag, 'Handover done');
 
             // Initialize NotificationService
-            this.$log.debug('Initializing NotificationService...');
+            this.$log.debug(this.logTag, 'Initializing NotificationService...');
             this.notificationService.init();
 
             // Create secure data channel
-            this.$log.debug('Create SecureDataChannel "' + WebClientService.DC_LABEL + '"...');
+            this.$log.debug(this.logTag, 'Create SecureDataChannel "' + WebClientService.DC_LABEL + '"...');
             this.secureDataChannel = this.pcHelper.createSecureDataChannel(
                 WebClientService.DC_LABEL,
                 (event: Event) => {
-                    this.$log.debug('SecureDataChannel open');
+                    this.$log.debug(this.logTag, 'SecureDataChannel open');
 
                     // Initialize fields
                     if (resetFields) {
@@ -444,6 +450,9 @@ export class WebClientService {
                     // Request initial data
                     this._requestInitialData();
 
+                    // Fetch current version
+                    this.versionService.checkForUpdate();
+
                     // Notify state service about data loading
                     this.stateService.updateConnectionBuildupState('loading');
                 },

+ 3 - 0
tests/service/webclient.js

@@ -27,6 +27,9 @@ describe('WebClientService', function() {
         });
 
         // Load modules
+        module('ngAria');
+        module('ngAnimate');
+        module('ngMaterial');
         module('3ema.services');
         module('3ema.container');
 

+ 3 - 0
tests/testsuite.html

@@ -14,6 +14,9 @@
         <script src="../node_modules/angular/angular.js"></script>
         <script src="../node_modules/angular-mocks/angular-mocks.js"></script>
         <script src="../node_modules/angular-translate/dist/angular-translate.min.js"></script>
+        <script src="../node_modules/angular-material/angular-material.min.js"></script>
+        <script src="../node_modules/angular-animate/angular-animate.min.js"></script>
+        <script src="../node_modules/angular-aria/angular-aria.min.js"></script>
 
         <script src="../dist/app.js"></script>