Parcourir la source

Merge pull request #904 from threema-ch/ci-updates

CI updates
Danilo Bargen il y a 5 ans
Parent
commit
524b6af51d

+ 2 - 0
.sass-lint.yml

@@ -31,6 +31,8 @@ rules:
   property-sort-order:
     - 2
     - order: concentric
+  force-attribute-nesting:
+    - 0
 
 # Auto-Fix Configuration
 resolvers:

+ 2 - 2
index.html

@@ -56,7 +56,7 @@
     <link rel="stylesheet" href="css/app.css?v=[[VERSION]]">
 </head>
 
-<body class="{{ ctrl.statusClass }}" ng-class="{expanded: ctrl.expandStatusBar}">
+<body ng-controller="ThemeController as themeCtrl" class="{{ ctrl.statusClass }}" ng-class="{expanded: ctrl.expandStatusBar}" md-theme="{{ themeCtrl.theme }}">
     <img src="img/bg.jpg?v=[[VERSION]]" aria-label="Background image: Blurred photo of a mountain" id="background-image" draggable="false">
 
     <noscript class="compat compat-nojs">
@@ -101,7 +101,7 @@
         </div>
         <footer ng-controller="FooterController as ctrl">
             <ul translate-cloak>
-                <li><a ng-click="ctrl.showVersionInfo('[[VERSION]]')" ng-keypress="ctrl.showVersionInfo('[[VERSION]]', $event)" tabindex="0">Version [[VERSION]] {{ ctrl.config.VERSION_MOUNTAIN }}</a></li>
+                <li><a ng-click="ctrl.showVersionInfo()" ng-keypress="ctrl.showVersionInfo($event)" tabindex="0">Version [[VERSION]] {{ ctrl.config.VERSION_MOUNTAIN }}</a></li>
                 <li><a href="https://threema.ch/threema-web" target="_blank" rel="noopener noreferrer" tabindex="0" translate>welcome.MORE_ABOUT_WEB</a></li>
                 <li><a ng-click="ctrl.showTroubleshooting()" ng-keypress="ctrl.showTroubleshooting($event)" translate>troubleshooting.TROUBLESHOOTING</a></li>
                 <li><a href="https://github.com/threema-ch/threema-web/blob/master/TRANSLATING.md" target="_blank" rel="noopener noreferrer" tabindex="0" translate>welcome.HELP_TRANSLATE</a></li>

Fichier diff supprimé car celui-ci est trop grand
+ 4 - 17
public/img/logo.svg


+ 44 - 8
src/app.ts

@@ -110,14 +110,50 @@ angular.module('3ema', [
 }])
 
 // Configure theme
