Prechádzať zdrojové kódy

Show connection state and unread state in favicon (#720)

Fixes #716, fixes #355.
Danilo Bargen 6 rokov pred
rodič
commit
27d8ca6ec6

+ 4 - 4
index.html

@@ -19,7 +19,7 @@
     along with Threema Web. If not, see <http://www.gnu.org/licenses/>.
 
 -->
-<html ng-app="3ema" ng-strict-di>
+<html ng-app="3ema" ng-strict-di ng-controller="StatusController as ctrl">
 <head>
     <meta charset="utf-8">
     <meta name="viewport" content="width=device-width, initial-scale=1">
@@ -33,8 +33,8 @@
     <!-- Favicon / Webmanifest / Browserconfig -->
     <link rel="manifest" href="manifest.webmanifest?v=[[VERSION]]">
     <link rel="apple-touch-icon" sizes="180x180" href="img/favicon/apple-touch-icon.png?v=[[VERSION]]">
-    <link rel="icon" type="image/png" sizes="32x32" href="img/favicon/favicon-32x32.png?v=[[VERSION]]">
-    <link rel="icon" type="image/png" sizes="16x16" href="img/favicon/favicon-16x16.png?v=[[VERSION]]">
+    <link rel="icon" type="image/png" sizes="32x32" href="{{ 'img/favicon/' + ctrl.faviconFilename + '?v=[[VERSION]]' }}">
+    <link rel="icon" type="image/png" sizes="16x16" href="{{ 'img/favicon/' + ctrl.faviconFilename + '?v=[[VERSION]]' }}">
     <link rel="mask-icon" href="img/favicon/safari-pinned-tab.svg?v=[[VERSION]]" color="#333333">
     <link rel="shortcut icon" href="img/favicon/favicon.ico?v=[[VERSION]]">
     <meta name="msapplication-TileColor" content="#313131">
@@ -56,7 +56,7 @@
     <link rel="stylesheet" href="css/app.css?v=[[VERSION]]">
 </head>
 
-<body ng-controller="StatusController as ctrl" class="{{ ctrl.statusClass }}" ng-class="{expanded: ctrl.expandStatusBar}">
+<body class="{{ ctrl.statusClass }}" ng-class="{expanded: ctrl.expandStatusBar}">
     <img src="img/bg.jpg?v=[[VERSION]]" aria-label="Background image: Blurred photo of a mountain" id="background-image" draggable="false">
 
     <noscript>

BIN
public/img/favicon/favicon-16x16-error.png


BIN
public/img/favicon/favicon-16x16-unread.png


BIN
public/img/favicon/favicon-16x16-warning.png


BIN
public/img/favicon/favicon-32x32-error.png


BIN
public/img/favicon/favicon-32x32-unread.png


BIN
public/img/favicon/favicon-32x32-warning.png


+ 43 - 0
public/img/favicon/gen-variants.sh

@@ -0,0 +1,43 @@
+#!/bin/bash
+# Generate favicon variants.
+# Requirements: bash, python3, imagemagick, optipng.
+set -euo pipefail
+
+COLOR_RED='#f44336'
+COLOR_YELLOW='#ff9800'
+COLOR_BLUE='#1b81e4'
+WIDTH=0.1875
+OFFSET=0.0625
+WIDTHS=(16 32)
+
+circle() {
+    offset_abs=$(python3 -c "print(int($1 * $OFFSET))")
+    x=$(python3 -c "print(int($1 - $1 * $WIDTH - $offset_abs))")
+    xx=$(python3 -c "print(int($1 - $offset_abs))")
+    y=$(python3 -c "print(int($1 * $WIDTH + $offset_abs))")
+    echo "circle $x,$y $xx,$y"
+}
+
+
+for w in ${WIDTHS[@]}; do
+    echo "Red ${w}x${w}"
+    magick favicon-${w}x${w}.png \
+        -fill $COLOR_RED \
+        -draw "$(circle $w)" \
+        favicon-${w}x${w}-error.png
+    optipng -o6 favicon-${w}x${w}-error.png
+
+    echo "Yellow ${w}x${w}"
+    magick favicon-${w}x${w}.png \
+        -fill $COLOR_YELLOW \
+        -draw "$(circle $w)" \
+        favicon-${w}x${w}-warning.png
+    optipng -o6 favicon-${w}x${w}-warning.png
+
+    echo "Blue ${w}x${w}"
+    magick favicon-${w}x${w}.png \
+        -fill $COLOR_BLUE \
+        -draw "$(circle $w)" \
+        favicon-${w}x${w}-unread.png
+    optipng -o6 favicon-${w}x${w}-unread.png
+done

+ 33 - 0
src/controllers/status.ts

@@ -38,6 +38,7 @@ export class StatusController {
 
     // State variable
     private state = GlobalConnectionState.Error;
+    private unreadCount = 0;
 
     // Expanded status bar
     public expandStatusBar = false;
@@ -83,6 +84,11 @@ export class StatusController {
                 this.onStateChange(stateChange.state, stateChange.prevState);
             },
         );
+        this.stateService.evtUnreadCountChange.attach(
+            (count: number) => {
+                this.unreadCount = count;
+            },
+        );
     }
 
     /**
@@ -335,4 +341,31 @@ export class StatusController {
         return this.controllerService.getControllerName() !== undefined
             && this.controllerService.getControllerName() === 'messenger';
     }
+
+    /**
+     * Return the favicon filename.
+     */
+    public get faviconFilename(): string {
+        switch (this.state) {
+            case GlobalConnectionState.Ok:
+                if (this.unreadCount > 0) {
+                    return 'favicon-32x32-unread.png';
+                } else {
+                    return 'favicon-32x32.png';
+                }
+                break;
+            case GlobalConnectionState.Warning:
+                const isRelayedData = this.webClientService.chosenTask === threema.ChosenTask.RelayedData;
+                if (isRelayedData) {
+                    // iOS devices are regularly disconnected, but this is normal behavior.
+                    return 'favicon-32x32.png';
+                } else {
+                    return 'favicon-32x32-warning.png';
+                }
+                break;
+            case GlobalConnectionState.Error:
+                return 'favicon-32x32-error.png';
+                break;
+        }
+    }
 }

