Add labels support into web client. (#1406)

* Add labels support into web client.
This commit is contained in:
Viacheslav Chimishuk
2022-02-10 06:12:34 +02:00
committed by GitHub
parent 7bdb6f777b
commit ab02edfde2
9 changed files with 111 additions and 15 deletions

File diff suppressed because one or more lines are too long

View File

@@ -45,6 +45,10 @@ export class ActionManager extends EventTarget {
shortcut: 'Alt+I',
text: 'Torrent Inspector',
},
'show-labels-dialog': {
enabled: false,
text: 'Edit Labels…',
},
'show-move-dialog': {
enabled: false,
shortcut: 'Alt+L',
@@ -185,9 +189,10 @@ export class ActionManager extends EventTarget {
'move-down',
'move-top',
'move-up',
'show-inspector',
'show-move-dialog',
'remove-selected-torrents',
'show-inspector',
'show-labels-dialog',
'show-move-dialog',
'trash-selected-torrents',
'verify-selected-torrents',
]);

View File

@@ -89,6 +89,7 @@ export class ContextMenu extends EventTarget {
add_item('verify-selected-torrents');
add_item('show-move-dialog');
add_item('show-rename-dialog');
add_item('show-labels-dialog');
add_separator();
add_item('reannounce-selected-torrents');
add_separator();

View File

@@ -117,6 +117,7 @@ export class Inspector extends EventTarget {
['origin', 'Origin:'],
['magnetLink', 'Magnet:'],
['comment', 'Comment:'],
['labels', 'Labels:'],
];
for (const [name, text] of rows) {
elements[name] = append_row(text);
@@ -490,6 +491,10 @@ export class Inspector extends EventTarget {
setTextContent(e.info.comment, string);
}
// labels
string = torrents.length === 0 ? none : torrents[0].getLabels().join(', ');
setTextContent(e.info.labels, string);
// origin
if (torrents.length === 0) {
string = none;

View File

@@ -103,6 +103,14 @@ export class Remote {
this.sendRequest(o, callback, context);
}
setLabels(torrentIds, labels, callback) {
const args = {
ids: torrentIds,
labels,
};
this.sendRequest({ arguments: args, method: 'torrent-set' }, callback);
}
loadDaemonStats(callback, context) {
const o = {
method: 'session-stats',

View File

@@ -18,6 +18,12 @@ const TorrentRendererHelper = {
}
return `ETA: ${Formatter.timeInterval(eta)}`;
},
formatLabels: (t) => {
if (t.getLabels().length > 0) {
return `🏷 ${t.getLabels().join(', ')}`;
}
return '';
},
formatUL: (t) => {
return `${Formatter.speedBps(t.getUploadSpeed())}`;
},
@@ -215,6 +221,10 @@ export class TorrentRendererFull {
setTextContent(e, t.getName());
e.classList.toggle('paused', is_stopped);
// labels
e = root._labels_container;
setTextContent(e, TorrentRendererHelper.formatLabels(t));
// progressbar
TorrentRendererHelper.renderProgressbar(controller, t, root._progressbar);
root._progressbar.classList.add('full');
@@ -251,6 +261,9 @@ export class TorrentRendererFull {
const name = document.createElement('div');
name.className = 'torrent-name';
const labels = document.createElement('div');
labels.className = 'torrent-labels';
const peers = document.createElement('div');
peers.className = 'torrent-peer-details';
@@ -268,12 +281,14 @@ export class TorrentRendererFull {
root.append(icon);
root.append(name);
root.append(labels);
root.append(peers);
root.append(progress);
root.append(details);
root._icon = icon;
root._name_container = name;
root._labels_container = labels;
root._peer_details_container = peers;
root._progress_details_container = details;
root._progressbar = progressbar;
@@ -323,6 +338,10 @@ export class TorrentRendererCompact {
e.classList.toggle('paused', t.isStopped());
setTextContent(e, t.getName());
// labels
e = root._labels_container;
setTextContent(e, TorrentRendererHelper.formatLabels(t));
// peer details
const has_error = t.getError() !== Torrent._ErrNone;
e = root._details_container;
@@ -350,17 +369,22 @@ export class TorrentRendererCompact {
const details = document.createElement('div');
details.className = 'torrent-peer-details compact';
const labels = document.createElement('div');
labels.className = 'torrent-labels compact';
const name = document.createElement('div');
name.className = 'torrent-name compact';
const root = document.createElement('li');
root.append(progressbar);
root.append(details);
root.append(labels);
root.append(name);
root.append(icon);
root.className = 'torrent compact';
root._progressbar = progressbar;
root._details_container = details;
root._labels_container = labels;
root._name_container = name;
return root;
}

View File

@@ -203,6 +203,9 @@ export class Torrent extends EventTarget {
getId() {
return this.fields.id;
}
getLabels() {
return this.fields.labels.sort();
}
getLastActivity() {
return this.fields.activityDate;
}
@@ -419,19 +422,36 @@ export class Torrent extends EventTarget {
}
/**
* @param filter one of Prefs.Filter*
* @param state one of Prefs.Filter*
* @param tracker tracker name
* @param search substring to look for, or null
* @param labels array of labels. Empty array matches all.
* @return true if it passes the test, false if it fails
*/
test(state, search, tracker) {
test(state, tracker, search, labels) {
// flter by state...
let pass = this.testState(state);
// maybe filter by text...
if (pass && search && search.length > 0) {
if (pass && search) {
pass = this.getCollatedName().includes(search.toLowerCase());
}
// maybe filter by labels...
if (pass) {
for (const l of labels) {
let m = false;
for (let j = 0; j < this.getLabels().length; j++) {
if (l === this.getLabels()[j]) {
m = true;
break;
}
}
pass = pass && m;
}
}
// maybe filter by tracker...
if (pass && tracker && tracker.length > 0) {
pass = this.getCollatedTrackers().includes(tracker);
@@ -627,6 +647,7 @@ Torrent.Fields.Stats = [
'eta',
'isFinished',
'isStalled',
'labels',
'leftUntilDone',
'metadataPercentComplete',
'peersConnected',

View File

@@ -14,6 +14,7 @@ import { PrefsDialog } from './prefs-dialog.js';
import { Remote, RPC } from './remote.js';
import { RemoveDialog } from './remove-dialog.js';
import { RenameDialog } from './rename-dialog.js';
import { LabelsDialog } from './labels-dialog.js';
import { ShortcutsDialog } from './shortcuts-dialog.js';
import { StatisticsDialog } from './statistics-dialog.js';
import { Torrent } from './torrent.js';
@@ -170,6 +171,9 @@ export class Transmission extends EventTarget {
case 'show-rename-dialog':
this.setCurrentPopup(new RenameDialog(this, this.remote));
break;
case 'show-labels-dialog':
this.setCurrentPopup(new LabelsDialog(this, this.remote));
break;
case 'start-all-torrents':
this._startTorrents(this._getAllTorrents());
break;
@@ -909,11 +913,21 @@ TODO: fix this when notifications get fixed
_refilter(rebuildEverything) {
const { sort_mode, sort_direction, filter_mode } = this.prefs;
const filter_text = this.filterText;
const filter_tracker = this.filterTracker;
const renderer = this.torrentRenderer;
const list = this.elements.torrent_list;
let filter_text = null;
let labels = null;
const m = /^labels:([\w,]*)(.*)$/.exec(this.filterText);
if (m) {
filter_text = m[2].trim();
labels = m[1].split(',');
} else {
filter_text = this.filterText;
labels = [];
}
const countRows = () => [...list.children].length;
const countSelectedRows = () =>
[...list.children].reduce(
@@ -958,7 +972,7 @@ TODO: fix this when notifications get fixed
for (const row of dirty_rows) {
const id = row.getTorrentId();
const t = this._torrents[id];
if (t && t.test(filter_mode, filter_text, filter_tracker)) {
if (t && t.test(filter_mode, filter_tracker, filter_text, labels)) {
temporary.push(row);
}
this.dirtyTorrents.delete(id);
@@ -969,7 +983,7 @@ TODO: fix this when notifications get fixed
// but don't already have a row
for (const id of this.dirtyTorrents.values()) {
const t = this._torrents[id];
if (t && t.test(filter_mode, filter_text, filter_tracker)) {
if (t && t.test(filter_mode, filter_tracker, filter_text, labels)) {
const row = new TorrentRow(renderer, this, t);
const e = row.getElement();
e.row = row;

View File

@@ -450,11 +450,11 @@ $popup-top: 61px; // TODO: ugly that this is hardcoded
display: grid;
grid-column-gap: 12px;
grid-template-areas:
'icon name'
'icon peers'
'icon progressbar'
'icon progress-text';
grid-template-columns: $icon-size 1fr;
'icon name labels'
'icon peers peers'
'icon progressbar progressbar'
'icon progress-text progress-text';
grid-template-columns: $icon-size auto 1fr;
padding: 2px 12px;
.icon {
@@ -468,6 +468,10 @@ $popup-top: 61px; // TODO: ugly that this is hardcoded
grid-area: name;
}
.torrent-labels {
grid-area: labels;
}
.torrent-peer-details {
grid-area: peers;
}
@@ -539,7 +543,6 @@ $popup-top: 61px; // TODO: ugly that this is hardcoded
white-space: nowrap;
&.compact {
flex: 1;
font-size: 1em;
font-weight: normal;
}
@@ -549,6 +552,21 @@ $popup-top: 61px; // TODO: ugly that this is hardcoded
}
}
.torrent-labels {
font-size: x-small;
font-weight: normal;
margin-bottom: 2px;
margin-top: 2px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
&.compact {
flex: 1;
font-size: x-small;
}
}
.torrent-progress-details,
.torrent-peer-details {
font-size: x-small;