transmission/web/src/action-manager.js

221 lines
6.1 KiB
JavaScript

/* @license This file Copyright (C) 2020-2022 Mnemosyne LLC.
It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
or any future license endorsed by Mnemosyne LLC.
License text can be found in the licenses/ folder. */
export class ActionManager extends EventTarget {
constructor() {
super();
this.actions = Object.seal({
'deselect-all': {
enabled: false,
shortcut: 'Control+A',
text: 'Deselect all',
},
'move-bottom': { enabled: false, text: 'Move to the back of the queue' },
'move-down': { enabled: false, text: 'Move down in the queue' },
'move-top': { enabled: false, text: 'Move to the front of the queue' },
'move-up': { enabled: false, text: 'Move up in the queue' },
'open-torrent': {
enabled: true,
shortcut: 'Alt+O',
text: 'Open torrent…',
},
'pause-all-torrents': { enabled: false, text: 'Pause all' },
'pause-selected-torrents': {
enabled: false,
shortcut: 'Alt+U',
text: 'Pause',
},
'reannounce-selected-torrents': {
enabled: false,
text: 'Ask tracker for more peers',
},
'remove-selected-torrents': { enabled: false, text: 'Remove from list…' },
'resume-selected-torrents': {
enabled: false,
shortcut: 'Alt+R',
text: 'Resume',
},
'resume-selected-torrents-now': { enabled: false, text: 'Resume now' },
'select-all': { enabled: false, shortcut: 'Alt+A', text: 'Select all' },
'show-about-dialog': { enabled: true, text: 'About' },
'show-inspector': {
enabled: false,
shortcut: 'Alt+I',
text: 'Torrent Inspector',
},
'show-labels-dialog': {
enabled: false,
text: 'Edit Labels…',
},
'show-move-dialog': {
enabled: false,
shortcut: 'Alt+L',
text: 'Set location…',
},
'show-overflow-menu': { enabled: true, text: 'More options…' },
'show-preferences-dialog': {
enabled: true,
shortcut: 'Alt+P',
text: 'Edit preferences',
},
'show-rename-dialog': {
enabled: false,
shortcut: 'Alt+N',
text: 'Rename…',
},
'show-shortcuts-dialog': { enabled: true, text: 'Keyboard shortcuts' },
'show-statistics-dialog': {
enabled: true,
shortcut: 'Alt+S',
text: 'Statistics',
},
'start-all-torrents': { enabled: false, text: 'Start all' },
'toggle-compact-rows': { enabled: true, text: 'Compact rows' },
'trash-selected-torrents': {
enabled: false,
text: 'Trash data and remove from list…',
},
'verify-selected-torrents': {
enabled: false,
shortcut: 'Alt+V',
text: 'Verify local data',
},
});
}
click(name) {
if (this.isEnabled(name)) {
const event_ = new Event('click');
event_.action = name;
this.dispatchEvent(event_);
}
}
getActionForShortcut(shortcut) {
for (const [name, properties] of Object.entries(this.actions)) {
if (shortcut === properties.shortcut) {
return name;
}
}
return null;
}
// return a map of shortcuts to action names
allShortcuts() {
return new Map(
Object.entries(this.actions)
.filter(([, properties]) => properties.shortcut)
.map(([name, properties]) => [properties.shortcut, name])
);
}
isEnabled(name) {
return this._getAction(name).enabled;
}
text(name) {
return this._getAction(name).text;
}
keyshortcuts(name) {
return this._getAction(name).shortcut;
}
update(event_) {
const counts = ActionManager._recount(event_.selected, event_.nonselected);
this._updateStates(counts);
}
_getAction(name) {
const action = this.actions[name];
if (!action) {
throw new Error(`no such action: ${name}`);
}
return action;
}
static _recount(selected, nonselected) {
const total = selected.length + nonselected.length;
const selected_paused = selected.filter((tor) => tor.isStopped()).length;
const selected_active = selected.length - selected_paused;
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;
const selected_queued = selected.filter((tor) => tor.isQueued()).length;
return {
active,
nonselected_active,
nonselected_paused,
paused,
selected: selected.length,
selected_active,
selected_paused,
selected_queued,
total,
};
}
_updateStates(counts) {
const set_enabled = (enabled, actions) => {
for (const action of actions) {
this._updateActionState(action, enabled);
}
};
set_enabled(counts.selected_paused > 0, ['resume-selected-torrents']);
set_enabled(counts.paused > 0, ['start-all-torrents']);
set_enabled(counts.active > 0, ['pause-all-torrents']);
set_enabled(counts.selected_paused > 0 || counts.selected_queued > 0, [
'resume-selected-torrents-now',
]);
set_enabled(counts.selected_active > 0, [
'pause-selected-torrents',
'reannounce-selected-torrents',
]);
set_enabled(counts.selected > 0, [
'deselect-all',
'move-bottom',
'move-down',
'move-top',
'move-up',
'remove-selected-torrents',
'show-inspector',
'show-labels-dialog',
'show-move-dialog',
'trash-selected-torrents',
'verify-selected-torrents',
]);
set_enabled(counts.selected === 1, ['show-rename-dialog']);
set_enabled(counts.selected < counts.total, ['select-all']);
}
_updateActionState(name, enabled) {
const action = this.actions[name];
if (!action) {
throw new Error(`no such action: ${name}`);
}
if (action.enabled !== enabled) {
action.enabled = enabled;
const event = new Event('change');
event.action = name;
event.enabled = enabled;
this.dispatchEvent(event);
}
}
}