mirror of
https://github.com/transmission/transmission
synced 2025-01-30 19:03:04 +00:00
chore: bump web client deps (#1698)
* chore: bump web client dependencies
This commit is contained in:
parent
95a45b0bad
commit
d2473f4c2f
17 changed files with 2148 additions and 2719 deletions
|
@ -119,7 +119,8 @@ module.exports = {
|
|||
"sonarjs/no-duplicate-string": "off",
|
||||
"sort-keys": "error",
|
||||
"strict": "error",
|
||||
'unicorn/consistent-function-scoping': 'off',
|
||||
"unicorn/consistent-function-scoping": "off",
|
||||
"unicorn/no-array-reduce": "off",
|
||||
"unicorn/no-fn-reference-in-iterator": "off",
|
||||
"unicorn/no-null": "off",
|
||||
"unicorn/no-reduce": "off",
|
||||
|
|
|
@ -18,36 +18,36 @@
|
|||
"lint:stylelint:fix": "stylelint --fix style/*scss"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.11.6",
|
||||
"@babel/eslint-parser": "^7.11.5",
|
||||
"@babel/plugin-proposal-class-properties": "^7.10.4",
|
||||
"css-loader": "^4.3.0",
|
||||
"eslint": "^7.11.0",
|
||||
"eslint-plugin-sonarjs": "^0.5.0",
|
||||
"eslint-plugin-unicorn": "^23.0.0",
|
||||
"file-loader": "^6.1.0",
|
||||
"@babel/core": "^7.14.3",
|
||||
"@babel/eslint-parser": "^7.14.3",
|
||||
"@babel/plugin-proposal-class-properties": "^7.13.0",
|
||||
"css-loader": "^5.2.4",
|
||||
"css-minimizer-webpack-plugin": "^3.0.0",
|
||||
"eslint": "^7.26.0",
|
||||
"eslint-plugin-sonarjs": "^0.7.0",
|
||||
"eslint-plugin-unicorn": "^32.0.1",
|
||||
"file-loader": "^6.2.0",
|
||||
"img-optimize-loader": "^1.0.7",
|
||||
"mini-css-extract-plugin": "^0.11.3",
|
||||
"node-sass": "^4.14.1",
|
||||
"mini-css-extract-plugin": "^1.6.0",
|
||||
"node-sass": "^6.0.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"optimize-css-assets-webpack-plugin": "^5.0.4",
|
||||
"prettier": "^2.1.2",
|
||||
"sass": "^1.26.11",
|
||||
"sass-loader": "^10.0.2",
|
||||
"style-loader": "^1.2.1",
|
||||
"stylelint": "^13.7.1",
|
||||
"prettier": "^2.3.0",
|
||||
"sass": "^1.32.13",
|
||||
"sass-loader": "^11.1.1",
|
||||
"style-loader": "^2.0.0",
|
||||
"stylelint": "^13.13.1",
|
||||
"stylelint-config-prettier": "^8.0.2",
|
||||
"stylelint-config-primer": "^9.2.1",
|
||||
"stylelint-config-sass-guidelines": "^7.1.0",
|
||||
"stylelint-config-standard": "^20.0.0",
|
||||
"svgo": "^1.3.2",
|
||||
"svgo-loader": "^2.2.1",
|
||||
"terser-webpack-plugin": "^4.2.2",
|
||||
"url-loader": "^4.1.0",
|
||||
"webpack": "^4.44.2",
|
||||
"webpack-bundle-analyzer": "^3.9.0",
|
||||
"webpack-cli": "^3.3.12",
|
||||
"webpack-dev-server": "^3.11.0"
|
||||
"stylelint-config-primer": "^11.0.1",
|
||||
"stylelint-config-sass-guidelines": "^8.0.0",
|
||||
"stylelint-config-standard": "^22.0.0",
|
||||
"svgo": "^2.3.0",
|
||||
"svgo-loader": "^3.0.0",
|
||||
"terser-webpack-plugin": "^5.1.2",
|
||||
"url-loader": "^4.1.1",
|
||||
"webpack": "^5.37.0",
|
||||
"webpack-bundle-analyzer": "^4.4.2",
|
||||
"webpack-cli": "^4.7.0",
|
||||
"webpack-dev-server": "^3.11.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"lodash.isequal": "^4.5.0"
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -137,11 +137,12 @@ export class ActionManager extends EventTarget {
|
|||
}
|
||||
|
||||
static _recount(selected, nonselected) {
|
||||
const test = (tor) => tor.isStopped();
|
||||
const total = selected.length + nonselected.length;
|
||||
const selected_paused = selected.filter(test).length;
|
||||
const selected_paused = selected.filter((tor) => tor.isStopped()).length;
|
||||
const selected_active = selected.length - selected_paused;
|
||||
const nonselected_paused = nonselected.filter(test).length;
|
||||
const nonselected_paused = nonselected.filter((tor) =>
|
||||
tor.isStopped()
|
||||
).length;
|
||||
const nonselected_active = nonselected.length - nonselected_paused;
|
||||
const paused = selected_paused + nonselected_paused;
|
||||
const active = selected_active + nonselected_active;
|
||||
|
|
|
@ -30,15 +30,21 @@ const fmt_MBps = new Intl.NumberFormat(current_locale, {
|
|||
unit: 'megabyte-per-second',
|
||||
});
|
||||
|
||||
export class Formatter {
|
||||
static countString(msgid, msgid_plural, n) {
|
||||
export const Formatter = {
|
||||
/** Round a string of a number to a specified number of decimal places */
|
||||
_toTruncFixed(number, places) {
|
||||
const returnValue = Math.floor(number * 10 ** places) / 10 ** places;
|
||||
return returnValue.toFixed(places);
|
||||
},
|
||||
|
||||
countString(msgid, msgid_plural, n) {
|
||||
return `${this.number(n)} ${this.ngettext(msgid, msgid_plural, n)}`;
|
||||
}
|
||||
},
|
||||
|
||||
// Formats the a memory size into a human-readable string
|
||||
// @param {Number} bytes the filesize in bytes
|
||||
// @return {String} human-readable string
|
||||
static mem(bytes) {
|
||||
mem(bytes) {
|
||||
if (bytes < 0) {
|
||||
return 'Unknown';
|
||||
}
|
||||
|
@ -55,22 +61,26 @@ export class Formatter {
|
|||
}
|
||||
|
||||
return 'E2BIG';
|
||||
}
|
||||
},
|
||||
|
||||
static ngettext(msgid, msgid_plural, n) {
|
||||
ngettext(msgid, msgid_plural, n) {
|
||||
return plural_rules.select(n) === 'one' ? msgid : msgid_plural;
|
||||
}
|
||||
},
|
||||
|
||||
number(number) {
|
||||
return number_format.format(number);
|
||||
},
|
||||
|
||||
// format a percentage to a string
|
||||
static percentString(x) {
|
||||
percentString(x) {
|
||||
const decimal_places = x < 100 ? 1 : 0;
|
||||
return this._toTruncFixed(x, decimal_places);
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* Format a ratio to a string
|
||||
*/
|
||||
static ratioString(x) {
|
||||
ratioString(x) {
|
||||
if (x === -1) {
|
||||
return 'None';
|
||||
}
|
||||
|
@ -78,32 +88,32 @@ export class Formatter {
|
|||
return '∞';
|
||||
}
|
||||
return this.percentString(x);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Formats the a disk capacity or file size into a human-readable string
|
||||
* @param {Number} bytes the filesize in bytes
|
||||
* @return {String} human-readable string
|
||||
*/
|
||||
static size(bytes) {
|
||||
size(bytes) {
|
||||
return this.mem(bytes);
|
||||
}
|
||||
},
|
||||
|
||||
static speed(KBps) {
|
||||
speed(KBps) {
|
||||
return KBps < 999.95 ? fmt_kBps.format(KBps) : fmt_MBps.format(KBps / 1000);
|
||||
}
|
||||
},
|
||||
|
||||
static speedBps(Bps) {
|
||||
speedBps(Bps) {
|
||||
return this.speed(this.toKBps(Bps));
|
||||
}
|
||||
},
|
||||
|
||||
static timeInterval(seconds) {
|
||||
const days = Math.floor(seconds / 86400);
|
||||
timeInterval(seconds) {
|
||||
const days = Math.floor(seconds / 86_400);
|
||||
if (days) {
|
||||
return this.countString('day', 'days', days);
|
||||
}
|
||||
|
||||
const hours = Math.floor((seconds % 86400) / 3600);
|
||||
const hours = Math.floor((seconds % 86_400) / 3600);
|
||||
if (hours) {
|
||||
return this.countString('hour', 'hours', hours);
|
||||
}
|
||||
|
@ -115,9 +125,9 @@ export class Formatter {
|
|||
|
||||
seconds = Math.floor(seconds % 60);
|
||||
return this.countString('second', 'seconds', seconds);
|
||||
}
|
||||
},
|
||||
|
||||
static timestamp(seconds) {
|
||||
timestamp(seconds) {
|
||||
if (!seconds) {
|
||||
return 'N/A';
|
||||
}
|
||||
|
@ -168,19 +178,9 @@ export class Formatter {
|
|||
time = [hours, minutes, seconds].join(':');
|
||||
|
||||
return [date, time, period].join(' ');
|
||||
}
|
||||
},
|
||||
|
||||
static toKBps(Bps) {
|
||||
toKBps(Bps) {
|
||||
return Math.floor(Bps / kilo);
|
||||
}
|
||||
|
||||
static number(number) {
|
||||
return number_format.format(number);
|
||||
}
|
||||
|
||||
/** Round a string of a number to a specified number of decimal places */
|
||||
static _toTruncFixed(number, places) {
|
||||
const returnValue = Math.floor(number * 10 ** places) / 10 ** places;
|
||||
return returnValue.toFixed(places);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -150,7 +150,7 @@ export class Inspector extends EventTarget {
|
|||
const thead = document.createElement('thead');
|
||||
const tr = document.createElement('tr');
|
||||
const names = ['', 'Up', 'Down', 'Done', 'Status', 'Address', 'Client'];
|
||||
names.forEach((name, index) => {
|
||||
for (const [index, name] of names.entries()) {
|
||||
const th = document.createElement('th');
|
||||
const classname = peer_column_classes[index];
|
||||
if (classname === 'encryption') {
|
||||
|
@ -159,7 +159,7 @@ export class Inspector extends EventTarget {
|
|||
th.classList.add(classname);
|
||||
setTextContent(th, name);
|
||||
tr.append(th);
|
||||
});
|
||||
}
|
||||
const tbody = document.createElement('tbody');
|
||||
thead.append(tr);
|
||||
table.append(thead);
|
||||
|
@ -201,9 +201,13 @@ export class Inspector extends EventTarget {
|
|||
// update the inspector when a selected torrent's data changes.
|
||||
const key = 'dataChanged';
|
||||
const callback = this.torrent_listener;
|
||||
this.torrents.forEach((t) => t.removeEventListener(key, callback));
|
||||
for (const t of this.torrents) {
|
||||
t.removeEventListener(key, callback);
|
||||
}
|
||||
this.torrents = [...torrents];
|
||||
this.torrents.forEach((t) => t.addEventListener(key, callback));
|
||||
for (const t of this.torrents) {
|
||||
t.addEventListener(key, callback);
|
||||
}
|
||||
|
||||
this._refreshTorrents();
|
||||
this._updateCurrentPage();
|
||||
|
@ -228,8 +232,8 @@ export class Inspector extends EventTarget {
|
|||
}
|
||||
|
||||
_updateCurrentPage() {
|
||||
const { elements } = this;
|
||||
switch (this.current_page) {
|
||||
const { current_page, elements } = this;
|
||||
switch (current_page) {
|
||||
case elements.files.root:
|
||||
this._updateFiles();
|
||||
break;
|
||||
|
@ -244,7 +248,7 @@ export class Inspector extends EventTarget {
|
|||
break;
|
||||
default:
|
||||
console.warn('unexpected page');
|
||||
console.log(this.current_page);
|
||||
console.log(current_page);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -254,8 +258,7 @@ export class Inspector extends EventTarget {
|
|||
const unknown = 'Unknown';
|
||||
const fmt = Formatter;
|
||||
const now = Date.now();
|
||||
const { torrents } = this;
|
||||
const e = this.elements;
|
||||
const { elements: e, torrents } = this;
|
||||
const sizeWhenDone = torrents.reduce(
|
||||
(accumulator, t) => accumulator + t.getSizeWhenDone(),
|
||||
0
|
||||
|
@ -502,7 +505,7 @@ export class Inspector extends EventTarget {
|
|||
const date = get(torrents[0]);
|
||||
const mixed_date = !torrents.every((t) => get(t) === date);
|
||||
|
||||
const empty_creator = !creator || !creator.length;
|
||||
const empty_creator = !creator || creator.length === 0;
|
||||
const empty_date = !date;
|
||||
if (mixed_creator || mixed_date) {
|
||||
string = mixed;
|
||||
|
@ -557,8 +560,8 @@ export class Inspector extends EventTarget {
|
|||
|
||||
_updatePeers() {
|
||||
const fmt = Formatter;
|
||||
const { torrents } = this;
|
||||
const { tbody } = this.elements.peers;
|
||||
const { elements, torrents } = this;
|
||||
const { tbody } = elements.peers;
|
||||
|
||||
const cell_setters = [
|
||||
(peer, td) => {
|
||||
|
@ -598,12 +601,12 @@ export class Inspector extends EventTarget {
|
|||
for (const peer of tor.getPeers()) {
|
||||
const tr = document.createElement('tr');
|
||||
tr.classList.add('peer-row');
|
||||
cell_setters.forEach((setter, index) => {
|
||||
for (const [index, setter] of cell_setters.entries()) {
|
||||
const td = document.createElement('td');
|
||||
td.classList.add(peer_column_classes[index]);
|
||||
setter(peer, td);
|
||||
tr.append(td);
|
||||
});
|
||||
}
|
||||
rows.push(tr);
|
||||
}
|
||||
|
||||
|
@ -624,7 +627,7 @@ export class Inspector extends EventTarget {
|
|||
case Torrent._TrackerWaiting: {
|
||||
const timeUntilAnnounce = Math.max(
|
||||
0,
|
||||
tracker.nextAnnounceTime - new Date().getTime() / 1000
|
||||
tracker.nextAnnounceTime - Date.now() / 1000
|
||||
);
|
||||
return `Next announce in ${Formatter.timeInterval(timeUntilAnnounce)}`;
|
||||
}
|
||||
|
@ -706,7 +709,7 @@ export class Inspector extends EventTarget {
|
|||
rows.push(title);
|
||||
}
|
||||
|
||||
tor.getTrackers().forEach((tracker, index) => {
|
||||
for (const [index, tracker] of tor.getTrackers().entries()) {
|
||||
const announceState = Inspector.getAnnounceState(tracker);
|
||||
const lastAnnounceStatusHash = Inspector.lastAnnounceStatus(tracker);
|
||||
const lastScrapeStatusHash = Inspector.lastScrapeStatus(tracker);
|
||||
|
@ -773,7 +776,7 @@ export class Inspector extends EventTarget {
|
|||
tier_div.append(element);
|
||||
|
||||
rows.push(tier_div);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: modify instead of rebuilding wholesale?
|
||||
|
@ -836,7 +839,7 @@ export class Inspector extends EventTarget {
|
|||
file_indices: [],
|
||||
};
|
||||
|
||||
tor.getFiles().forEach((file, index) => {
|
||||
for (const [index, file] of tor.getFiles().entries()) {
|
||||
const { name } = file;
|
||||
const tokens = name.split('/');
|
||||
let walk = tree;
|
||||
|
@ -856,7 +859,7 @@ export class Inspector extends EventTarget {
|
|||
walk.file_index = index;
|
||||
delete walk.children;
|
||||
leaves.push(walk);
|
||||
});
|
||||
}
|
||||
|
||||
for (const leaf of leaves) {
|
||||
const { file_index } = leaf;
|
||||
|
@ -901,7 +904,7 @@ export class Inspector extends EventTarget {
|
|||
|
||||
_updateFiles() {
|
||||
const { list } = this.elements.files;
|
||||
const { torrents } = this;
|
||||
const { file_rows, file_torrent, file_torrent_n, torrents } = this;
|
||||
|
||||
// only show one torrent at a time
|
||||
if (torrents.length !== 1) {
|
||||
|
@ -911,7 +914,7 @@ export class Inspector extends EventTarget {
|
|||
|
||||
const [tor] = torrents;
|
||||
const n = tor.getFiles().length;
|
||||
if (tor !== this.file_torrent || n !== this.file_torrent_n) {
|
||||
if (tor !== file_torrent || n !== file_torrent_n) {
|
||||
// rebuild the file list...
|
||||
this._clearFileList();
|
||||
this.file_torrent = tor;
|
||||
|
@ -923,7 +926,9 @@ export class Inspector extends EventTarget {
|
|||
list.append(fragment);
|
||||
} else {
|
||||
// ...refresh the already-existing file list
|
||||
this.file_rows.forEach((row) => row.refresh());
|
||||
for (const row of file_rows) {
|
||||
row.refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,8 +53,8 @@ export class OpenDialog extends EventTarget {
|
|||
}
|
||||
|
||||
_onConfirm() {
|
||||
const { remote } = this;
|
||||
const { file_input, folder_input, start_input, url_input } = this.elements;
|
||||
const { controller, elements, remote } = this;
|
||||
const { file_input, folder_input, start_input, url_input } = elements;
|
||||
const paused = !start_input.checked;
|
||||
const destination = folder_input.value.trim();
|
||||
|
||||
|
@ -79,7 +79,7 @@ export class OpenDialog extends EventTarget {
|
|||
remote.sendRequest(o, (response) => {
|
||||
if (response.result !== 'success') {
|
||||
alert(`Error adding "${file.name}": ${response.result}`);
|
||||
this.controller.setCurrentPopup(
|
||||
controller.setCurrentPopup(
|
||||
new AlertDialog({
|
||||
heading: `Error adding "${file.name}"`,
|
||||
message: response.result,
|
||||
|
@ -93,7 +93,7 @@ export class OpenDialog extends EventTarget {
|
|||
|
||||
let url = url_input.value.trim();
|
||||
if (url.length > 0) {
|
||||
if (url.match(/^[\da-f]{40}$/i)) {
|
||||
if (/^[\da-f]{40}$/i.test(url)) {
|
||||
url = `magnet:?xt=urn:btih:${url}`;
|
||||
}
|
||||
const o = {
|
||||
|
@ -107,7 +107,7 @@ export class OpenDialog extends EventTarget {
|
|||
console.log(o);
|
||||
remote.sendRequest(o, (payload, response) => {
|
||||
if (response.result !== 'success') {
|
||||
this.controller.setCurrentPopup(
|
||||
controller.setCurrentPopup(
|
||||
new AlertDialog({
|
||||
heading: `Error adding "${url}"`,
|
||||
message: response.result,
|
||||
|
|
|
@ -318,7 +318,7 @@ export class OverflowMenu extends EventTarget {
|
|||
select.addEventListener('change', (event_) => {
|
||||
const { value } = event_.target;
|
||||
console.log(event_);
|
||||
if (event_.target.value === unlimited) {
|
||||
if (value === unlimited) {
|
||||
this.remote.savePrefs({ [RPC._UpSpeedLimited]: false });
|
||||
} else {
|
||||
this.remote.savePrefs({
|
||||
|
@ -361,7 +361,7 @@ export class OverflowMenu extends EventTarget {
|
|||
select.addEventListener('change', (event_) => {
|
||||
const { value } = event_.target;
|
||||
console.log(event_);
|
||||
if (event_.target.value === unlimited) {
|
||||
if (value === unlimited) {
|
||||
this.remote.savePrefs({ [RPC._DownSpeedLimited]: false });
|
||||
} else {
|
||||
this.remote.savePrefs({
|
||||
|
@ -460,7 +460,8 @@ export class OverflowMenu extends EventTarget {
|
|||
e.textContent = 'Source Code';
|
||||
options.append(e);
|
||||
|
||||
Object.values(actions).forEach(this._updateElement.bind(this));
|
||||
this._updateElement = this._updateElement.bind(this);
|
||||
|
||||
return { actions, elements, root };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,6 +66,7 @@ export class Prefs extends EventTarget {
|
|||
static _setCookie(key, value) {
|
||||
const date = new Date();
|
||||
date.setFullYear(date.getFullYear() + 1);
|
||||
// eslint-disable-next-line unicorn/no-document-cookie
|
||||
document.cookie = `${key}=${value}; SameSite=Strict; expires=${date.toGMTString()}; path=/`;
|
||||
}
|
||||
|
||||
|
@ -80,7 +81,7 @@ export class Prefs extends EventTarget {
|
|||
if (value === 'false') {
|
||||
return false;
|
||||
}
|
||||
if (value.match(/^\d+$/)) {
|
||||
if (/^\d+$/.test(value)) {
|
||||
return Number.parseInt(value, 10);
|
||||
}
|
||||
return value;
|
||||
|
|
|
@ -246,7 +246,7 @@ export class Remote {
|
|||
);
|
||||
}
|
||||
addTorrentByUrl(url, options) {
|
||||
if (url.match(/^[\da-f]{40}$/i)) {
|
||||
if (/^[\da-f]{40}$/i.test(url)) {
|
||||
url = `magnet:?xt=urn:btih:${url}`;
|
||||
}
|
||||
const o = {
|
||||
|
|
|
@ -38,12 +38,12 @@ export class RemoveDialog extends EventTarget {
|
|||
}
|
||||
|
||||
_onConfirm() {
|
||||
const { torrents } = this.options;
|
||||
const { remote, torrents, trash } = this.options;
|
||||
if (torrents.length > 0) {
|
||||
if (this.options.trash) {
|
||||
this.options.remote.removeTorrentsAndData(torrents);
|
||||
if (trash) {
|
||||
remote.removeTorrentsAndData(torrents);
|
||||
} else {
|
||||
this.options.remote.removeTorrents(torrents);
|
||||
remote.removeTorrents(torrents);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,13 +64,13 @@ export class RemoveDialog extends EventTarget {
|
|||
static _createMessage(options) {
|
||||
let heading = null;
|
||||
let message = null;
|
||||
const { torrents } = options;
|
||||
const { torrents, trash } = options;
|
||||
const [torrent] = torrents;
|
||||
if (options.trash && torrents.length === 1) {
|
||||
if (trash && torrents.length === 1) {
|
||||
heading = `Remove ${torrent.getName()} and delete data?`;
|
||||
message =
|
||||
'All data downloaded for this torrent will be deleted. Are you sure you want to remove it?';
|
||||
} else if (options.trash) {
|
||||
} else if (trash) {
|
||||
heading = `Remove ${torrents.length} transfers and delete data?`;
|
||||
message =
|
||||
'All data downloaded for these torrents will be deleted. Are you sure you want to remove them?';
|
||||
|
|
|
@ -72,14 +72,14 @@ export class StatisticsDialog extends EventTarget {
|
|||
|
||||
static _create() {
|
||||
const elements = createDialogContainer('statistics-dialog');
|
||||
const { workarea } = elements;
|
||||
elements.confirm.remove();
|
||||
elements.dismiss.textContent = 'Close';
|
||||
const { confirm, dismiss, heading, root, workarea } = elements;
|
||||
confirm.remove();
|
||||
dismiss.textContent = 'Close';
|
||||
delete elements.confirm;
|
||||
|
||||
const heading_text = 'Statistics';
|
||||
elements.root.setAttribute('aria-label', heading_text);
|
||||
elements.heading.textContent = heading_text;
|
||||
root.setAttribute('aria-label', heading_text);
|
||||
heading.textContent = heading_text;
|
||||
|
||||
const labels = ['Uploaded:', 'Downloaded:', 'Ratio:', 'Running time:'];
|
||||
let section = createInfoSection('Current session', labels);
|
||||
|
|
|
@ -11,8 +11,21 @@ import { Formatter } from './formatter.js';
|
|||
import { Torrent } from './torrent.js';
|
||||
import { setTextContent } from './utils.js';
|
||||
|
||||
class TorrentRendererHelper {
|
||||
static getProgressInfo(controller, t) {
|
||||
const TorrentRendererHelper = {
|
||||
formatDL: (t) => {
|
||||
return `▼${Formatter.speedBps(t.getDownloadSpeed())}`;
|
||||
},
|
||||
formatETA: (t) => {
|
||||
const eta = t.getETA();
|
||||
if (eta < 0 || eta >= 999 * 60 * 60) {
|
||||
return '';
|
||||
}
|
||||
return `ETA: ${Formatter.timeInterval(eta)}`;
|
||||
},
|
||||
formatUL: (t) => {
|
||||
return `▲${Formatter.speedBps(t.getUploadSpeed())}`;
|
||||
},
|
||||
getProgressInfo: (controller, t) => {
|
||||
const status = t.getStatus();
|
||||
const classList = ['torrent-progress-bar'];
|
||||
let percent = null;
|
||||
|
@ -46,29 +59,14 @@ class TorrentRendererHelper {
|
|||
classList,
|
||||
percent,
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
static renderProgressbar(controller, t, progressbar) {
|
||||
renderProgressbar: (controller, t, progressbar) => {
|
||||
const info = TorrentRendererHelper.getProgressInfo(controller, t);
|
||||
progressbar.className = info.classList.join(' ');
|
||||
progressbar.style['background-size'] = `${info.percent}% 100%, 100% 100%`;
|
||||
}
|
||||
|
||||
static formatUL(t) {
|
||||
return `▲${Formatter.speedBps(t.getUploadSpeed())}`;
|
||||
}
|
||||
static formatDL(t) {
|
||||
return `▼${Formatter.speedBps(t.getDownloadSpeed())}`;
|
||||
}
|
||||
|
||||
static formatETA(t) {
|
||||
const eta = t.getETA();
|
||||
if (eta < 0 || eta >= 999 * 60 * 60) {
|
||||
return '';
|
||||
}
|
||||
return `ETA: ${Formatter.timeInterval(eta)}`;
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
///
|
||||
|
||||
|
|
|
@ -256,9 +256,9 @@ export class Transmission extends EventTarget {
|
|||
this.prefs.addEventListener('change', ({ key, value }) =>
|
||||
this._onPrefChanged(key, value)
|
||||
);
|
||||
this.prefs
|
||||
.entries()
|
||||
.forEach(([key, value]) => this._onPrefChanged(key, value));
|
||||
for (const [key, value] of this.prefs.entries()) {
|
||||
this._onPrefChanged(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
loadDaemonPrefs() {
|
||||
|
@ -419,9 +419,9 @@ export class Transmission extends EventTarget {
|
|||
_dispatchSelectionChanged() {
|
||||
const nonselected = [];
|
||||
const selected = [];
|
||||
this._rows.forEach((r) =>
|
||||
(r.isSelected() ? selected : nonselected).push(r.getTorrent())
|
||||
);
|
||||
for (const r of this._rows) {
|
||||
(r.isSelected() ? selected : nonselected).push(r.getTorrent());
|
||||
}
|
||||
|
||||
const event = new Event('torrent-selection-changed');
|
||||
event.nonselected = nonselected;
|
||||
|
@ -455,7 +455,7 @@ export class Transmission extends EventTarget {
|
|||
|
||||
// Process key events
|
||||
_keyDown(event_) {
|
||||
const { keyCode } = event_;
|
||||
const { ctrlKey, keyCode, metaKey, shiftKey, target } = event_;
|
||||
|
||||
// look for a shortcut
|
||||
const aria_keys = Transmission._createKeyShortcutFromKeyboardEvent(event_);
|
||||
|
@ -474,19 +474,14 @@ export class Transmission extends EventTarget {
|
|||
}
|
||||
|
||||
const any_popup_active = document.querySelector('.popup:not(.hidden)');
|
||||
const is_input_focused = event_.target.matches('input');
|
||||
const is_input_focused = target.matches('input');
|
||||
const rows = this._rows;
|
||||
|
||||
// Some shortcuts can only be used if the following conditions are met:
|
||||
// 1. when no input fields are focused
|
||||
// 2. when no other dialogs are visible
|
||||
// 3. when the meta or ctrl key isn't pressed (i.e. opening dev tools shouldn't trigger the info panel)
|
||||
if (
|
||||
!is_input_focused &&
|
||||
!any_popup_active &&
|
||||
!event_.metaKey &&
|
||||
!event_.ctrlKey
|
||||
) {
|
||||
if (!is_input_focused && !any_popup_active && !metaKey && !ctrlKey) {
|
||||
const shift_key = keyCode === 16; // shift key pressed
|
||||
const up_key = keyCode === 38; // up key pressed
|
||||
const dn_key = keyCode === 40; // down key pressed
|
||||
|
@ -520,7 +515,7 @@ export class Transmission extends EventTarget {
|
|||
this._deselectRow(rows[last]);
|
||||
}
|
||||
} else {
|
||||
if (event_.shiftKey) {
|
||||
if (shiftKey) {
|
||||
this._selectRange(r);
|
||||
} else {
|
||||
this._setSelectedRow(r);
|
||||
|
@ -582,12 +577,13 @@ export class Transmission extends EventTarget {
|
|||
const type = event_.data.Transfer.types
|
||||
.filter((t) => ['text/uri-list', 'text/plain'].contains(t))
|
||||
.pop();
|
||||
event_.dataTransfer
|
||||
for (const uri of event_.dataTransfer
|
||||
.getData(type)
|
||||
.split('\n')
|
||||
.map((string) => string.trim())
|
||||
.filter((string) => Transmission._isValidURL(string))
|
||||
.forEach((uri) => this.remote.addTorrentByUrl(uri, paused));
|
||||
.filter((string) => Transmission._isValidURL(string))) {
|
||||
this.remote.addTorrentByUrl(uri, paused);
|
||||
}
|
||||
|
||||
event_.preventDefault();
|
||||
return false;
|
||||
|
@ -632,9 +628,9 @@ export class Transmission extends EventTarget {
|
|||
const keys = table.shift();
|
||||
const o = {};
|
||||
for (const row of table) {
|
||||
keys.forEach((key, index) => {
|
||||
for (const [index, key] of keys.entries()) {
|
||||
o[key] = row[index];
|
||||
});
|
||||
}
|
||||
const { id } = o;
|
||||
let t = this._torrents[id];
|
||||
if (t) {
|
||||
|
@ -656,10 +652,11 @@ export class Transmission extends EventTarget {
|
|||
|
||||
if (needinfo.length > 0) {
|
||||
// whee, new torrents! get their initial information.
|
||||
const more_fields = ['id'].concat(
|
||||
Torrent.Fields.Metadata,
|
||||
Torrent.Fields.Stats
|
||||
);
|
||||
const more_fields = [
|
||||
'id',
|
||||
...Torrent.Fields.Metadata,
|
||||
...Torrent.Fields.Stats,
|
||||
];
|
||||
this.updateTorrents(needinfo, more_fields);
|
||||
this.refilterSoon();
|
||||
}
|
||||
|
@ -691,12 +688,12 @@ TODO: fix this when notifications get fixed
|
|||
*/
|
||||
|
||||
refreshTorrents() {
|
||||
const fields = ['id'].concat(Torrent.Fields.Stats);
|
||||
const fields = ['id', ...Torrent.Fields.Stats];
|
||||
this.updateTorrents('recently-active', fields);
|
||||
}
|
||||
|
||||
_initializeTorrents() {
|
||||
const fields = ['id'].concat(Torrent.Fields.Metadata, Torrent.Fields.Stats);
|
||||
const fields = ['id', ...Torrent.Fields.Metadata, ...Torrent.Fields.Stats];
|
||||
this.updateTorrents(null, fields);
|
||||
}
|
||||
|
||||
|
@ -910,7 +907,9 @@ TODO: fix this when notifications get fixed
|
|||
this.prefs.sort_mode,
|
||||
this.prefs.sort_direction
|
||||
);
|
||||
torrents.forEach((tor, index) => (rows[index] = id2row[tor.getId()]));
|
||||
for (const [index, tor] of torrents.entries()) {
|
||||
rows[index] = id2row[tor.getId()];
|
||||
}
|
||||
}
|
||||
|
||||
_refilter(rebuildEverything) {
|
||||
|
@ -1036,13 +1035,11 @@ TODO: fix this when notifications get fixed
|
|||
this.dirtyTorrents.clear();
|
||||
|
||||
// set the odd/even property
|
||||
rows
|
||||
.map((row) => row.getElement())
|
||||
.forEach((e, index) => {
|
||||
const even = index % 2 === 0;
|
||||
e.classList.toggle('even', even);
|
||||
e.classList.toggle('odd', !even);
|
||||
});
|
||||
for (const [index, e] of rows.map((row) => row.getElement()).entries()) {
|
||||
const even = index % 2 === 0;
|
||||
e.classList.toggle('even', even);
|
||||
e.classList.toggle('odd', !even);
|
||||
}
|
||||
|
||||
this._updateStatusbar();
|
||||
if (
|
||||
|
|
|
@ -9,26 +9,9 @@
|
|||
|
||||
import isEqual from 'lodash.isequal';
|
||||
|
||||
export class Utils {
|
||||
/**
|
||||
* Checks to see if the content actually changed before poking the DOM.
|
||||
*/
|
||||
static setInnerHTML(e, html) {
|
||||
if (!e) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* innerHTML is listed as a string, but the browser seems to change it.
|
||||
* For example, "∞" gets changed to "∞" somewhere down the line.
|
||||
* So, let's use an arbitrary different field to test our state... */
|
||||
if (e.currentHTML !== html) {
|
||||
e.currentHTML = html;
|
||||
e.innerHTML = html;
|
||||
}
|
||||
}
|
||||
|
||||
export const Utils = {
|
||||
/** Given a numerator and denominator, return a ratio string */
|
||||
static ratio(numerator, denominator) {
|
||||
ratio(numerator, denominator) {
|
||||
let result = Math.floor((100 * numerator) / denominator) / 100;
|
||||
|
||||
// check for special cases
|
||||
|
@ -42,8 +25,25 @@ export class Utils {
|
|||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks to see if the content actually changed before poking the DOM.
|
||||
*/
|
||||
setInnerHTML(e, html) {
|
||||
if (!e) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* innerHTML is listed as a string, but the browser seems to change it.
|
||||
* For example, "∞" gets changed to "∞" somewhere down the line.
|
||||
* So, let's use an arbitrary different field to test our state... */
|
||||
if (e.currentHTML !== html) {
|
||||
e.currentHTML = html;
|
||||
e.innerHTML = html;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export function createTabsContainer(id, tabs, callback) {
|
||||
const root = document.createElement('div');
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const path = require('path');
|
||||
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
|
||||
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
|
||||
const TerserPlugin = require('terser-webpack-plugin');
|
||||
const webpack = require('webpack');
|
||||
|
||||
|
@ -45,11 +45,7 @@ const config = {
|
|||
optimization: {
|
||||
minimizer: [
|
||||
new TerserPlugin(),
|
||||
new OptimizeCSSAssetsPlugin({
|
||||
cssProcessorPluginOptions: {
|
||||
preset: ['default', { discardComments: { removeAll: true } }],
|
||||
}
|
||||
})
|
||||
new CssMinimizerPlugin(),
|
||||
],
|
||||
},
|
||||
output: {
|
||||
|
|
4479
web/yarn.lock
4479
web/yarn.lock
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue