瀏覽代碼

Merge branch 'master' into beta

Danilo Bargen 7 年之前
父節點
當前提交
330697761d

+ 4 - 7
index.html

@@ -57,7 +57,7 @@
 </head>
 
 <body ng-controller="StatusController as ctrl" class="{{ ctrl.statusClass }}" ng-class="{expanded: ctrl.expandStatusBar}">
-    <img src="img/bg.jpg?v=1" id="background-image" draggable="false">
+    <img src="img/bg.jpg?v=1" alt="Background image: Blurred photo of a mountain" id="background-image" draggable="false">
 
     <noscript>
         <img id="logo-noscript" src="img/logo.svg?v=[[VERSION]]"/>
@@ -69,7 +69,7 @@
 
     <div id="main-wrapper" ng-cloak ng-class="{wide: ctrl.wide()}">
         <header>
-            <h1 id="title">
+            <h1 id="title" aria-label="Threema Web Logo">
                 <div id="logo" ng-include src="'img/logo.svg?v=[[VERSION]]'"></div>
             </h1>
         </header>
@@ -85,11 +85,8 @@
         </div>
         <footer ng-controller="FooterController as ctrl">
             <ul>
-                <li><a ng-click="ctrl.showVersionInfo('[[VERSION]]')">Version [[VERSION]] {{ ctrl.config.VERSION_MOUNTAIN }}</a></li>
-                <li><a
-                        href="https://threema.ch/threema-web"
-                        target="_blank"
-                        rel="noopener noreferrer" translate>welcome.MORE_ABOUT_WEB</a>
+                <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 href="https://threema.ch/threema-web" target="_blank" rel="noopener noreferrer" tabindex="0" translate>welcome.MORE_ABOUT_WEB</a>
                 </li>
             </ul>
         </footer>

+ 2 - 1
public/i18n/de.json

@@ -181,7 +181,8 @@
         "MUTED_NONE": "Keine Benachrichtigungen",
         "MUTED_MENTION_ONLY": "Nur bei Erwähnung benachrichtigen",
         "MUTED_SILENT": "Stumme Benachrichtigungen",
