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 <twilczynski@naver.com>
Co-authored-by: André Colomb <src@andre.colomb.de>
This commit is contained in:
tomasz1986 2023-12-10 15:14:10 +01:00 committed by GitHub
parent a28de73031
commit d42fff1016
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 136 additions and 49 deletions

View File

@ -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,

View File

@ -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.",

View File

@ -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">

View File

@ -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';