+ 20 - 0
src/services/state.ts

@@ -37,6 +37,7 @@ export class StateService {
     // Events
     public evtConnectionBuildupStateChange = new AsyncEvent<threema.ConnectionBuildupStateChange>();
     public evtGlobalConnectionStateChange = new AsyncEvent<threema.GlobalConnectionStateChange>();
+    public evtUnreadCountChange = new AsyncEvent<number>();
 
     // Connection states
     public signalingConnectionState: saltyrtc.SignalingState;
@@ -53,6 +54,9 @@ export class StateService {
     private _state: threema.GlobalConnectionState;
     public wasConnected: boolean;
 
+    // Unread messages
+    private _unreadCount: number = 0;
+
     public static $inject = ['$log', '$interval'];
     constructor($log: ng.ILogService, $interval: ng.IIntervalService) {
         this.$log = $log;
@@ -241,6 +245,21 @@ export class StateService {
         }
     }
 
+    /**
+     * Getters and setters for unread messages count.
+     */
+    public get unreadCount(): number {
+        return this._unreadCount;
+    }
+    public set unreadCount(count: number) {
+        if (this._unreadCount === count) {
+            // No need to dispatch any events
+            return;
+        }
+        this._unreadCount = count;
+        this.evtUnreadCountChange.post(count);
+    }
+
     /**
      * Reset all states.
      */
@@ -255,5 +274,6 @@ export class StateService {
         this.wasConnected = false;
         this.connectionBuildupState = connectionBuildupState;
         this.progress = 0;
+        this.unreadCount = 0;
     }
 }

+ 14 - 6
src/services/title.ts

@@ -14,6 +14,7 @@
  * 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 {StateService} from './state';
 
 /**
  * The title service can update the window title.
@@ -23,19 +24,26 @@ export class TitleService {
     private $log: ng.ILogService;
     private $document: ng.IDocumentService;
 
+    private stateService: StateService;
+
     private DEFAULT_TITLE = 'Threema Web';
     private title: string;
     private unreadCount: number = 0;
 
-    public static $inject = ['$log', '$document'];
-    constructor($log: ng.ILogService, $document: ng.IDocumentService) {
+    public static $inject = ['$log', '$document', 'StateService'];
+    constructor($log: ng.ILogService, $document: ng.IDocumentService, stateService: StateService) {
         this.$log = $log;
         this.$document = $document;
-        this.update();
-    }
+        this.stateService = stateService;
+
+        // Event handlers
+        this.stateService.evtUnreadCountChange.attach(
+            (count: number) => {
+                this.unreadCount = count;
+                this.update();
+            },
+        );
 
-    public updateUnreadCount(count: number): void {
-        this.unreadCount = count;
         this.update();
     }
 

+ 3 - 3
src/services/webclient.ts

@@ -177,7 +177,7 @@ export class WebClientService {
     private qrCodeService: QrCodeService;
     private receiverService: ReceiverService;
     private timeoutService: TimeoutService;
-    private titleService: TitleService;
+    private titleService: TitleService; // Don't remove, needs to be initialized to handle events
     private versionService: VersionService;
 
     // State handling
@@ -3975,14 +3975,14 @@ export class WebClientService {
         const totalUnreadCount = this.conversations
             .get()
             .reduce((a: number, b: threema.Conversation) => a + b.unreadCount, 0);
-        this.titleService.updateUnreadCount(totalUnreadCount);
+        this.stateService.unreadCount = totalUnreadCount;
     }
 
     /**
      * Reset the unread count in the window title
      */
     private resetUnreadCount(): void {
-        this.titleService.updateUnreadCount(0);
+        this.stateService.unreadCount = 0;
     }
 
     /**