-        "ALL": "Alle"
+        "ALL": "Alle",
+        "LOADING_MESSAGES": "Nachrichten werden geladen…"
     },
     "messageStates": {
         "WE_ACK": "Sie haben ein Daumen-Hoch gesendet",

+ 2 - 1
public/i18n/en.json

@@ -180,7 +180,8 @@
         "MUTED_NONE": "No notifications",
         "MUTED_MENTION_ONLY": "Only show notification when mentioned",
         "MUTED_SILENT": "Silent notifications",
-        "ALL": "All"
+        "ALL": "All",
+        "LOADING_MESSAGES": "Loading messages…"
     },
     "messageStates": {
         "WE_ACK": "You sent thumbs-up",

+ 6 - 1
src/controllers/footer.ts

@@ -15,6 +15,8 @@
  * along with Threema Web. If not, see <http://www.gnu.org/licenses/>.
  */
 
+import {isActionTrigger} from '../helpers';
+
 /**
  * Handle footer information.
  */
@@ -29,7 +31,10 @@ export class FooterController {
         this.config = CONFIG;
     }
 
-    public showVersionInfo(version: string): void {
+    public showVersionInfo(version: string, ev?: KeyboardEvent): void {
+        if (ev !== undefined && !isActionTrigger(ev)) {
+            return;
+        }
         this.$mdDialog.show({
             controller: [
                 '$mdDialog',

+ 4 - 2
src/directives/avatar.ts

@@ -68,7 +68,8 @@ export default [
 
                     this.highResolution = this.resolution === 'high';
                     this.isLoading = this.highResolution;
-                    this.backgroundColor = this.receiver.color;
+                    this.backgroundColor = (this.receiver as threema.Receiver).color;
+                    this.receiverName = (this.receiver as threema.Receiver).displayName;
                     this.avatarClass = () => {
                         return 'avatar-' + this.resolution + (this.isLoading ? ' is-loading' : '');
                     };
@@ -215,7 +216,8 @@ export default [
                          ng-class="ctrl.avatarClass()"
                          ng-style="{ 'background-color': ctrl.backgroundColor }"
                          ng-src="{{ ctrl.getAvatarUri() }}"
-                         in-view="ctrl.requestAvatar($inview)">
+                         in-view="ctrl.requestAvatar($inview)"
+                         aria-label="avatar {{ ctrl.receiverName }}">
                </div>
             `,
         };

+ 2 - 2
src/directives/battery.ts

@@ -60,10 +60,10 @@ export default [
             template: `
                 <div class="battery-status" ng-if="ctrl.available()"" ng-class="{'alert': ctrl.alert()}">
                     <md-icon
-                        aria-label="{{ ctrl.description() }}"
+                        aria-label="Battery status: {{ ctrl.description() }}"
                         title="{{ ctrl.description() }}"
                         class="material-icons md-light md-24">{{ ctrl.icon() }}</md-icon>
-                       <span class="battery-percent">{{ ctrl.percent() }}%</span>
+                    <span class="battery-percent" aria-hidden="true">{{ ctrl.percent() }}%</span>
                 </div>
             `,
         };

+ 31 - 7
src/directives/compose_area.ts

@@ -15,6 +15,7 @@
  * along with Threema Web. If not, see <http://www.gnu.org/licenses/>.
  */
 
+import {isActionTrigger} from '../helpers';
 import {BrowserService} from '../services/browser';
 import {StringService} from '../services/string';
 
@@ -528,7 +529,7 @@ export default [
                 }
 
                 // Emoji trigger is clicked
-                function onEmojiTrigger(ev: MouseEvent): void {
+                function onEmojiTrigger(ev: UIEvent): void {
                     ev.stopPropagation();
                     // Toggle visibility of picker
                     if (emojiKeyboard.hasClass('active')) {
@@ -622,14 +623,14 @@ export default [
                 }
 
                 // File trigger is clicked
-                function onFileTrigger(ev: MouseEvent): void {
+                function onFileTrigger(ev: UIEvent): void {
                     ev.preventDefault();
                     ev.stopPropagation();
                     const input = element[0].querySelector('.file-input') as HTMLInputElement;
                     input.click();
                 }
 
-                function onSendTrigger(ev: MouseEvent): boolean {
+                function onSendTrigger(ev: UIEvent): boolean {
                     ev.preventDefault();
                     ev.stopPropagation();
                     return sendText();
@@ -788,15 +789,30 @@ export default [
 
                 // Handle click on emoji trigger
                 emojiTrigger.on('click', onEmojiTrigger);
+                emojiTrigger.on('keypress', (ev: KeyboardEvent) => {
+                    if (isActionTrigger(ev)) {
+                        onEmojiTrigger(ev);
+                    }
+                });
 
                 // Handle click on file trigger
                 fileTrigger.on('click', onFileTrigger);
+                fileTrigger.on('keypress', (ev: KeyboardEvent) => {
+                    if (isActionTrigger(ev)) {
+                        onFileTrigger(ev);
+                    }
+                });
 
                 // Handle file uploads
                 fileInput.on('change', onFileSelected);
 
                 // Handle click on send trigger
                 sendTrigger.on('click', onSendTrigger);
+                sendTrigger.on('keypress', (ev: KeyboardEvent) => {
+                    if (isActionTrigger(ev)) {
+                        onSendTrigger(ev);
+                    }
+                });
 
                 updateView();
 
@@ -827,14 +843,22 @@ export default [
             template: `
                 <div>
                     <div>
-                        <i class="md-primary emoji-trigger trigger is-enabled material-icons">tag_faces</i>
+                        <i class="md-primary emoji-trigger trigger is-enabled material-icons" role="button" aria-label="emoji" tabindex="0">tag_faces</i>
                     </div>
                     <div>
-                        <div class="compose" contenteditable translate translate-attr-data-placeholder="messenger.COMPOSE_MESSAGE" autofocus></div>
+                        <div
+                            class="compose"
+                            contenteditable
+                            autofocus
+                            translate
+                            translate-attr-data-placeholder="messenger.COMPOSE_MESSAGE"
+                            translate-attr-aria-label="messenger.COMPOSE_MESSAGE"
+                            tabindex="0"
+                        ></div>
                     </div>
                     <div>
-                        <i class="md-primary send-trigger trigger material-icons">send</i>
-                        <i class="md-primary file-trigger trigger is-enabled material-icons">attach_file</i>
+                        <i class="md-primary send-trigger trigger material-icons" role="button" aria-label="send" tabindex="0">send</i>
+                        <i class="md-primary file-trigger trigger is-enabled material-icons" role="button" aria-label="attach file" tabindex="0">attach_file</i>
                         <input class="file-input" type="file" style="visibility: hidden" multiple>
                     </div>
                 </div>

+ 5 - 1
src/directives/message_state.ts

@@ -28,7 +28,11 @@ export default [
                 message: '=eeeMessage',
             },
             template: `
-                <i class="material-icons md-dark md-14 {{message.state}}" title="{{ message | messageStateTitleText | translate }}">{{ message | messageStateIcon }}</i>
+                <i
+                    class="material-icons md-dark md-14 {{message.state}}"
+                    title="{{ message | messageStateTitleText | translate }}"
+                    aria-label="icon {{ message | messageStateTitleText | translate }}"
+                >{{ message | messageStateIcon }}</i>
             `,
         };
     },

+ 17 - 0
src/helpers.ts

@@ -354,3 +354,20 @@ export function arraysAreEqual(a1: Uint8Array, a2: Uint8Array): boolean {
     }
     return true;
 }
+
+/*
+ * Return whether this key event should trigger a button.
+ * https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values
+ */
+export function isActionTrigger(ev: KeyboardEvent): boolean {
+    if (ev.key === undefined) {
+        return false;
+    }
+    switch (ev.key) {
+        case 'Enter':
+        case ' ':
+            return true;
+        default:
+            return false;
+    }
+}

+ 2 - 2
src/partials/messenger.conversation.html

@@ -40,7 +40,7 @@
         <ul class="chat">
             <li in-view="$inview && !ctrl.locked && ctrl.topOfChat()" class="load-more">
                 <div ng-if="ctrl.hasMoreMessages()" class="loading">
-                    <img ng-src="img/spinner.gif" alt="...">
+                    <img ng-src="img/spinner.gif" alt="..." translate translate-attr-aria-label="messenger.LOADING_MESSAGES">
                 </div>
             </li>
             <li ng-repeat="message in ctrl.messages" id="message-{{message.id}}">
@@ -62,7 +62,7 @@
     </div>
 
     <div id="scrolljump" ng-click="ctrl.scrollDown()" ng-if="ctrl.showScrollJump && !ctrl.receiver.locked"
-         translate translate-attr-title="messenger.SCROLL_DOWN">
+         translate translate-attr-title="messenger.SCROLL_DOWN" role="button" aria-label="scroll to bottom" tabindex="0">
         <svg x="0" y="0" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 50 50" width="100" height="100" style="fill: rgb(66, 66, 66);"><g fill="none" stroke="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10" stroke-dasharray="" stroke-dashoffset="0" font-family="sans-serif" font-weight="normal" font-size="12" text-anchor="start" mix-blend-mode="normal"><g><g><path d="M0,50l0,-50l50,0l0,50z" fill="none"/><path d="M25,46.5c-11.87412,0 -21.5,-9.62588 -21.5,-21.5l0,0c0,-11.87412 9.62588,-21.5 21.5,-21.5l0,0c11.87412,0 21.5,9.62588 21.5,21.5l0,0c0,11.87412 -9.62588,21.5 -21.5,21.5z" fill="#ffffff"/><g fill="#424242"><path d="M25,0.16c-13.69656,0 -24.84,11.14344 -24.84,24.84c0,13.69764 11.14344,24.84 24.84,24.84c13.69764,0 24.84,-11.14236 24.84,-24.84c0,-13.69656 -11.14236,-24.84 -24.84,-24.84z M36.56356,22.52356l-10.8,10.8c-0.2106,0.2106 -0.48708,0.31644 -0.76356,0.31644c-0.27648,0 -0.55296,-0.10584 -0.76356,-0.31644l-10.8,-10.8c-0.42228,-0.42228 -0.42228,-1.10484 0,-1.52712c0.42228,-0.42228 1.10484,-0.42228 1.52712,0l10.03644,10.03644l10.03644,-10.03644c0.42228,-0.42228 1.10484,-0.42228 1.52712,0c0.42228,0.42228 0.42228,1.10484 0,1.52712z"/></g></g></g></g></svg>
     </div>
 

+ 12 - 10
src/partials/messenger.navigation.html

@@ -15,25 +15,25 @@
         <md-menu-content width="4">
             <md-menu-item>
                 <md-button ng-click="ctrl.closeSession()">
-                    <md-icon aria-label="Close session" class="material-icons md-24">exit_to_app</md-icon>
+                    <md-icon aria-hidden="true" class="material-icons md-24">exit_to_app</md-icon>
                     <span translate>common.SESSION_CLOSE</span>
                 </md-button>
             </md-menu-item>
             <md-menu-item ng-if="ctrl.isPersistent()">
                 <md-button ng-click="ctrl.deleteSession()">
-                    <md-icon aria-label="Delete session" class="material-icons md-24">delete</md-icon>
+                    <md-icon aria-hidden="true" class="material-icons md-24">delete</md-icon>
                     <span translate>common.SESSION_DELETE</span>
                 </md-button>
             </md-menu-item>
             <md-menu-item>
                 <md-button ng-click="ctrl.settings()">
-                    <md-icon aria-label="Settings" class="material-icons md-24">settings</md-icon>
+                    <md-icon aria-hidden="true" class="material-icons md-24">settings</md-icon>
                     <span translate>messenger.SETTINGS</span>
                 </md-button>
             </md-menu-item>
             <md-menu-item>
                 <md-button ng-click="ctrl.about()">
-                    <md-icon aria-label="About" class="material-icons md-24">info</md-icon>
+                    <md-icon aria-hidden="true" class="material-icons md-24">info</md-icon>
                     <span translate>messenger.ABOUT</span>
                 </md-button>
             </md-menu-item>
@@ -45,15 +45,15 @@
 <div id="navigation-header">
     <div class="main">
         <md-nav-bar md-no-ink md-selected-nav-item="ctrl.activeTab" nav-bar-aria-label="navigation links">
-            <md-nav-item md-nav-click="1" name="conversations">
+            <md-nav-item md-nav-click="1" name="conversations" aria-label="conversations">
                 <i class="material-icons md-dark md-24" translate translate-attr-title="messenger.CONVERSATIONS">speaker_notes</i>
             </md-nav-item>
-            <md-nav-item md-nav-click="1" name="contacts">
+            <md-nav-item md-nav-click="1" name="contacts" aria-label="contacts">
                 <i class="material-icons md-dark md-24" translate translate-attr-title="messenger.CONTACTS">person</i>
             </md-nav-item>
         </md-nav-bar>
         <span flex></span>
-        <md-button aria-label="Search" class="md-icon-button" ng-click="ctrl.toggleSearch()">
+        <md-button aria-label="search" class="md-icon-button" ng-click="ctrl.toggleSearch()">
             <i class="material-icons md-dark md-24" translate translate-attr-title="messenger.SEARCH">search</i>
         </md-button>
     </div>
@@ -69,21 +69,23 @@
         <li ng-repeat="conversation in ctrl.conversations() | filter:ctrl.searchConversation"
             ng-init="dndModeSimplified = ctrl.dndModeSimplified(conversation)"
             ui-sref="messenger.home.conversation({ type: conversation.type, id: conversation.id, initParams: null })"
-            class="conversation-wrapper" ng-if="ctrl.isVisible(conversation)">
+            aria-label="conversation with {{ conversation.receiver.displayName }}"
+            class="conversation-wrapper"
+            ng-if="ctrl.isVisible(conversation)">
 
             <div class="conversation"
                   ng-class="{'unread': conversation.unreadCount > 0,
                             'starred': conversation.isStarred,
                             'active': ctrl.isActive(conversation)}">
 
-                <section class="avatar-box">
+                <section class="avatar-box" role="button">
                     <eee-avatar eee-receiver="conversation.receiver"
                                 eee-resolution="'low'"></eee-avatar>
                 </section>
 
                 <section class="conversation-box">
                     <section class="receiver-box">
-                        <span class="title" ng-class="{'disabled': conversation.receiver.disabled === true}" ng-bind-html="conversation.receiver.displayName | escapeHtml | emojify">
+                        <span class="title" ng-class="{'disabled': conversation.receiver.disabled === true}" ng-bind-html="conversation.receiver.displayName | escapeHtml | emojify" role="button">
                         </span>
                         <span class="notification-settings" ng-if="dndModeSimplified === 'on'">
                             <img height="16" width="16" src="img/ic_dnd_total_silence.svg" translate translate-attr-title="messenger.MUTED_NONE">

+ 3 - 1
src/partials/welcome.html

@@ -42,7 +42,9 @@
                                    translate-attr="{'placeholder': 'welcome.PASSWORD', 'aria-label': 'welcome.PASSWORD'}"
                                    autocomplete="current-password">
                         </md-input-container>
-                        <md-button type="submit" class="md-raised md-primary"><span translate>welcome.BTN_RECONNECT</span></md-button>
+                        <md-button type="submit" class="md-raised md-primary" translate translate-attr-aria-label="welcome.BTN_RECONNECT">
+                            <span translate aria-hidden="true">welcome.BTN_RECONNECT</span>
+                        </md-button>
                     </form>
                     <p>
                         <span translate>welcome.ALTERNATIVELY</span>

+ 1 - 6
src/sass/helpers/_mouse.scss

@@ -1,9 +1,4 @@
 @mixin mouse-hand {
       cursor: pointer;
       cursor: hand;
-
-      &:focus {
-            outline: none;
-            border: 0;
-      }
-}
+}