Explorar o código

gui: Show folder/device status on small screens (#8643)

gui: Show folder/device status on small screens

On larger screens, folder and device status is shown in a textual form
directly next to folder and device titles. However, on small screens,
only icons are currently shown, which may be ambiguous to new users, who
cannot possibly know what a specific icon means (see [1]). Thus, on
small screens only, display a new entry in folder/device info that
contains the same textual information that is shown in the title on
larger screens.

Signed-off-by: Tomasz Wilczyński <[email protected]>
Co-authored-by: André Colomb <[email protected]>
tomasz1986 %!s(int64=2) %!d(string=hai) anos
pai
achega
d42fff1016

+ 6 - 4
gui/default/assets/css/overrides.css

@@ -180,7 +180,7 @@ input[type="checkbox"].extended-attributes-filter-rule-checkbox {
     margin-right: .14285715em;
 }
 
-.remote-devices-panel {
+.inline-icon {
     display: inline-block;
 }
 
@@ -460,15 +460,17 @@ ul.three-columns li, ul.two-columns li {
 }
 
 @media (max-width: 419px) {
-    /* the selectors are build to target only the content of folder and device
-       panels as it would "destroy" e.g. out of sync or recent changes listings */
+    /* The selectors are build to target only the content of folder and device
+       panels as it would "destroy" e.g. out of sync or recent changes listings.
+       The !important is needed to override .visible-xs that sets display to a
+       specific table element instead of block. */
     div[id^='device-'].panel-collapse table,
     div[id^='folder-'].panel-collapse table,
     div[id^='device-'].panel-collapse tbody,
     div[id^='folder-'].panel-collapse tbody,
     div[id^='device-'].panel-collapse tr,
     div[id^='folder-'].panel-collapse tr {
-        display: block;
+        display: block !important;
     }
     div[id^='device-'].panel-collapse th,
     div[id^='folder-'].panel-collapse th,

+ 2 - 0
gui/default/assets/lang/lang-en.json

@@ -99,6 +99,7 @@
     "Device ID": "Device ID",
     "Device Identification": "Device Identification",
     "Device Name": "Device Name",
+    "Device Status": "Device Status",
     "Device is untrusted, enter encryption password": "Device is untrusted, enter encryption password",
     "Device rate limits": "Device rate limits",
     "Device that last modified the item": "Device that last modified the item",
@@ -168,6 +169,7 @@
     "Folder ID": "Folder ID",
     "Folder Label": "Folder Label",
     "Folder Path": "Folder Path",
+    "Folder Status": "Folder Status",
     "Folder Type": "Folder Type",
     "Folder type \"{%receiveEncrypted%}\" can only be set when adding a new folder.": "Folder type \"{{receiveEncrypted}}\" can only be set when adding a new folder.",
     "Folder type \"{%receiveEncrypted%}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.": "Folder type \"{{receiveEncrypted}}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.",

+ 21 - 45
gui/default/index.html

@@ -395,37 +395,12 @@
                     <span ng-if="folder.type == 'receiveencrypted'" class="fas fa-fw fa-lock"></span>
                   </div>
                   <div class="panel-status pull-right text-{{folderClass(folder)}}" ng-switch="folderStatus(folder)">
-                    <span ng-switch-when="paused"><span class="hidden-xs" translate>Paused</span><span class="visible-xs" aria-label="{{'Paused' | translate}}"><i class="fas fa-fw fa-pause"></i></span></span>
-                    <span ng-switch-when="unknown"><span class="hidden-xs" translate>Unknown</span><span class="visible-xs" aria-label="{{'Unknown' | translate}}"><i class="fas fa-fw fa-question-circle"></i></span></span>
-                    <span ng-switch-when="unshared"><span class="hidden-xs" translate>Unshared</span><span class="visible-xs" aria-label="{{'Unshared' | translate}}"><i class="fas fa-fw fa-unlink"></i></span></span>
-                    <span ng-switch-when="scan-waiting"><span class="hidden-xs" translate>Waiting to Scan</span><span class="visible-xs" aria-label="{{'Waiting to Scan' | translate}}"><i class="fas fa-fw fa-hourglass-half"></i></span></span>
-                    <span ng-switch-when="cleaning"><span class="hidden-xs" translate>Cleaning Versions</span><span class="visible-xs" aria-label="{{'Cleaning Versions' | translate}}"><i class="fas fa-fw fa-recycle"></i></span></span>
-                    <span ng-switch-when="clean-waiting"><span class="hidden-xs" translate>Waiting to Clean</span><span class="visible-xs" aria-label="{{'Waiting to Clean' | translate}}"><i class="fas fa-fw fa-hourglass-half"></i></span></span>
-                    <span ng-switch-when="stopped"><span class="hidden-xs" translate>Stopped</span><span class="visible-xs" aria-label="{{'Stopped' | translate}}"><i class="fas fa-fw fa-stop"></i></span></span>
-                    <span ng-switch-when="scanning">
-                      <span class="hidden-xs" translate>Scanning</span>
-                      <span class="hidden-xs" ng-if="scanPercentage(folder.id) != undefined">
-                        ({{scanPercentage(folder.id) | percent}})
-                      </span>
-                      <span class="visible-xs" aria-label="{{'Scanning' | translate}}"><i class="fas fa-fw fa-search"></i></span>
-                    </span>
-                    <span ng-switch-when="idle"><span class="hidden-xs" translate>Up to Date</span><span class="visible-xs" aria-label="{{'Up to Date' | translate}}"><i class="fas fa-fw fa-check"></i></span></span>
-                    <span ng-switch-when="localadditions"><span class="hidden-xs" translate>Local Additions</span><span class="visible-xs" aria-label="{{'Local Additions' | translate}}"><i class="fas fa-fw fa-check"></i></span></span>
-                    <span ng-switch-when="sync-waiting">
-                      <span class="hidden-xs" translate>Waiting to Sync</span>
-                      <span class="visible-xs" aria-label="{{'Waiting to Sync' | translate}}"><i class="fas fa-fw fa-hourglass-half"></i></span>
-                    </span>
-                    <span ng-switch-when="sync-preparing">
-                      <span class="hidden-xs" translate>Preparing to Sync</span>
-                      <span class="visible-xs" aria-label="{{'Preparing to Sync' | translate}}"><i class="fas fa-fw fa-hourglass-half"></i></span>
-                    </span>
-                    <span ng-switch-when="syncing">
-                      <span class="hidden-xs" translate>Syncing</span>
-                      <span>({{syncPercentage(folder.id) | percent}}, {{model[folder.id].needBytes | binary}}B)</span>
-                    </span>
-                    <span ng-switch-when="outofsync"><span class="hidden-xs" translate>Out of Sync</span><span class="visible-xs" aria-label="{{'Out of Sync' | translate}}"><i class="fas fa-fw fa-exclamation-circle"></i></span></span>
-                    <span ng-switch-when="faileditems"><span class="hidden-xs" translate>Failed Items</span><span class="visible-xs" aria-label="{{'Failed Items' | translate}}"><i class="fas fa-fw fa-exclamation-circle"></i></span></span>
-                    <span ng-switch-when="localunencrypted"><span class="hidden-xs">{{'Unexpected Items' | translate}}</span><span class="visible-xs" aria-label="{{'Unexpected Items' | translate}}"><i class="fas fa-fw fa-exclamation-circle"></i></span></span>
+                    <span class="hidden-xs">{{folderStatusText(folder)}}</span>
+                    <span ng-switch-when="scanning" ng-if="scanPercentage(folder.id) != undefined">({{scanPercentage(folder.id) | percent}})</span>
+                    <span ng-switch-when="syncing">({{syncPercentage(folder.id) | percent}}, {{model[folder.id].needBytes | binary}}B)</span>
+		    <span class="inline-icon">
+                      <span class="visible-xs fa fa-fw {{folderStatusIcon(folder)}}" aria-label="{{folderStatusText(folder)}}"></span>
+		    </span>
                   </div>
                   <div class="panel-title-text">
                     <span tooltip data-original-title="{{folder.label.length != 0 ? folder.id : ''}}">{{folder.label.length != 0 ? folder.label : folder.id}}</span>
@@ -436,6 +411,10 @@
                 <div class="panel-body">
                   <table class="table table-condensed table-striped table-auto">
                     <tbody>
+                      <tr class="visible-xs">
+                        <th><span class="fa fa-fw {{folderStatusIcon(folder)}}"></span>&nbsp;<span translate>Folder Status</span></th>
+                        <td class="text-right">{{folderStatusText(folder)}}</td>
+                      </tr>
                       <tr ng-show="folder.label != undefined && folder.label.length > 0">
                         <th><span class="fas fa-fw fa-info-circle"></span>&nbsp;<span translate>Folder ID</span></th>
                         <td class="text-right no-overflow-ellipse">{{folder.id}}</td>
@@ -794,23 +773,16 @@
                 <div class="panel-progress" ng-show="deviceStatus(deviceCfg) == 'syncing'" ng-attr-style="width: {{completion[deviceCfg.deviceID]._total | percent}}"></div>
                 <h4 class="panel-title">
                   <identicon class="panel-icon" data-value="deviceCfg.deviceID"></identicon>
-                  <span class="pull-right text-{{deviceClass(deviceCfg)}}">
-                    <span ng-switch="deviceStatus(deviceCfg)" class="remote-devices-panel">
-                      <span ng-switch-when="insync"><span class="hidden-xs" translate>Up to Date</span><span class="visible-xs" aria-label="{{'Up to Date' | translate}}"><i class="fas fa-fw fa-check"></i></span></span>
-                      <span ng-switch-when="unused-insync"><span class="hidden-xs" translate>Connected (Unused)</span><span class="visible-xs" aria-label="{{'Connected (Unused)' | translate}}"><i class="fas fa-fw fa-unlink"></i></span></span>
-                      <span ng-switch-when="syncing">
-                        <span class="hidden-xs" translate>Syncing</span> ({{completion[deviceCfg.deviceID]._total | percent}}, {{completion[deviceCfg.deviceID]._needBytes | binary}}B)
-                      </span>
-                      <span ng-switch-when="paused"><span class="hidden-xs" translate>Paused</span><span class="visible-xs" aria-label="{{'Paused' | translate}}"><i class="fas fa-fw fa-pause"></i></span></span>
-                      <span ng-switch-when="unused-paused"><span class="hidden-xs" translate>Paused (Unused)</span><span class="visible-xs" aria-label="{{'Paused (Unused)' | translate}}"><i class="fas fa-fw fa-unlink"></i></span></span>
-                      <span ng-switch-when="disconnected"><span class="hidden-xs" translate>Disconnected</span><span class="visible-xs" aria-label="{{'Disconnected' | translate}}"><i class="fas fa-fw fa-power-off"></i></span></span>
-                      <span ng-switch-when="disconnected-inactive"><span class="hidden-xs" translate>Disconnected (Inactive)</span><span class="visible-xs" aria-label="{{'Disconnected (Inactive)' | translate}}"><i class="fas fa-fw fa-power-off"></i></span></span>
-                      <span ng-switch-when="unused-disconnected"><span class="hidden-xs" translate>Disconnected (Unused)</span><span class="visible-xs" aria-label="{{'Disconnected (Unused)' | translate}}"><i class="fas fa-fw fa-unlink"></i></span></span>
+                  <div class="panel-status pull-right text-{{deviceClass(deviceCfg)}}" ng-switch="deviceStatus(deviceCfg)">
+                    <span class="hidden-xs">{{deviceStatusText(deviceCfg)}}</span>
+                    <span ng-switch-when="syncing">({{completion[deviceCfg.deviceID]._total | percent}}, {{completion[deviceCfg.deviceID]._needBytes | binary}}B)</span>
+                    <span class="inline-icon">
+                      <span class="visible-xs fa fa-fw {{deviceStatusIcon(deviceCfg)}}" aria-label="{{deviceStatusText(deviceCfg)}}"></span>
                     </span>
-                    <span class="remote-devices-panel">
+                    <span class="inline-icon">
                       <span ng-class="rdConnTypeIcon(rdConnType(deviceCfg.deviceID))" class="reception reception-theme"></span>
                     </span>
-                  </span>
+                  </div>
                   <div class="panel-title-text">{{deviceName(deviceCfg)}}</div>
                 </h4>
               </button>
@@ -818,6 +790,10 @@
                 <div class="panel-body">
                   <table class="table table-condensed table-striped table-auto">
                     <tbody>
+                      <tr class="visible-xs">
+                        <th><span class="fa fa-fw {{deviceStatusIcon(deviceCfg)}}"></span>&nbsp;<span translate>Device Status</span></th>
+                        <td class="text-right">{{deviceStatusText(deviceCfg)}}</td>
+                      </tr>
                       <tr ng-if="!connections[deviceCfg.deviceID].connected">
                         <th><span class="fas fa-fw fa-eye"></span>&nbsp;<span translate>Last seen</span></th>
                         <td class="text-right">

+ 107 - 0
gui/default/syncthing/core/syncthingController.js

@@ -1151,6 +1151,113 @@ angular.module('syncthing.core')
             }
         };
 
+        $scope.deviceStatusIcon = function(cfg) {
+            switch ($scope.deviceStatus(cfg)) {
+                case 'disconnected':
+                case 'disconnected-inactive':
+                    return 'fa-power-off';
+                case 'insync':
+                    return 'fa-check';
+                case 'paused':
+                    return 'fa-pause';
+                case 'syncing':
+                    return 'fa-sync';
+                case 'unused-disconnected':
+                case 'unused-insync':
+                case 'unused-paused':
+                    return 'fa-unlink';
+            }
+        };
+
+        $scope.deviceStatusText = function(device) {
+            switch ($scope.deviceStatus(device)) {
+                case 'disconnected':
+                    return $translate.instant('Disconnected');
+                case 'disconnected-inactive':
+                    return $translate.instant('Disconnected (Inactive)');
+                case 'insync':
+                    return $translate.instant('Up to Date');
+                case 'paused':
+                    return $translate.instant('Paused');
+                case 'syncing':
+                    return $translate.instant('Syncing');
+                case 'unused-disconnected':
+                    return $translate.instant('Disconnected (Unused)');
+                case 'unused-insync':
+                    return $translate.instant('Connected (Unused)');
+                case 'unused-paused':
+                    return $translate.instant('Paused (Unused)');
+            }
+        };
+
+        $scope.folderStatusIcon = function(cfg) {
+            switch ($scope.folderStatus(cfg)) {
+                case 'clean-waiting':
+                case 'scan-waiting':
+                case 'sync-preparing':
+                case 'sync-waiting':
+                    return 'fa-hourglass-half';
+                case 'cleaning':
+                    return 'fa-recycle';
+                case 'faileditems':
+                case 'localunencrypted':
+                case 'outofsync':
+                    return 'fa-exclamation-circle';
+                case 'idle':
+                case 'localadditions':
+                    return 'fa-check';
+                case 'paused':
+                    return 'fa-pause';
+                case 'scanning':
+                    return 'fa-search';
+                case 'stopped':
+                    return 'fa-stop';
+                case 'syncing':
+                    return 'fa-sync';
+                case 'unknown':
+                    return 'fa-question-circle';
+                case 'unshared':
+                    return 'fa-unlink';
+            }
+        };
+
+        $scope.folderStatusText = function(folder) {
+            switch ($scope.folderStatus(folder)) {
+                case 'clean-waiting':
+                    return $translate.instant('Waiting to Clean');
+                case 'cleaning':
+                    return $translate.instant('Cleaning Versions');
+                case 'faileditems':
+                    return $translate.instant('Failed Items');
+                case 'idle':
+                    return $translate.instant('Up to Date');
+                case 'localadditions':
+                    return $translate.instant('Local Additions');
+                case 'localunencrypted':
+                    return $translate.instant('Unexpected Items');
+                case 'outofsync':
+                    return $translate.instant('Out of Sync');
+                case 'paused':
+                    return $translate.instant('Paused');
+                case 'scan-waiting':
+                    return $translate.instant('Waiting to Scan');
+                case 'scanning':
+                    return $translate.instant('Scanning');
+                case 'stopped':
+                    return $translate.instant('Stopped');
+                case 'sync-preparing':
+                    return $translate.instant('Preparing to Sync');
+                case 'sync-waiting':
+                    return $translate.instant('Waiting to Sync');
+                case 'syncing':
+                    return $translate.instant('Syncing');
+                case 'unknown':
+                    return $translate.instant('Unknown');
+                case 'unshared':
+                    return $translate.instant('Unshared');
+            }
+        };
+
         $scope.deviceClass = function (deviceCfg) {
             if (typeof $scope.connections[deviceCfg.deviceID] === 'undefined') {
                 return 'info';