-.config(['$mdThemingProvider', ($mdThemingProvider) => {
-    $mdThemingProvider.theme('default')
-        .primaryPalette('grey', {
-             default: '800',
-        })
-        .accentPalette('teal', {
-            default: '500',
-        });
+.config(['$mdThemingProvider', ($mdThemingProvider: ng.material.IThemingProvider) => {
+    $mdThemingProvider.definePalette('threema', {
+        50: 'f2faf5',
+        100: 'e6f6eb',
+        200: 'cdedd9',
+        300: 'b4e4c5',
+        400: '9bdbb2',
+        500: '82d29f',
+        600: '69ca8c',
+        700: '50c078',
+        800: '37b865',
+        900: '1eae52',
+        A100: '05a63f',
+        A200: '048432',
+        A400: '03732b',
+        A700: '036325',
+        contrastDefaultColor: 'light',
+        contrastDarkColors: ['50', '100', '200', '300', '400', '500', '600'],
+    });
+    $mdThemingProvider.definePalette('threemawork', {
+        50: '#f2f9ff',
+        100: 'e5f4ff',
+        200: 'cceaff',
+        300: 'b2dfff',
+        400: '99d5ff',
+        500: '7fcaff',
+        600: '66c0ff',
+        700: '4cb5ff',
+        800: '33abff',
+        900: '19a0ff',
+        A100: '0096ff',
+        A200: '0086e5',
+        A400: '0068b2',
+        A700: '004a7f',
+        contrastDefaultColor: 'light',
+        contrastDarkColors: ['50', '100', '200', '300', '400', '500', '600'],
+    });
+    $mdThemingProvider.theme('threema')
+        .primaryPalette('grey', {default: '800'})
+        .accentPalette('threema', {default: 'A100'});
+    $mdThemingProvider.theme('threemawork')
+        .primaryPalette('grey', {default: '800'})
+        .accentPalette('threemawork', {default: 'A100'});
+    $mdThemingProvider.alwaysWatchTheme(true);
 }])
 
 // Optimizations: https://docs.angularjs.org/guide/production

+ 2 - 0
src/config.ts

@@ -73,5 +73,7 @@ export default {
     MSGPACK_LOG_TRACE: false,
     // Transport log level
     TRANSPORT_LOG_LEVEL: 'warn',
+    // Always show the real connection state using the dot in the logo
+    VISUALIZE_STATE: true,
 
 } as threema.Config;

+ 2 - 0
src/controllers.ts

@@ -17,10 +17,12 @@
 
 import {FooterController} from './controllers/footer';
 import {StatusController} from './controllers/status';
+import {ThemeController} from './controllers/theme';
 
 angular.module('3ema.controllers', ['3ema.services'])
 
 .controller('FooterController', FooterController)
 .controller('StatusController', StatusController)
+.controller('ThemeController', ThemeController)
 
 ;

+ 19 - 2
src/controllers/dialog.ts

@@ -15,6 +15,8 @@
  * along with Threema Web. If not, see <http://www.gnu.org/licenses/>.
  */
 
+import {ThemeService} from '../services/theme';
+
 /**
  * A general purpose dialog controller.
  */
@@ -22,10 +24,25 @@ export class DialogController {
     public readonly $mdDialog: ng.material.IDialogService;
     public readonly activeElement: HTMLElement | null;
 
-    public static readonly $inject = ['$mdDialog'];
-    constructor($mdDialog: ng.material.IDialogService, activeElement?: HTMLElement) {
+    public theme: string;
+
+    public static readonly $inject = ['$scope', '$mdDialog', 'ThemeService'];
+    constructor(
+        $scope: ng.IScope,
+        $mdDialog: ng.material.IDialogService,
+        themeService: ThemeService,
+        activeElement?: HTMLElement,
+    ) {
         this.$mdDialog = $mdDialog;
         this.activeElement = activeElement !== undefined ? activeElement : document.activeElement as HTMLElement;
+
+        // Unfortunately md-dialog does not properly update when the root theme is changed.
+        // This means that we have to listen to theme changes manually and
+        // update the md-theme attribute on the dialog template.
+        this.theme = themeService.theme;
+        themeService.evtThemeChange.attach(
+            (newTheme: threema.Theme) => $scope.$apply(() => this.theme = newTheme),
+        );
     }
 
     /**

+ 24 - 17
src/controllers/footer.ts

@@ -16,8 +16,30 @@
  */
 
 import {isActionTrigger} from '../helpers';
+import {ThemeService} from '../services/theme';
+import {DialogController} from './dialog';
 import {TroubleshootingController} from './troubleshooting';
 
+class VersionDialogController extends DialogController {
+    public readonly version: string;
+    public readonly fullVersion: string;
+
+    public readonly config: threema.Config;
+
+    public static readonly $inject = ['$scope', '$mdDialog', 'ThemeService', 'CONFIG'];
+    constructor(
+        $scope: ng.IScope,
+        $mdDialog: ng.material.IDialogService,
+        themeService: ThemeService,
+        config: threema.Config,
+    ) {
+        super($scope, $mdDialog, themeService);
+        this.version = config.VERSION;
+        this.fullVersion = `${config.VERSION} ${config.VERSION_MOUNTAIN}`;
+        this.config = config;
+    }
+}
+
 /**
  * Handle footer information.
  */
@@ -32,27 +54,12 @@ export class FooterController {
         this.config = CONFIG;
     }
 
-    public showVersionInfo(version: string, ev?: KeyboardEvent): void {
+    public showVersionInfo(ev?: KeyboardEvent): void {
         if (ev !== undefined && !isActionTrigger(ev)) {
             return;
         }
         this.$mdDialog.show({
-            controller: [
-                '$mdDialog',
-                'CONFIG',
-                function($mdDialog: ng.material.IDialogService, CONFIG: threema.Config) {
-                    this.activeElement = null;
-                    this.version = version;
-                    this.fullVersion = `${version} ${CONFIG.VERSION_MOUNTAIN}`;
-                    this.config = CONFIG;
-                    this.cancel = () => {
-                        $mdDialog.cancel();
-                        if (this.activeElement !== null) {
-                            this.activeElement.focus(); // reset focus
-                        }
-                    };
-                },
-            ],
+            controller: VersionDialogController,
             controllerAs: 'ctrl',
             templateUrl: 'partials/dialog.version.html',
             parent: angular.element(document.body),

+ 16 - 1
src/controllers/status.ts

@@ -39,6 +39,9 @@ export class StatusController {
     // Logging
     private readonly log: Logger;
 
+    // Config
+    private config: threema.Config;
+
     // State variable
     private state = GlobalConnectionState.Error;
     private unreadCount = 0;
@@ -63,15 +66,20 @@ export class StatusController {
 
     public static $inject = [
         '$scope', '$timeout', '$state',
+        'CONFIG',
         'ControllerService', 'StateService', 'LogService', 'TimeoutService', 'WebClientService',
     ];
     constructor($scope, $timeout: ng.ITimeoutService, $state: UiStateService,
+                config: threema.Config,
                 controllerService: ControllerService, stateService: StateService, logService: LogService,
                 timeoutService: TimeoutService, webClientService: WebClientService) {
 
         // Logging
         this.log = logService.getLogger('Status-C', 'color: #000; background-color: #ffff99');
 
+        // Config
+        this.config = config;
+
         // Angular services
         this.$timeout = $timeout;
         this.$state = $state;
@@ -101,7 +109,14 @@ export class StatusController {
      * Return the prefixed status.
      */
     public get statusClass(): string {
-        return 'status-task-' + this.webClientService.chosenTask + ' status-' + this.state;
+        const classes = [
+            `status-task-${this.webClientService.chosenTask}`,
+            `status-${this.state}`,
+        ];
+        if (this.config.VISUALIZE_STATE) {
+            classes.push(`visualize-state`);
+        }
+        return classes.join(' ');
     }
 
     /**

+ 50 - 0
src/controllers/theme.ts

@@ -0,0 +1,50 @@
+/**
+ * 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/>.
+ */
+
+import {Logger} from 'ts-log';
+
+import {LogService} from '../services/log';
+import {ThemeService} from '../services/theme';
+
+/**
+ * This controller handles theming.
+ */
+export class ThemeController {
+    // Logging
+    private readonly log: Logger;
+
+    // Config
+    private config: threema.Config;
+
+    // Theme name
+    public theme: string;
+
+    public static $inject = ['$scope', 'LogService', 'ThemeService'];
+    constructor($scope, logService: LogService, themeService: ThemeService) {
+        // Logging
+        this.log = logService.getLogger('Theme-C', 'color: #000; background-color: #ffff99');
+
+        // Initialize theme
+        this.theme = themeService.theme;
+
+        // Listen to theme changes
+        themeService.evtThemeChange.attach((newTheme: threema.Theme) => {
+            this.log.debug(`Updating theme: ${this.theme} -> ${newTheme}`);
+            $scope.$apply(() => this.theme = newTheme);
+        });
+    }
+}

+ 4 - 2
src/controllers/troubleshooting.ts

@@ -22,13 +22,14 @@ import * as clipboard from '../helpers/clipboard';
 
 import {BrowserService} from '../services/browser';
 import {LogService} from '../services/log';
+import {ThemeService} from '../services/theme';
 import {WebClientService} from '../services/webclient';
 import {DialogController} from './dialog';
 
 export class TroubleshootingController extends DialogController {
     public static readonly $inject = [
         '$scope', '$mdDialog', '$mdToast', '$translate',
-        'CONFIG', 'LogService', 'BrowserService', 'WebClientService',
+        'CONFIG', 'LogService', 'BrowserService', 'ThemeService', 'WebClientService',
     ];
 
     private readonly $scope: ng.IScope;
@@ -52,9 +53,10 @@ export class TroubleshootingController extends DialogController {
         config: threema.Config,
         logService: LogService,
         browserService: BrowserService,
+        themeService: ThemeService,
         webClientService: WebClientService,
     ) {
-        super($mdDialog);
+        super($scope, $mdDialog, themeService);
         this.$scope = $scope;
         this.$mdToast = $mdToast;
         this.$translate = $translate;

+ 1 - 1
src/partials/dialog.about.html

@@ -1,4 +1,4 @@
-<md-dialog aria-label="About">
+<md-dialog aria-label="About" md-theme="{{ ctrl.theme }}">
     <form ng-cloak>
         <md-toolbar>
             <div class="md-toolbar-tools">

+ 1 - 1
src/partials/dialog.device-unreachable.html

@@ -1,4 +1,4 @@
-<md-dialog aria-label="Device Unreachable">
+<md-dialog aria-label="Device Unreachable" md-theme="{{ ctrl.theme }}">
     <form ng-cloak>
         <md-toolbar>
             <div class="md-toolbar-tools">

+ 1 - 1
src/partials/dialog.push-rejected.html

@@ -1,4 +1,4 @@
-<md-dialog translate-attr="{'aria-label': 'common.ERROR'}">
+<md-dialog translate-attr="{'aria-label': 'common.ERROR'}" md-theme="{{ ctrl.theme }}">
     <form ng-cloak>
         <md-toolbar>
             <div class="md-toolbar-tools">

+ 1 - 1
src/partials/dialog.qr.html

@@ -1,4 +1,4 @@
-<md-dialog aria-label="My Threema ID" id="my-threema-id-dialog">
+<md-dialog aria-label="My Threema ID" id="my-threema-id-dialog" md-theme="{{ ctrl.theme }}">
     <form ng-cloak>
         <md-toolbar>
             <div class="md-toolbar-tools">

+ 1 - 1
src/partials/dialog.settings.html

@@ -1,4 +1,4 @@
-<md-dialog aria-label="Settings">
+<md-dialog aria-label="Settings" md-theme="{{ ctrl.theme }}">
     <form ng-cloak>
         <md-toolbar>
             <div class="md-toolbar-tools">

+ 1 - 1
src/partials/dialog.troubleshooting.html

@@ -1,4 +1,4 @@
-<md-dialog aria-label="Troubleshooting">
+<md-dialog aria-label="Troubleshooting" md-theme="{{ ctrl.theme }}">
     <form ng-cloak>
         <md-toolbar>
             <div class="md-toolbar-tools">

+ 1 - 1
src/partials/dialog.unlockfailed.html

@@ -1,4 +1,4 @@
-<md-dialog aria-label="Unlocking failed">
+<md-dialog aria-label="Unlocking failed" md-theme="{{ ctrl.theme }}">
     <form ng-cloak>
         <md-toolbar>
             <div class="md-toolbar-tools">

+ 1 - 1
src/partials/dialog.version.html

@@ -1,4 +1,4 @@
-<md-dialog aria-label="About">
+<md-dialog aria-label="About" md-theme="{{ ctrl.theme }}">
     <form ng-cloak>
         <md-toolbar>
             <div class="md-toolbar-tools">

+ 73 - 40
src/partials/messenger.ts

@@ -40,6 +40,7 @@ import {NotificationService} from '../services/notification';
 import {ReceiverService} from '../services/receiver';
 import {SettingsService} from '../services/settings';
 import {StateService} from '../services/state';
+import {ThemeService} from '../services/theme';
 import {TimeoutService} from '../services/timeout';
 import {VersionService} from '../services/version';
 import {WebClientService} from '../services/webclient';
@@ -53,17 +54,20 @@ import ControllerModelMode = threema.ControllerModelMode;
  * Handle sending of files.
  */
 class SendFileController extends DialogController {
-    public static $inject = ['$mdDialog', 'LogService', 'preview'];
-
     public caption: string;
     public sendAsFile: boolean = false;
     private preview: threema.FileMessageData | null = null;
     public previewDataUrl: string | null = null;
 
-    constructor($mdDialog: ng.material.IDialogService,
-                logService: LogService,
-                preview: threema.FileMessageData) {
-        super($mdDialog);
+    public static $inject = ['$scope', '$mdDialog', 'LogService', 'ThemeService', 'preview'];
+    constructor(
+        $scope: ng.IScope,
+        $mdDialog: ng.material.IDialogService,
+        logService: LogService,
+        themeService: ThemeService,
+        preview: threema.FileMessageData,
+    ) {
+        super($scope, $mdDialog, themeService);
         const log = logService.getLogger('SendFile-C');
         this.preview = preview;
         if (preview !== null) {
@@ -94,10 +98,6 @@ class SendFileController extends DialogController {
  * Handle device unreachable
  */
 export class DeviceUnreachableController extends DialogController {
-    public static readonly $inject = [
-        '$rootScope', '$window', '$mdDialog',
-        'StateService', 'WebClientService',
-    ];
     private readonly $rootScope: any;
     private readonly $window: ng.IWindowService;
     private readonly stateService: StateService;
@@ -105,11 +105,19 @@ export class DeviceUnreachableController extends DialogController {
     public retrying: boolean = false;
     public progress: number = 0;
 
+    public static readonly $inject = [
+        '$rootScope', '$window', '$mdDialog',
+        'StateService', 'ThemeService', 'WebClientService',
+    ];
     constructor(
-        $rootScope: any, $window: ng.IWindowService, $mdDialog: ng.material.IDialogService,
-        stateService: StateService, webClientService: WebClientService,
+        $rootScope: any,
+        $window: ng.IWindowService,
+        $mdDialog: ng.material.IDialogService,
+        stateService: StateService,
+        themeService: ThemeService,
+        webClientService: WebClientService,
     ) {
-        super($mdDialog);
+        super($rootScope, $mdDialog, themeService);
         this.$rootScope = $rootScope;
         this.$window = $window;
         this.stateService = stateService;
@@ -175,14 +183,18 @@ export class PushRejectedDialogController extends DialogController {
 
     private readonly trustedKeyStore: TrustedKeyStoreService;
 
-    public static readonly $inject = ['$mdDialog', '$window', 'TrustedKeyStore' , 'LogService'];
+    public static readonly $inject = [
+        '$scope', '$mdDialog', '$window', 'ThemeService', 'TrustedKeyStore', 'LogService',
+    ];
     constructor(
+        $scope: ng.IScope,
         $mdDialog: ng.material.IDialogService,
         $window: ng.IWindowService,
+        themeService: ThemeService,
         trustedKeyStore: TrustedKeyStoreService,
         logService: LogService,
     ) {
-        super($mdDialog);
+        super($scope, $mdDialog, themeService);
         this.$window = $window;
         this.log = logService.getLogger('PushRejectedDialog-C');
         this.trustedKeyStore = trustedKeyStore;
@@ -203,8 +215,6 @@ export class PushRejectedDialogController extends DialogController {
  * Handle settings
  */
 class SettingsController extends DialogController {
-    public static $inject = ['$mdDialog', '$window', 'SettingsService', 'NotificationService'];
-
     public $window: ng.IWindowService;
     public settingsService: SettingsService;
     private notificationService: NotificationService;
@@ -215,11 +225,18 @@ class SettingsController extends DialogController {
     private notificationPreview: boolean;
     private notificationSound: boolean;
 
-    constructor($mdDialog: ng.material.IDialogService,
-                $window: ng.IWindowService,
-                settingsService: SettingsService,
-                notificationService: NotificationService) {
-        super($mdDialog);
+    public static $inject = [
+        '$scope', '$mdDialog', '$window', 'SettingsService', 'ThemeService', 'NotificationService',
+    ];
+    constructor(
+        $scope: ng.IScope,
+        $mdDialog: ng.material.IDialogService,
+        $window: ng.IWindowService,
+        settingsService: SettingsService,
+        themeService: ThemeService,
+        notificationService: NotificationService,
+    ) {
+        super($scope, $mdDialog, themeService);
         this.$window = $window;
         this.settingsService = settingsService;
         this.notificationService = notificationService;
@@ -944,9 +961,14 @@ class ConversationController {
 class AboutDialogController extends DialogController {
     public readonly config: threema.Config;
 
-    public static readonly $inject = ['$mdDialog', 'CONFIG'];
-    constructor($mdDialog: ng.material.IDialogService, config: threema.Config) {
-        super($mdDialog);
+    public static readonly $inject = ['$scope', '$mdDialog', 'ThemeService', 'CONFIG'];
+    constructor(
+        $scope: ng.IScope,
+        $mdDialog: ng.material.IDialogService,
+        themeService: ThemeService,
+        config: threema.Config,
+    ) {
+        super($scope, $mdDialog, themeService);
         this.config = config;
     }
 }
@@ -1309,6 +1331,31 @@ class MessengerController {
     }
 }
 
+class QrDialogController extends DialogController {
+    public readonly profile: threema.MeReceiver;
+    public readonly qrCode;
+
+    public static readonly $inject = ['$scope', '$mdDialog', 'ThemeService', 'WebClientService'];
+    constructor(
+        $scope: ng.IScope,
+        $mdDialog: ng.material.IDialogService,
+        themeService: ThemeService,
+        webClientService: WebClientService,
+    ) {
+        super($scope, $mdDialog, themeService);
+
+        this.profile = webClientService.me;
+        this.qrCode = {
+             errorCorrectionLevel: 'L',
+             size: '400px',
+             data: '3mid:'
+             + this.profile.id
+             + ','
+             + u8aToHex(new Uint8Array(this.profile.publicKey)),
+         };
+    }
+}
+
 class ReceiverDetailController {
     // Angular services
     private $mdDialog: any;
@@ -1471,24 +1518,10 @@ class ReceiverDetailController {
      * Show the QR code of the public key.
      */
     public showQr(): void {
-        const profile = this.webClientService.me;
         const $mdDialog = this.$mdDialog;
         $mdDialog.show({
             controllerAs: 'ctrl',
-            controller: [function() {
-               this.cancel = () =>  {
-                   $mdDialog.cancel();
-               };
-               this.profile = profile;
-               this.qrCode = {
-                    errorCorrectionLevel: 'L',
-                    size: '400px',
-                    data: '3mid:'
-                    + profile.id
-                    + ','
-                    + u8aToHex(new Uint8Array(profile.publicKey)),
-                };
-            }],
+            controller: QrDialogController,
             templateUrl: 'partials/dialog.qr.html',
             parent: angular.element(document.body),
             clickOutsideToClose: true,

+ 6 - 6
src/sass/app.scss

@@ -4,6 +4,12 @@
 // - https://www.sitepoint.com/architecture-sass-project/
 // - http://vanseodesign.com/css/sass-directory-structures/
 
+// Helpers: Tools, helper files, variables, config files.
+@import 'helpers/colors';
+@import 'helpers/sizes';
+@import 'helpers/message_bubble';
+@import 'helpers/texts';
+
 // Base: Boilerplate content. It holds the styles every page of your site
 // should receive.
 @import 'base/reset';
@@ -11,12 +17,6 @@
 @import 'base/colors';
 @import 'base/cloak';
 
-// Helpers: Tools, helper files, variables, config files.
-@import 'helpers/colors';
-@import 'helpers/sizes';
-@import 'helpers/message_bubble';
-@import 'helpers/texts';
-
 // Layouts: Macro layout files. Styles for major sections of the layout like a
 // header or footer and styles for a grid system would belong here.
 @import 'layout/main';

+ 9 - 0
src/sass/base/_colors.scss

@@ -11,3 +11,12 @@ body {
 footer {
     color: #ffffff;
 }
+
+dl.key-values dt {
+    color: $theme-primary;
+    font-weight: 500;
+}
+
+body[md-theme='threemawork'] dl.key-values dt {
+    color: $work-primary;
+}

+ 26 - 6
src/sass/components/_links.scss

@@ -1,20 +1,40 @@
-// Based on http://mrmrs.io/links/
+// sass-lint:disable no-mergeable-selectors
+
 a {
     transition: color .4s;
     text-decoration: none;
-    color: #265c83;
+}
+
+a {
+    color: $theme-primary;
+
+    &:link,
+    &:visited {
+        color: $theme-primary;
+    }
+
+    &:hover {
+        color: $theme-b400;
+    }
+
+    &:active {
+        color: $theme-b400;
+    }
+}
+
+body[md-theme='threemawork'] a {
+    color: $work-primary;
 
     &:link,
     &:visited {
-        color: #265c83;
+        color: $work-primary;
     }
 
     &:hover {
-        color: lighten(#265c83, 30%);
+        color: $work-b500;
     }
 
     &:active {
-        transition: color .3s;
-        color: #007be6;
+        color: $work-b500;
     }
 }

+ 2 - 2
src/sass/components/_verification_level.scss

@@ -29,7 +29,7 @@
             div {
                 &:nth-of-type(1),
                 &:nth-of-type(2) {
-                    background-color: $work-blue;
+                    background-color: $work-primary;
                 }
             }
         }
@@ -45,7 +45,7 @@
                 &:nth-of-type(1),
                 &:nth-of-type(2),
                 &:nth-of-type(3) {
-                    background-color: $work-blue;
+                    background-color: $work-primary;
                 }
             }
         }

+ 16 - 3
src/sass/helpers/_colors.scss

@@ -1,13 +1,26 @@
-$status-ok: #4caf50;
+// Main theme colors
+$theme-primary: #05a63f;
+$theme-b300: #03732b;
+$theme-b400: #036325;
+$theme-b500: #02521f;
+$theme-green: $theme-primary;
+
+// Work theme colors
+$work-primary: #0096ff;
+$work-b300: #0068b2;
+$work-b400: #005a99;
+$work-b500: #004a7f;
+
+// Status colors
+$status-ok: $theme-primary;
 $status-warning: #ff9800;
 $status-error: #f44336;
 $status-starred: #ffc107;
+
 $border-grey: #dddddd;
 $background-grey: lighten($border-grey, 7%);
 $material-grey: rgba(0, 0, 0, .54);
 $material-grey-dark: rgba(0, 0, 0, .87);
-$url-light-blue: #63a6cf;
-$work-blue: #3b84df;
 
 $dark-background-color: #f6f6f6;
 $light-background-color: #ffffff;

+ 1 - 1
src/sass/layout/_main.scss

@@ -393,7 +393,7 @@ md-list-item .md-list-item-inner > md-checkbox .md-label {
 }
 
 .status-yes i {
-    color: #4caf50;
+    color: $theme-green;
 }
 
 .status-no i {

+ 8 - 3
src/sass/sections/_status_bar.scss

@@ -49,12 +49,17 @@ body {
     }
 }
 
+#title .dot {
+    fill: #ffffff;
+}
+
 .status-ok {
     #status-bar {
         background-color: $status-ok;
+        height: 0;
     }
 
-    #title .dot {
+    &.visualize-state #title .dot {
         fill: $status-ok;
     }
 }
@@ -64,7 +69,7 @@ body {
         background-color: $status-warning;
     }
 
-    #title .dot {
+    &.visualize-state #title .dot {
         fill: $status-warning;
     }
 }
@@ -74,7 +79,7 @@ body {
         background-color: $status-error;
     }
 
-    #title .dot {
+    &.visualize-state #title .dot {
         fill: $status-error;
     }
 }

+ 2 - 0
src/services.ts

@@ -33,6 +33,7 @@ import {ReceiverService} from './services/receiver';
 import {SettingsService} from './services/settings';
 import {StateService} from './services/state';
 import {StringService} from './services/string';
+import {ThemeService} from './services/theme';
 import {TimeoutService} from './services/timeout';
 import {TitleService} from './services/title';
 import {UriService} from './services/uri';
@@ -54,6 +55,7 @@ angular.module('3ema.services', [])
 .service('QrCodeService', QrCodeService)
 .service('ReceiverService', ReceiverService)
 .service('StateService', StateService)
+.service('ThemeService', ThemeService)
 .service('TimeoutService', TimeoutService)
 .service('TitleService', TitleService)
 .service('TrustedKeyStore', TrustedKeyStoreService)

+ 58 - 0
src/services/theme.ts

@@ -0,0 +1,58 @@
+/**
+ * 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/>.
+ */
+
+import {AsyncEvent} from 'ts-events';
+import {Logger} from 'ts-log';
+
+import {LogService} from './log';
+
+export class ThemeService {
+    // Angular services
+    private $interval: ng.IIntervalService;
+
+    // Logging
+    private readonly log: Logger;
+
+    // Events
+    public evtThemeChange = new AsyncEvent<threema.Theme>();
+
+    // Private attributes
+    private _theme: threema.Theme = threema.Theme.Regular;
+
+    public static $inject = ['$interval', 'LogService'];
+    constructor($interval: ng.IIntervalService, logService: LogService) {
+        this.$interval = $interval;
+        this.log = logService.getLogger('Theme-S', 'color: #fff; background-color: #cc9900');
+
+        this.log.debug(`Initializing with theme ${this.theme}`);
+    }
+
+    /**
+     * Return the current theme.
+     */
+    public get theme(): threema.Theme {
+        return this._theme;
+    }
+
+    /**
+     * Change the theme.
+     */
+    public changeTheme(theme: threema.Theme) {
+        this._theme = theme;
+        this.evtThemeChange.post(theme);
+    }
+}

+ 11 - 1
src/services/webclient.ts

@@ -54,6 +54,7 @@ import {PushService, PushSession} from './push';
 import {QrCodeService} from './qrcode';
 import {ReceiverService} from './receiver';
 import {StateService} from './state';
+import {ThemeService} from './theme';
 import {TimeoutService} from './timeout';
 import {TitleService} from './title';
 import {VersionService} from './version';
@@ -193,6 +194,7 @@ export class WebClientService {
     private pushService: PushService;
     private qrCodeService: QrCodeService;
     private receiverService: ReceiverService;
+    private themeService: ThemeService;
     private timeoutService: TimeoutService;
     private titleService: TitleService; // Don't remove, needs to be initialized to handle events
     private versionService: VersionService;
@@ -286,7 +288,7 @@ export class WebClientService {
         'LogService', 'Container', 'TrustedKeyStore',
         'StateService', 'NotificationService', 'MessageService', 'PushService', 'BrowserService',
         'TitleService', 'QrCodeService', 'MimeService', 'ReceiverService',
-        'VersionService', 'BatteryStatusService', 'TimeoutService',
+        'VersionService', 'BatteryStatusService', 'ThemeService', 'TimeoutService',
         'CONFIG',
     ];
     constructor($rootScope: any,
@@ -311,6 +313,7 @@ export class WebClientService {
                 receiverService: ReceiverService,
                 versionService: VersionService,
                 batteryStatusService: BatteryStatusService,
+                themeService: ThemeService,
                 timeoutService: TimeoutService,
                 CONFIG: threema.Config) {
 
@@ -334,6 +337,7 @@ export class WebClientService {
         this.pushService = pushService;
         this.qrCodeService = qrCodeService;
         this.receiverService = receiverService;
+        this.themeService = themeService;
         this.timeoutService = timeoutService;
         this.titleService = titleService;
         this.versionService = versionService;
@@ -3410,6 +3414,12 @@ export class WebClientService {
             this.pushService.init(this.pushToken, this.pushTokenType);
         }
 
+        // If this is a work app, set a class on the HTML body
+        // that can be used for customization.
+        if (this.clientInfo.isWork) {
+            this.themeService.changeTheme(threema.Theme.Work);
+        }
+
         this.registerInitializationStep(InitializationStep.ClientInfo);
         future.resolve();
     }

+ 6 - 0
src/threema.d.ts

@@ -688,6 +688,7 @@ declare namespace threema {
         ARP_LOG_TRACE: boolean;
         MSGPACK_LOG_TRACE: boolean;
         TRANSPORT_LOG_LEVEL: LogLevel;
+        VISUALIZE_STATE: boolean;
     }
 
     interface InitialConversationData {
@@ -712,6 +713,11 @@ declare namespace threema {
         Ios = 'ios',
     }
 
+    const enum Theme {
+        Regular = 'threema',
+        Work = 'threemawork',
+    }
+
     interface ClientInfo {
         // The device name
         device: string;

+ 1 - 1
troubleshoot/index.html

@@ -82,7 +82,7 @@
             vertical-align: top;
         }
 
-        .status-yes i { color: #4caf50; }
+        .status-yes i { color: #05a63f; }
         .status-no i { color: #f44336; }
         .status-unknown i { color: #0277BD; }
 

Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff