chore: bump web client deps (#1698)

* chore: bump web client dependencies
This commit is contained in:
Charles Kerr
2021-05-19 08:43:46 -05:00
committed by GitHub
parent 95a45b0bad
commit d2473f4c2f
17 changed files with 2148 additions and 2719 deletions

View File

@@ -119,7 +119,8 @@ module.exports = {
"sonarjs/no-duplicate-string": "off", "sonarjs/no-duplicate-string": "off",
"sort-keys": "error", "sort-keys": "error",
"strict": "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-fn-reference-in-iterator": "off",
"unicorn/no-null": "off", "unicorn/no-null": "off",
"unicorn/no-reduce": "off", "unicorn/no-reduce": "off",

View File

@@ -18,36 +18,36 @@
"lint:stylelint:fix": "stylelint --fix style/*scss" "lint:stylelint:fix": "stylelint --fix style/*scss"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.11.6", "@babel/core": "^7.14.3",
"@babel/eslint-parser": "^7.11.5", "@babel/eslint-parser": "^7.14.3",
"@babel/plugin-proposal-class-properties": "^7.10.4", "@babel/plugin-proposal-class-properties": "^7.13.0",
"css-loader": "^4.3.0", "css-loader": "^5.2.4",
"eslint": "^7.11.0", "css-minimizer-webpack-plugin": "^3.0.0",
"eslint-plugin-sonarjs": "^0.5.0", "eslint": "^7.26.0",
"eslint-plugin-unicorn": "^23.0.0", "eslint-plugin-sonarjs": "^0.7.0",
"file-loader": "^6.1.0", "eslint-plugin-unicorn": "^32.0.1",
"file-loader": "^6.2.0",
"img-optimize-loader": "^1.0.7", "img-optimize-loader": "^1.0.7",
"mini-css-extract-plugin": "^0.11.3", "mini-css-extract-plugin": "^1.6.0",
"node-sass": "^4.14.1", "node-sass": "^6.0.0",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"optimize-css-assets-webpack-plugin": "^5.0.4", "prettier": "^2.3.0",
"prettier": "^2.1.2", "sass": "^1.32.13",
"sass": "^1.26.11", "sass-loader": "^11.1.1",
"sass-loader": "^10.0.2", "style-loader": "^2.0.0",
"style-loader": "^1.2.1", "stylelint": "^13.13.1",
"stylelint": "^13.7.1",
"stylelint-config-prettier": "^8.0.2", "stylelint-config-prettier": "^8.0.2",
"stylelint-config-primer": "^9.2.1", "stylelint-config-primer": "^11.0.1",
"stylelint-config-sass-guidelines": "^7.1.0", "stylelint-config-sass-guidelines": "^8.0.0",
"stylelint-config-standard": "^20.0.0", "stylelint-config-standard": "^22.0.0",
"svgo": "^1.3.2", "svgo": "^2.3.0",
"svgo-loader": "^2.2.1", "svgo-loader": "^3.0.0",
"terser-webpack-plugin": "^4.2.2", "terser-webpack-plugin": "^5.1.2",
"url-loader": "^4.1.0", "url-loader": "^4.1.1",
"webpack": "^4.44.2", "webpack": "^5.37.0",
"webpack-bundle-analyzer": "^3.9.0", "webpack-bundle-analyzer": "^4.4.2",
"webpack-cli": "^3.3.12", "webpack-cli": "^4.7.0",
"webpack-dev-server": "^3.11.0" "webpack-dev-server": "^3.11.2"
}, },
"dependencies": { "dependencies": {
"lodash.isequal": "^4.5.0" "lodash.isequal": "^4.5.0"

File diff suppressed because one or more lines are too long

View File

@@ -137,11 +137,12 @@ export class ActionManager extends EventTarget {
} }
static _recount(selected, nonselected) { static _recount(selected, nonselected) {
const test = (tor) => tor.isStopped();
const total = selected.length + nonselected.length; 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 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 nonselected_active = nonselected.length - nonselected_paused;
const paused = selected_paused + nonselected_paused; const paused = selected_paused + nonselected_paused;
const active = selected_active + nonselected_active; const active = selected_active + nonselected_active;

View File

@@ -30,15 +30,21 @@ const fmt_MBps = new Intl.NumberFormat(current_locale, {
unit: 'megabyte-per-second', unit: 'megabyte-per-second',
}); });
export class Formatter { export const Formatter = {
static countString(msgid, msgid_plural, n) { /** 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)}`; return `${this.number(n)} ${this.ngettext(msgid, msgid_plural, n)}`;
} },
// Formats the a memory size into a human-readable string // Formats the a memory size into a human-readable string
// @param {Number} bytes the filesize in bytes // @param {Number} bytes the filesize in bytes
// @return {String} human-readable string // @return {String} human-readable string
static mem(bytes) { mem(bytes) {
if (bytes < 0) { if (bytes < 0) {
return 'Unknown'; return 'Unknown';
} }
@@ -55,22 +61,26 @@ export class Formatter {
} }
return 'E2BIG'; return 'E2BIG';
} },
static ngettext(msgid, msgid_plural, n) { ngettext(msgid, msgid_plural, n) {
return plural_rules.select(n) === 'one' ? msgid : msgid_plural; return plural_rules.select(n) === 'one' ? msgid : msgid_plural;
} },
number(number) {
return number_format.format(number);
},
// format a percentage to a string // format a percentage to a string
static percentString(x) { percentString(x) {
const decimal_places = x < 100 ? 1 : 0; const decimal_places = x < 100 ? 1 : 0;
return this._toTruncFixed(x, decimal_places); return this._toTruncFixed(x, decimal_places);
} },
/* /*
* Format a ratio to a string * Format a ratio to a string
*/ */
static ratioString(x) { ratioString(x) {
if (x === -1) { if (x === -1) {
return 'None'; return 'None';
} }
@@ -78,32 +88,32 @@ export class Formatter {
return '&infin;'; return '&infin;';
} }
return this.percentString(x); return this.percentString(x);
} },
/** /**
* Formats the a disk capacity or file size into a human-readable string * Formats the a disk capacity or file size into a human-readable string
* @param {Number} bytes the filesize in bytes * @param {Number} bytes the filesize in bytes
* @return {String} human-readable string * @return {String} human-readable string
*/ */
static size(bytes) { size(bytes) {
return this.mem(bytes); return this.mem(bytes);
} },
static speed(KBps) { speed(KBps) {
return KBps < 999.95 ? fmt_kBps.format(KBps) : fmt_MBps.format(KBps / 1000); return KBps < 999.95 ? fmt_kBps.format(KBps) : fmt_MBps.format(KBps / 1000);
} },
static speedBps(Bps) { speedBps(Bps) {
return this.speed(this.toKBps(Bps)); return this.speed(this.toKBps(Bps));
} },
static timeInterval(seconds) { timeInterval(seconds) {
const days = Math.floor(seconds / 86400); const days = Math.floor(seconds / 86_400);
if (days) { if (days) {
return this.countString('day', 'days', days); return this.countString('day', 'days', days);
} }
const hours = Math.floor((seconds % 86400) / 3600); const hours = Math.floor((seconds % 86_400) / 3600);
if (hours) { if (hours) {
return this.countString('hour', 'hours', hours); return this.countString('hour', 'hours', hours);
} }
@@ -115,9 +125,9 @@ export class Formatter {
seconds = Math.floor(seconds % 60); seconds = Math.floor(seconds % 60);
return this.countString('second', 'seconds', seconds); return this.countString('second', 'seconds', seconds);
} },
static timestamp(seconds) { timestamp(seconds) {
if (!seconds) { if (!seconds) {
return 'N/A'; return 'N/A';
} }
@@ -168,19 +178,9 @@ export class Formatter {
time = [hours, minutes, seconds].join(':'); time = [hours, minutes, seconds].join(':');
return [date, time, period].join(' '); return [date, time, period].join(' ');
} },
static toKBps(Bps) { toKBps(Bps) {
return Math.floor(Bps / kilo); 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);
}
}

View File

@@ -150,7 +150,7 @@ export class Inspector extends EventTarget {
const thead = document.createElement('thead'); const thead = document.createElement('thead');
const tr = document.createElement('tr'); const tr = document.createElement('tr');
const names = ['', 'Up', 'Down', 'Done', 'Status', 'Address', 'Client']; 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 th = document.createElement('th');
const classname = peer_column_classes[index]; const classname = peer_column_classes[index];
if (classname === 'encryption') { if (classname === 'encryption') {
@@ -159,7 +159,7 @@ export class Inspector extends EventTarget {
th.classList.add(classname); th.classList.add(classname);
setTextContent(th, name); setTextContent(th, name);
tr.append(th); tr.append(th);
}); }
const tbody = document.createElement('tbody'); const tbody = document.createElement('tbody');
thead.append(tr); thead.append(tr);
table.append(thead); table.append(thead);
@@ -201,9 +201,13 @@ export class Inspector extends EventTarget {
// update the inspector when a selected torrent's data changes. // update the inspector when a selected torrent's data changes.
const key = 'dataChanged'; const key = 'dataChanged';
const callback = this.torrent_listener; 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 = [...torrents];
this.torrents.forEach((t) => t.addEventListener(key, callback)); for (const t of this.torrents) {
t.addEventListener(key, callback);
}
this._refreshTorrents(); this._refreshTorrents();
this._updateCurrentPage(); this._updateCurrentPage();
@@ -228,8 +232,8 @@ export class Inspector extends EventTarget {
} }
_updateCurrentPage() { _updateCurrentPage() {
const { elements } = this; const { current_page, elements } = this;
switch (this.current_page) { switch (current_page) {
case elements.files.root: case elements.files.root:
this._updateFiles(); this._updateFiles();
break; break;
@@ -244,7 +248,7 @@ export class Inspector extends EventTarget {
break; break;
default: default:
console.warn('unexpected page'); 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 unknown = 'Unknown';
const fmt = Formatter; const fmt = Formatter;
const now = Date.now(); const now = Date.now();
const { torrents } = this; const { elements: e, torrents } = this;
const e = this.elements;
const sizeWhenDone = torrents.reduce( const sizeWhenDone = torrents.reduce(
(accumulator, t) => accumulator + t.getSizeWhenDone(), (accumulator, t) => accumulator + t.getSizeWhenDone(),
0 0
@@ -502,7 +505,7 @@ export class Inspector extends EventTarget {
const date = get(torrents[0]); const date = get(torrents[0]);
const mixed_date = !torrents.every((t) => get(t) === date); 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; const empty_date = !date;
if (mixed_creator || mixed_date) { if (mixed_creator || mixed_date) {
string = mixed; string = mixed;
@@ -557,8 +560,8 @@ export class Inspector extends EventTarget {
_updatePeers() { _updatePeers() {
const fmt = Formatter; const fmt = Formatter;
const { torrents } = this; const { elements, torrents } = this;
const { tbody } = this.elements.peers; const { tbody } = elements.peers;
const cell_setters = [ const cell_setters = [
(peer, td) => { (peer, td) => {
@@ -598,12 +601,12 @@ export class Inspector extends EventTarget {
for (const peer of tor.getPeers()) { for (const peer of tor.getPeers()) {
const tr = document.createElement('tr'); const tr = document.createElement('tr');
tr.classList.add('peer-row'); tr.classList.add('peer-row');
cell_setters.forEach((setter, index) => { for (const [index, setter] of cell_setters.entries()) {
const td = document.createElement('td'); const td = document.createElement('td');
td.classList.add(peer_column_classes[index]); td.classList.add(peer_column_classes[index]);
setter(peer, td); setter(peer, td);
tr.append(td); tr.append(td);
}); }
rows.push(tr); rows.push(tr);
} }
@@ -624,7 +627,7 @@ export class Inspector extends EventTarget {
case Torrent._TrackerWaiting: { case Torrent._TrackerWaiting: {
const timeUntilAnnounce = Math.max( const timeUntilAnnounce = Math.max(
0, 0,
tracker.nextAnnounceTime - new Date().getTime() / 1000 tracker.nextAnnounceTime - Date.now() / 1000
); );
return `Next announce in ${Formatter.timeInterval(timeUntilAnnounce)}`; return `Next announce in ${Formatter.timeInterval(timeUntilAnnounce)}`;
} }
@@ -706,7 +709,7 @@ export class Inspector extends EventTarget {
rows.push(title); rows.push(title);
} }
tor.getTrackers().forEach((tracker, index) => { for (const [index, tracker] of tor.getTrackers().entries()) {
const announceState = Inspector.getAnnounceState(tracker); const announceState = Inspector.getAnnounceState(tracker);
const lastAnnounceStatusHash = Inspector.lastAnnounceStatus(tracker); const lastAnnounceStatusHash = Inspector.lastAnnounceStatus(tracker);
const lastScrapeStatusHash = Inspector.lastScrapeStatus(tracker); const lastScrapeStatusHash = Inspector.lastScrapeStatus(tracker);
@@ -773,7 +776,7 @@ export class Inspector extends EventTarget {
tier_div.append(element); tier_div.append(element);
rows.push(tier_div); rows.push(tier_div);
}); }
} }
// TODO: modify instead of rebuilding wholesale? // TODO: modify instead of rebuilding wholesale?
@@ -836,7 +839,7 @@ export class Inspector extends EventTarget {
file_indices: [], file_indices: [],
}; };
tor.getFiles().forEach((file, index) => { for (const [index, file] of tor.getFiles().entries()) {
const { name } = file; const { name } = file;
const tokens = name.split('/'); const tokens = name.split('/');
let walk = tree; let walk = tree;
@@ -856,7 +859,7 @@ export class Inspector extends EventTarget {
walk.file_index = index; walk.file_index = index;
delete walk.children; delete walk.children;
leaves.push(walk); leaves.push(walk);
}); }
for (const leaf of leaves) { for (const leaf of leaves) {
const { file_index } = leaf; const { file_index } = leaf;
@@ -901,7 +904,7 @@ export class Inspector extends EventTarget {
_updateFiles() { _updateFiles() {
const { list } = this.elements.files; 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 // only show one torrent at a time
if (torrents.length !== 1) { if (torrents.length !== 1) {
@@ -911,7 +914,7 @@ export class Inspector extends EventTarget {
const [tor] = torrents; const [tor] = torrents;
const n = tor.getFiles().length; 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... // rebuild the file list...
this._clearFileList(); this._clearFileList();
this.file_torrent = tor; this.file_torrent = tor;
@@ -923,7 +926,9 @@ export class Inspector extends EventTarget {
list.append(fragment); list.append(fragment);
} else { } else {
// ...refresh the already-existing file list // ...refresh the already-existing file list
this.file_rows.forEach((row) => row.refresh()); for (const row of file_rows) {
row.refresh();
}
} }
} }
} }

View File

@@ -53,8 +53,8 @@ export class OpenDialog extends EventTarget {
} }
_onConfirm() { _onConfirm() {
const { remote } = this; const { controller, elements, remote } = this;
const { file_input, folder_input, start_input, url_input } = this.elements; const { file_input, folder_input, start_input, url_input } = elements;
const paused = !start_input.checked; const paused = !start_input.checked;
const destination = folder_input.value.trim(); const destination = folder_input.value.trim();
@@ -79,7 +79,7 @@ export class OpenDialog extends EventTarget {
remote.sendRequest(o, (response) => { remote.sendRequest(o, (response) => {
if (response.result !== 'success') { if (response.result !== 'success') {
alert(`Error adding "${file.name}": ${response.result}`); alert(`Error adding "${file.name}": ${response.result}`);
this.controller.setCurrentPopup( controller.setCurrentPopup(
new AlertDialog({ new AlertDialog({
heading: `Error adding "${file.name}"`, heading: `Error adding "${file.name}"`,
message: response.result, message: response.result,
@@ -93,7 +93,7 @@ export class OpenDialog extends EventTarget {
let url = url_input.value.trim(); let url = url_input.value.trim();
if (url.length > 0) { if (url.length > 0) {
if (url.match(/^[\da-f]{40}$/i)) { if (/^[\da-f]{40}$/i.test(url)) {
url = `magnet:?xt=urn:btih:${url}`; url = `magnet:?xt=urn:btih:${url}`;
} }
const o = { const o = {
@@ -107,7 +107,7 @@ export class OpenDialog extends EventTarget {
console.log(o); console.log(o);
remote.sendRequest(o, (payload, response) => { remote.sendRequest(o, (payload, response) => {
if (response.result !== 'success') { if (response.result !== 'success') {
this.controller.setCurrentPopup( controller.setCurrentPopup(
new AlertDialog({ new AlertDialog({
heading: `Error adding "${url}"`, heading: `Error adding "${url}"`,
message: response.result, message: response.result,

View File

@@ -318,7 +318,7 @@ export class OverflowMenu extends EventTarget {
select.addEventListener('change', (event_) => { select.addEventListener('change', (event_) => {
const { value } = event_.target; const { value } = event_.target;
console.log(event_); console.log(event_);
if (event_.target.value === unlimited) { if (value === unlimited) {
this.remote.savePrefs({ [RPC._UpSpeedLimited]: false }); this.remote.savePrefs({ [RPC._UpSpeedLimited]: false });
} else { } else {
this.remote.savePrefs({ this.remote.savePrefs({
@@ -361,7 +361,7 @@ export class OverflowMenu extends EventTarget {
select.addEventListener('change', (event_) => { select.addEventListener('change', (event_) => {
const { value } = event_.target; const { value } = event_.target;
console.log(event_); console.log(event_);
if (event_.target.value === unlimited) { if (value === unlimited) {
this.remote.savePrefs({ [RPC._DownSpeedLimited]: false }); this.remote.savePrefs({ [RPC._DownSpeedLimited]: false });
} else { } else {
this.remote.savePrefs({ this.remote.savePrefs({
@@ -460,7 +460,8 @@ export class OverflowMenu extends EventTarget {
e.textContent = 'Source Code'; e.textContent = 'Source Code';
options.append(e); options.append(e);
Object.values(actions).forEach(this._updateElement.bind(this)); this._updateElement = this._updateElement.bind(this);
return { actions, elements, root }; return { actions, elements, root };
} }
} }

View File

@@ -66,6 +66,7 @@ export class Prefs extends EventTarget {
static _setCookie(key, value) { static _setCookie(key, value) {
const date = new Date(); const date = new Date();
date.setFullYear(date.getFullYear() + 1); date.setFullYear(date.getFullYear() + 1);
// eslint-disable-next-line unicorn/no-document-cookie
document.cookie = `${key}=${value}; SameSite=Strict; expires=${date.toGMTString()}; path=/`; document.cookie = `${key}=${value}; SameSite=Strict; expires=${date.toGMTString()}; path=/`;
} }
@@ -80,7 +81,7 @@ export class Prefs extends EventTarget {
if (value === 'false') { if (value === 'false') {
return false; return false;
} }
if (value.match(/^\d+$/)) { if (/^\d+$/.test(value)) {
return Number.parseInt(value, 10); return Number.parseInt(value, 10);
} }
return value; return value;

View File

@@ -246,7 +246,7 @@ export class Remote {
); );
} }
addTorrentByUrl(url, options) { addTorrentByUrl(url, options) {
if (url.match(/^[\da-f]{40}$/i)) { if (/^[\da-f]{40}$/i.test(url)) {
url = `magnet:?xt=urn:btih:${url}`; url = `magnet:?xt=urn:btih:${url}`;
} }
const o = { const o = {

View File

@@ -38,12 +38,12 @@ export class RemoveDialog extends EventTarget {
} }
_onConfirm() { _onConfirm() {
const { torrents } = this.options; const { remote, torrents, trash } = this.options;
if (torrents.length > 0) { if (torrents.length > 0) {
if (this.options.trash) { if (trash) {
this.options.remote.removeTorrentsAndData(torrents); remote.removeTorrentsAndData(torrents);
} else { } else {
this.options.remote.removeTorrents(torrents); remote.removeTorrents(torrents);
} }
} }
@@ -64,13 +64,13 @@ export class RemoveDialog extends EventTarget {
static _createMessage(options) { static _createMessage(options) {
let heading = null; let heading = null;
let message = null; let message = null;
const { torrents } = options; const { torrents, trash } = options;
const [torrent] = torrents; const [torrent] = torrents;
if (options.trash && torrents.length === 1) { if (trash && torrents.length === 1) {
heading = `Remove ${torrent.getName()} and delete data?`; heading = `Remove ${torrent.getName()} and delete data?`;
message = message =
'All data downloaded for this torrent will be deleted. Are you sure you want to remove it?'; '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?`; heading = `Remove ${torrents.length} transfers and delete data?`;
message = message =
'All data downloaded for these torrents will be deleted. Are you sure you want to remove them?'; 'All data downloaded for these torrents will be deleted. Are you sure you want to remove them?';

View File

@@ -72,14 +72,14 @@ export class StatisticsDialog extends EventTarget {
static _create() { static _create() {
const elements = createDialogContainer('statistics-dialog'); const elements = createDialogContainer('statistics-dialog');
const { workarea } = elements; const { confirm, dismiss, heading, root, workarea } = elements;
elements.confirm.remove(); confirm.remove();
elements.dismiss.textContent = 'Close'; dismiss.textContent = 'Close';
delete elements.confirm; delete elements.confirm;
const heading_text = 'Statistics'; const heading_text = 'Statistics';
elements.root.setAttribute('aria-label', heading_text); root.setAttribute('aria-label', heading_text);
elements.heading.textContent = heading_text; heading.textContent = heading_text;
const labels = ['Uploaded:', 'Downloaded:', 'Ratio:', 'Running time:']; const labels = ['Uploaded:', 'Downloaded:', 'Ratio:', 'Running time:'];
let section = createInfoSection('Current session', labels); let section = createInfoSection('Current session', labels);

View File

@@ -11,8 +11,21 @@ import { Formatter } from './formatter.js';
import { Torrent } from './torrent.js'; import { Torrent } from './torrent.js';
import { setTextContent } from './utils.js'; import { setTextContent } from './utils.js';
class TorrentRendererHelper { const TorrentRendererHelper = {
static getProgressInfo(controller, t) { 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 status = t.getStatus();
const classList = ['torrent-progress-bar']; const classList = ['torrent-progress-bar'];
let percent = null; let percent = null;
@@ -46,29 +59,14 @@ class TorrentRendererHelper {
classList, classList,
percent, percent,
}; };
} },
static renderProgressbar(controller, t, progressbar) { renderProgressbar: (controller, t, progressbar) => {
const info = TorrentRendererHelper.getProgressInfo(controller, t); const info = TorrentRendererHelper.getProgressInfo(controller, t);
progressbar.className = info.classList.join(' '); progressbar.className = info.classList.join(' ');
progressbar.style['background-size'] = `${info.percent}% 100%, 100% 100%`; 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)}`;
}
}
/// ///

View File

@@ -256,9 +256,9 @@ export class Transmission extends EventTarget {
this.prefs.addEventListener('change', ({ key, value }) => this.prefs.addEventListener('change', ({ key, value }) =>
this._onPrefChanged(key, value) this._onPrefChanged(key, value)
); );
this.prefs for (const [key, value] of this.prefs.entries()) {
.entries() this._onPrefChanged(key, value);
.forEach(([key, value]) => this._onPrefChanged(key, value)); }
} }
loadDaemonPrefs() { loadDaemonPrefs() {
@@ -419,9 +419,9 @@ export class Transmission extends EventTarget {
_dispatchSelectionChanged() { _dispatchSelectionChanged() {
const nonselected = []; const nonselected = [];
const selected = []; const selected = [];
this._rows.forEach((r) => for (const r of this._rows) {
(r.isSelected() ? selected : nonselected).push(r.getTorrent()) (r.isSelected() ? selected : nonselected).push(r.getTorrent());
); }
const event = new Event('torrent-selection-changed'); const event = new Event('torrent-selection-changed');
event.nonselected = nonselected; event.nonselected = nonselected;
@@ -455,7 +455,7 @@ export class Transmission extends EventTarget {
// Process key events // Process key events
_keyDown(event_) { _keyDown(event_) {
const { keyCode } = event_; const { ctrlKey, keyCode, metaKey, shiftKey, target } = event_;
// look for a shortcut // look for a shortcut
const aria_keys = Transmission._createKeyShortcutFromKeyboardEvent(event_); 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 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; const rows = this._rows;
// Some shortcuts can only be used if the following conditions are met: // Some shortcuts can only be used if the following conditions are met:
// 1. when no input fields are focused // 1. when no input fields are focused
// 2. when no other dialogs are visible // 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) // 3. when the meta or ctrl key isn't pressed (i.e. opening dev tools shouldn't trigger the info panel)
if ( if (!is_input_focused && !any_popup_active && !metaKey && !ctrlKey) {
!is_input_focused &&
!any_popup_active &&
!event_.metaKey &&
!event_.ctrlKey
) {
const shift_key = keyCode === 16; // shift key pressed const shift_key = keyCode === 16; // shift key pressed
const up_key = keyCode === 38; // up key pressed const up_key = keyCode === 38; // up key pressed
const dn_key = keyCode === 40; // down key pressed const dn_key = keyCode === 40; // down key pressed
@@ -520,7 +515,7 @@ export class Transmission extends EventTarget {
this._deselectRow(rows[last]); this._deselectRow(rows[last]);
} }
} else { } else {
if (event_.shiftKey) { if (shiftKey) {
this._selectRange(r); this._selectRange(r);
} else { } else {
this._setSelectedRow(r); this._setSelectedRow(r);
@@ -582,12 +577,13 @@ export class Transmission extends EventTarget {
const type = event_.data.Transfer.types const type = event_.data.Transfer.types
.filter((t) => ['text/uri-list', 'text/plain'].contains(t)) .filter((t) => ['text/uri-list', 'text/plain'].contains(t))
.pop(); .pop();
event_.dataTransfer for (const uri of event_.dataTransfer
.getData(type) .getData(type)
.split('\n') .split('\n')
.map((string) => string.trim()) .map((string) => string.trim())
.filter((string) => Transmission._isValidURL(string)) .filter((string) => Transmission._isValidURL(string))) {
.forEach((uri) => this.remote.addTorrentByUrl(uri, paused)); this.remote.addTorrentByUrl(uri, paused);
}
event_.preventDefault(); event_.preventDefault();
return false; return false;
@@ -632,9 +628,9 @@ export class Transmission extends EventTarget {
const keys = table.shift(); const keys = table.shift();
const o = {}; const o = {};
for (const row of table) { for (const row of table) {
keys.forEach((key, index) => { for (const [index, key] of keys.entries()) {
o[key] = row[index]; o[key] = row[index];
}); }
const { id } = o; const { id } = o;
let t = this._torrents[id]; let t = this._torrents[id];
if (t) { if (t) {
@@ -656,10 +652,11 @@ export class Transmission extends EventTarget {
if (needinfo.length > 0) { if (needinfo.length > 0) {
// whee, new torrents! get their initial information. // whee, new torrents! get their initial information.
const more_fields = ['id'].concat( const more_fields = [
Torrent.Fields.Metadata, 'id',
Torrent.Fields.Stats ...Torrent.Fields.Metadata,
); ...Torrent.Fields.Stats,
];
this.updateTorrents(needinfo, more_fields); this.updateTorrents(needinfo, more_fields);
this.refilterSoon(); this.refilterSoon();
} }
@@ -691,12 +688,12 @@ TODO: fix this when notifications get fixed
*/ */
refreshTorrents() { refreshTorrents() {
const fields = ['id'].concat(Torrent.Fields.Stats); const fields = ['id', ...Torrent.Fields.Stats];
this.updateTorrents('recently-active', fields); this.updateTorrents('recently-active', fields);
} }
_initializeTorrents() { _initializeTorrents() {
const fields = ['id'].concat(Torrent.Fields.Metadata, Torrent.Fields.Stats); const fields = ['id', ...Torrent.Fields.Metadata, ...Torrent.Fields.Stats];
this.updateTorrents(null, fields); this.updateTorrents(null, fields);
} }
@@ -910,7 +907,9 @@ TODO: fix this when notifications get fixed
this.prefs.sort_mode, this.prefs.sort_mode,
this.prefs.sort_direction 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) { _refilter(rebuildEverything) {
@@ -1036,13 +1035,11 @@ TODO: fix this when notifications get fixed
this.dirtyTorrents.clear(); this.dirtyTorrents.clear();
// set the odd/even property // set the odd/even property
rows for (const [index, e] of rows.map((row) => row.getElement()).entries()) {
.map((row) => row.getElement()) const even = index % 2 === 0;
.forEach((e, index) => { e.classList.toggle('even', even);
const even = index % 2 === 0; e.classList.toggle('odd', !even);
e.classList.toggle('even', even); }
e.classList.toggle('odd', !even);
});
this._updateStatusbar(); this._updateStatusbar();
if ( if (

View File

@@ -9,26 +9,9 @@
import isEqual from 'lodash.isequal'; import isEqual from 'lodash.isequal';
export class Utils { export const 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, "&infin;" 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;
}
}
/** Given a numerator and denominator, return a ratio string */ /** Given a numerator and denominator, return a ratio string */
static ratio(numerator, denominator) { ratio(numerator, denominator) {
let result = Math.floor((100 * numerator) / denominator) / 100; let result = Math.floor((100 * numerator) / denominator) / 100;
// check for special cases // check for special cases
@@ -42,8 +25,25 @@ export class Utils {
} }
return result; 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, "&infin;" 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) { export function createTabsContainer(id, tabs, callback) {
const root = document.createElement('div'); const root = document.createElement('div');

View File

@@ -1,7 +1,7 @@
const path = require('path'); const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 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 TerserPlugin = require('terser-webpack-plugin');
const webpack = require('webpack'); const webpack = require('webpack');
@@ -45,11 +45,7 @@ const config = {
optimization: { optimization: {
minimizer: [ minimizer: [
new TerserPlugin(), new TerserPlugin(),
new OptimizeCSSAssetsPlugin({ new CssMinimizerPlugin(),
cssProcessorPluginOptions: {
preset: ['default', { discardComments: { removeAll: true } }],
}
})
], ],
}, },
output: { output: {

File diff suppressed because it is too large Load Diff