diff --git a/web/assets/css/transmission-app.scss b/web/assets/css/transmission-app.scss index 28483a52d..e0da3c5c6 100644 --- a/web/assets/css/transmission-app.scss +++ b/web/assets/css/transmission-app.scss @@ -962,6 +962,16 @@ $video-image: '../img/film.svg'; } } +.tabs-container-close { + font-size: 150%; + cursor: pointer; + background: var(--color-bg-primary); + border: 0; + color: var(--color-fg-primary); + position: absolute; + padding: 10px 16px; +} + .tabs-buttons { align-self: center; background-color: var(--color-bg-tabs); diff --git a/web/src/inspector.js b/web/src/inspector.js index cca2021bb..d11b3ca9a 100644 --- a/web/src/inspector.js +++ b/web/src/inspector.js @@ -6,12 +6,7 @@ import { FileRow } from './file-row.js'; import { Formatter } from './formatter.js'; import { Torrent } from './torrent.js'; -import { - OutsideClickListener, - Utils, - createTextualTabsContainer, - setTextContent, -} from './utils.js'; +import { Utils, createTextualTabsContainer, setTextContent } from './utils.js'; const peer_column_classes = [ 'encryption', @@ -39,8 +34,7 @@ export class Inspector extends EventTarget { this.file_torrent = null; this.file_torrent_n = null; this.file_rows = null; - this.outside = new OutsideClickListener(this.elements.root); - this.outside.addEventListener('click', () => this.close()); + this.elements.dismiss.addEventListener('click', () => this.close()); Object.seal(this); controller.addEventListener( @@ -54,7 +48,6 @@ export class Inspector extends EventTarget { close() { if (!this.closed) { - this.outside.stop(); clearInterval(this.interval); this._setTorrents([]); this.elements.root.remove(); diff --git a/web/src/prefs-dialog.js b/web/src/prefs-dialog.js index 02e2d0d5d..3798895d4 100644 --- a/web/src/prefs-dialog.js +++ b/web/src/prefs-dialog.js @@ -50,6 +50,10 @@ export class PrefsDialog extends EventTarget { } _onPortChecked(response) { + if (this.closed) { + return; + } + const element = this.elements.network.port_status_label; const is_open = response.arguments['port-is-open']; element.dataset.open = is_open; @@ -783,6 +787,7 @@ export class PrefsDialog extends EventTarget { PrefsDialog._toggleProtocolHandler(event_.currentTarget); }, ); + this.elements.dismiss.addEventListener('click', () => this.close()); this.outside = new OutsideClickListener(this.elements.root); this.outside.addEventListener('click', () => this.close()); diff --git a/web/src/transmission.js b/web/src/transmission.js index debc3a506..4b7ccbe06 100644 --- a/web/src/transmission.js +++ b/web/src/transmission.js @@ -45,14 +45,11 @@ export class Transmission extends EventTarget { this._rows = []; this.dirtyTorrents = new Set(); + this.changeStatus = false; this.refilterSoon = debounce(() => this._refilter(false)); this.refilterAllSoon = debounce(() => this._refilter(true)); this.boundPopupCloseListener = this.popupCloseListener.bind(this); - this.dispatchSelectionChangedSoon = debounce( - () => this._dispatchSelectionChanged(), - 200, - ); // listen to actions // TODO: consider adding a mutator listener here to see dynamic additions @@ -125,7 +122,9 @@ export class Transmission extends EventTarget { this.setCurrentPopup(new AboutDialog(this.version_info)); break; case 'show-inspector': - this.setCurrentPopup(new Inspector(this)); + if (!this.popup || this.popup.name !== 'inspector') { + this.setCurrentPopup(new Inspector(this)); + } break; case 'show-move-dialog': this.setCurrentPopup(new MoveDialog(this, this.remote)); @@ -187,22 +186,26 @@ export class Transmission extends EventTarget { this.refilterAllSoon(); }); - //if (!isMobileDevice) { document.addEventListener('keydown', this._keyDown.bind(this)); document.addEventListener('keyup', this._keyUp.bind(this)); e = document.querySelector('#torrent-container'); - e.addEventListener('click', () => { + e.addEventListener('click', (e_) => { if (this.popup && this.popup.name !== 'inspector') { this.setCurrentPopup(null); - } else { + } + if (e_.target === e_.currentTarget) { this._deselectAll(); } }); + e.addEventListener('dblclick', () => { + if (!this.popup || this.popup.name !== 'inspector') { + this.action_manager.click('show-inspector'); + } + }); e.addEventListener('dragenter', Transmission._dragenter); e.addEventListener('dragover', Transmission._dragenter); e.addEventListener('drop', this._drop.bind(this)); this._setupSearchBox(); - //} this.elements = { torrent_list: document.querySelector('#torrent-list'), @@ -376,31 +379,31 @@ export class Transmission extends EventTarget { for (const e of this.elements.torrent_list.children) { e.classList.toggle('selected', e === e_sel); } - this.dispatchSelectionChangedSoon(); + this._dispatchSelectionChanged(); } _selectRow(row) { row.getElement().classList.add('selected'); - this.dispatchSelectionChangedSoon(); + this._dispatchSelectionChanged(); } _deselectRow(row) { row.getElement().classList.remove('selected'); - this.dispatchSelectionChangedSoon(); + this._dispatchSelectionChanged(); } _selectAll() { for (const e of this.elements.torrent_list.children) { e.classList.add('selected'); } - this.dispatchSelectionChangedSoon(); + this._dispatchSelectionChanged(); } _deselectAll() { for (const e of this.elements.torrent_list.children) { e.classList.remove('selected'); } - this.dispatchSelectionChangedSoon(); + this._dispatchSelectionChanged(); delete this._last_torrent_clicked; } @@ -426,7 +429,7 @@ export class Transmission extends EventTarget { } } - this.dispatchSelectionChangedSoon(); + this._dispatchSelectionChanged(); } _dispatchSelectionChanged() { @@ -636,6 +639,11 @@ export class Transmission extends EventTarget { } _onTorrentChanged(event_) { + if (this.changeStatus) { + this._dispatchSelectionChanged(); + this.changeStatus = false; + } + // update our dirty fields const tor = event_.currentTarget; this.dirtyTorrents.add(tor.getId()); @@ -726,7 +734,6 @@ TODO: fix this when notifications get fixed if (this.popup && this.popup.name !== 'inspector') { this.setCurrentPopup(null); - return; } // handle the per-row pause/resume button @@ -797,6 +804,7 @@ TODO: fix this when notifications get fixed } _startTorrents(torrents, force) { + this.changeStatus = true; this.remote.startTorrents( Transmission._getTorrentIds(torrents), force, @@ -821,9 +829,14 @@ TODO: fix this when notifications get fixed } _stopTorrents(torrents) { + this.changeStatus = true; this.remote.stopTorrents( Transmission._getTorrentIds(torrents), - this.refreshTorrents, + () => { + setTimeout(() => { + this.refreshTorrents(); + }, 500); + }, this, ); } @@ -1020,9 +1033,6 @@ TODO: fix this when notifications get fixed e.row = row; dirty_rows.push(row); e.addEventListener('click', this._onRowClicked.bind(this)); - e.addEventListener('dblclick', () => - this.action_manager.click('show-inspector'), - ); } } @@ -1086,7 +1096,7 @@ TODO: fix this when notifications get fixed old_sel_count !== countSelectedRows() || old_row_count !== countRows() ) { - this.dispatchSelectionChangedSoon(); + this._dispatchSelectionChanged(); } } diff --git a/web/src/utils.js b/web/src/utils.js index 8b674f9ec..5256ac4ed 100644 --- a/web/src/utils.js +++ b/web/src/utils.js @@ -62,6 +62,11 @@ export function createTextualTabsContainer(id, tabs, callback) { buttons.classList.add('tabs-buttons'); root.append(buttons); + const dismiss = document.createElement('button'); + dismiss.classList.add('tabs-container-close'); + dismiss.innerHTML = '×'; + root.append(dismiss); + const pages = document.createElement('div'); pages.classList.add('tabs-pages'); root.append(pages); @@ -89,6 +94,7 @@ export function createTextualTabsContainer(id, tabs, callback) { return { buttons: button_array, + dismiss, root, }; }