diff --git a/src/webui/www/private/scripts/dynamicTable.js b/src/webui/www/private/scripts/dynamicTable.js index c1cbce6cedd8..1683e4eccaa9 100644 --- a/src/webui/www/private/scripts/dynamicTable.js +++ b/src/webui/www/private/scripts/dynamicTable.js @@ -71,11 +71,11 @@ window.qBittorrent.DynamicTable ??= (() => { class DynamicTable { #DynamicTableHeaderContextMenuClass = null; - setup(dynamicTableDivId, dynamicTableFixedHeaderDivId, contextMenu, useVirtualList = false) { + setup(dynamicTableDivId, dynamicTableFixedHeaderDivId, contextMenu) { this.dynamicTableDivId = dynamicTableDivId; this.dynamicTableFixedHeaderDivId = dynamicTableFixedHeaderDivId; this.dynamicTableDiv = document.getElementById(dynamicTableDivId); - this.useVirtualList = useVirtualList && (clientData.get("use_virtual_list") === true); + this.useVirtualList = clientData.get("use_virtual_list") === true; this.fixedTableHeader = document.querySelector(`#${dynamicTableFixedHeaderDivId} thead tr`); this.hiddenTableHeader = this.dynamicTableDiv.querySelector("thead tr"); this.table = this.dynamicTableDiv.querySelector("table"); @@ -3072,19 +3072,52 @@ window.qBittorrent.DynamicTable ??= (() => { class RssFeedTable extends DynamicTable { initColumns() { - this.newColumn("state_icon", "", "", 30, true); this.newColumn("name", "", "QBT_TR(RSS feeds)QBT_TR[CONTEXT=FeedListWidget]", -1, true); - this.columns["state_icon"].dataProperties[0] = ""; - // map name row to "[name] ([unread])" - this.columns["name"].dataProperties.push("unread"); - this.columns["name"].updateTd = function(td, row) { - const name = this.getRowValue(row, 0); - const unreadCount = this.getRowValue(row, 1); - const value = `${name} (${unreadCount})`; - td.textContent = value; - td.title = value; + this.columns["name"].dataProperties.push("unread", "status"); + this.columns["name"].updateTd = (td, row) => { + td.style.display = "block"; + + const data = row.full_data; + const name = data.name; + const indentation = data.indentation; + const value = `${name} (${data.unread})`; + + let statusImg = "images/application-rss.svg"; + switch (data.status) { + case "hasError": + statusImg = "images/task-reject.svg"; + break; + case "isLoading": + statusImg = "images/spinner.svg"; + break; + case "unread": + statusImg = "images/mail-inbox.svg"; + break; + case "isFolder": + statusImg = "images/folder-documents.svg"; + break; + } + let img = td.children[0]; + if (img === undefined) { + img = document.createElement("img"); + img.className = "stateIcon"; + img.style.width = "20px"; + img.style.paddingRight = "5px"; + img.style.marginBottom = "-3px"; + td.append(img); + } + img.title = value; + img.src = statusImg; + img.style.marginLeft = `${indentation * 20}px`; + + let span = td.children[1]; + if (span === undefined) { + span = document.createElement("span"); + td.append(span); + } + span.textContent = value; }; } setupHeaderMenu() {} @@ -3095,13 +3128,8 @@ window.qBittorrent.DynamicTable ??= (() => { selectRow(rowId) { super.selectRow(rowId); - let path = ""; - for (const row of this.getRowValues()) { - if (row.rowId === rowId) { - path = row.full_data.dataPath; - break; - } - } + const row = this.getRow(rowId); + const path = row.full_data.dataPath; window.qBittorrent.Rss.showRssFeed(path); } setupCommonEvents() { @@ -3114,67 +3142,27 @@ window.qBittorrent.DynamicTable ??= (() => { window.qBittorrent.Rss.moveItem(this.getRow(tr.rowId).full_data.dataPath); }); } - updateRow(tr, fullUpdate) { - const row = this.rows.get(tr.rowId); - const tds = this.getRowCells(tr); - - tds[0].style.overflow = "visible"; - const indentation = row.full_data.indentation; - tds[0].style.paddingLeft = `${indentation * 32 + 4}px`; - tds[1].style.paddingLeft = `${indentation * 32 + 4}px`; - - return super.updateRow(tr, fullUpdate); - } - updateIcons() { - // state_icon - for (const row of this.getRowValues()) { - let img_path; - switch (row.full_data.status) { - case "default": - img_path = "images/application-rss.svg"; - break; - case "hasError": - img_path = "images/task-reject.svg"; - break; - case "isLoading": - img_path = "images/spinner.svg"; - break; - case "unread": - img_path = "images/mail-inbox.svg"; - break; - case "isFolder": - img_path = "images/folder-documents.svg"; - break; - } - let td; - for (let i = 0; i < this.tableBody.rows.length; ++i) { - if (this.tableBody.rows[i].rowId === row.rowId) { - td = this.tableBody.rows[i].children[0]; - break; - } - } - if (td.getChildren("img").length > 0) { - const img = td.getChildren("img")[0]; - if (!img.src.includes(img_path)) { - img.src = img_path; - img.title = status; - } - } - else { - const img = document.createElement("img"); - img.src = img_path; - img.className = "stateIcon"; - img.width = "22"; - img.height = "22"; - td.append(img); - } - } + updateRowElement(tr, rowId, top) { + super.updateRowElement(tr, rowId, top); + if (this.useVirtualList) + tr.style.width = "100%"; } } class RssArticleTable extends DynamicTable { initColumns() { this.newColumn("name", "", "QBT_TR(Torrents: (double-click to download))QBT_TR[CONTEXT=RSSWidget]", -1, true); + + this.columns["name"].updateTd = (td, row) => { + td.style.display = "block"; + + const data = row.full_data; + td.textContent = data.name; + td.title = data.name; + + const tr = td.parentElement; + tr.classList.toggle("unreadArticle", !data.isRead); + }; } setupHeaderMenu() {} setupHeaderEvents() {} @@ -3184,17 +3172,13 @@ window.qBittorrent.DynamicTable ??= (() => { selectRow(rowId) { super.selectRow(rowId); - let articleId = ""; - let feedUid = ""; - for (const row of this.getRowValues()) { - if (row.rowId === rowId) { - articleId = row.full_data.dataId; - feedUid = row.full_data.feedUid; - this.tableBody.rows[row.rowId].classList.remove("unreadArticle"); - break; - } - } - window.qBittorrent.Rss.showDetails(feedUid, articleId); + const tr = this.getTrByRowId(rowId); + tr.classList.remove("unreadArticle"); + + const row = this.getRow(rowId); + const data = row.full_data; + data.isRead = true; + window.qBittorrent.Rss.showDetails(data.feedUid, data.dataId); } setupCommonEvents() { @@ -3208,11 +3192,10 @@ window.qBittorrent.DynamicTable ??= (() => { qBittorrent.Client.createAddTorrentWindow(name, torrentURL); }); } - updateRow(tr, fullUpdate) { - const row = this.rows.get(tr.rowId); - tr.classList.toggle("unreadArticle", !row.full_data.isRead); - - return super.updateRow(tr, fullUpdate); + updateRowElement(tr, rowId, top) { + super.updateRowElement(tr, rowId, top); + if (this.useVirtualList) + tr.style.width = "100%"; } } @@ -3222,26 +3205,25 @@ window.qBittorrent.DynamicTable ??= (() => { this.newColumn("name", "", "", -1, true); this.columns["checked"].updateTd = (td, row) => { - if (document.getElementById(`cbRssDlRule${row.rowId}`) === null) { - const checkbox = document.createElement("input"); + let checkbox = row.firstElementChild; + if (checkbox === undefined) { + checkbox = document.createElement("input"); checkbox.type = "checkbox"; - checkbox.id = `cbRssDlRule${row.rowId}`; - checkbox.checked = row.full_data.checked; - - checkbox.addEventListener("click", function(e) { + checkbox.dataset.id = row.rowId; + checkbox.addEventListener("click", (e) => { + const targetId = e.target.dataset.id; + const row = window.qBittorrent.RssDownloader.rssDownloaderRulesTable.getRow(targetId); window.qBittorrent.RssDownloader.rssDownloaderRulesTable.updateRowData({ rowId: row.rowId, - checked: this.checked + checked: checkbox.checked }); - window.qBittorrent.RssDownloader.modifyRuleState(row.full_data.name, "enabled", this.checked); + window.qBittorrent.RssDownloader.modifyRuleState(row.full_data.name, "enabled", checkbox.checked); e.stopPropagation(); }); td.append(checkbox); } - else { - document.getElementById(`cbRssDlRule${row.rowId}`).checked = row.full_data.checked; - } + checkbox.checked = row.full_data.checked; }; this.columns["checked"].staticWidth = 50; } @@ -3264,15 +3246,15 @@ window.qBittorrent.DynamicTable ??= (() => { selectRow(rowId) { super.selectRow(rowId); - let name = ""; - for (const row of this.getRowValues()) { - if (row.rowId === rowId) { - name = row.full_data.name; - break; - } - } + const row = this.getRow(rowId); + const name = row.full_data.name; window.qBittorrent.RssDownloader.showRule(name); } + updateRowElement(tr, rowId, top) { + super.updateRowElement(tr, rowId, top); + if (this.useVirtualList) + tr.style.width = "100%"; + } } class RssDownloaderFeedSelectionTable extends DynamicTable { @@ -3281,25 +3263,24 @@ window.qBittorrent.DynamicTable ??= (() => { this.newColumn("name", "", "", -1, true); this.columns["checked"].updateTd = (td, row) => { - if (document.getElementById(`cbRssDlFeed${row.rowId}`) === null) { - const checkbox = document.createElement("input"); + let checkbox = row.firstElementChild; + if (checkbox === undefined) { + checkbox = document.createElement("input"); checkbox.type = "checkbox"; - checkbox.id = `cbRssDlFeed${row.rowId}`; - checkbox.checked = row.full_data.checked; - - checkbox.addEventListener("click", function(e) { + checkbox.dataset.id = row.rowId; + checkbox.addEventListener("click", (e) => { + const targetId = e.target.dataset.id; + const row = window.qBittorrent.RssDownloader.rssDownloaderRulesTable.getRow(targetId); window.qBittorrent.RssDownloader.rssDownloaderFeedSelectionTable.updateRowData({ rowId: row.rowId, - checked: this.checked + checked: checkbox.checked }); e.stopPropagation(); }); td.append(checkbox); } - else { - document.getElementById(`cbRssDlFeed${row.rowId}`).checked = row.full_data.checked; - } + checkbox.checked = row.full_data.checked; }; this.columns["checked"].staticWidth = 50; } @@ -3309,11 +3290,27 @@ window.qBittorrent.DynamicTable ??= (() => { return [...this.getRowValues()]; } selectRow() {} + updateRowElement(tr, rowId, top) { + super.updateRowElement(tr, rowId, top); + if (this.useVirtualList) + tr.style.width = "100%"; + } } class RssDownloaderArticlesTable extends DynamicTable { initColumns() { this.newColumn("name", "", "", -1, true); + + this.columns["name"].updateTd = (td, row) => { + td.style.display = "block"; + + const data = row.full_data; + td.textContent = data.name; + td.title = data.name; + const tr = td.parentElement; + tr.classList.toggle("articleTableFeed", data.isFeed); + tr.classList.toggle("articleTableArticle", !data.isFeed); + }; } setupHeaderMenu() {} setupHeaderEvents() {} @@ -3321,12 +3318,10 @@ window.qBittorrent.DynamicTable ??= (() => { return [...this.getRowValues()]; } selectRow() {} - updateRow(tr, fullUpdate) { - const row = this.rows.get(tr.rowId); - tr.classList.toggle("articleTableFeed", row.full_data.isFeed); - tr.classList.toggle("articleTableArticle", !row.full_data.isFeed); - - return super.updateRow(tr, fullUpdate); + updateRowElement(tr, rowId, top) { + super.updateRowElement(tr, rowId, top); + if (this.useVirtualList) + tr.style.width = "100%"; } } diff --git a/src/webui/www/private/scripts/prop-peers.js b/src/webui/www/private/scripts/prop-peers.js index 9c4061897048..428e2d7965bf 100644 --- a/src/webui/www/private/scripts/prop-peers.js +++ b/src/webui/www/private/scripts/prop-peers.js @@ -194,7 +194,7 @@ window.qBittorrent.PropPeers ??= (() => { await clipboardCopy(text); }); - torrentPeersTable.setup("torrentPeersTableDiv", "torrentPeersTableFixedHeaderDiv", torrentPeersContextMenu, true); + torrentPeersTable.setup("torrentPeersTableDiv", "torrentPeersTableFixedHeaderDiv", torrentPeersContextMenu); return exports(); })(); diff --git a/src/webui/www/private/scripts/prop-trackers.js b/src/webui/www/private/scripts/prop-trackers.js index e25f2e3b7d9e..f8d8d8349c73 100644 --- a/src/webui/www/private/scripts/prop-trackers.js +++ b/src/webui/www/private/scripts/prop-trackers.js @@ -338,7 +338,7 @@ window.qBittorrent.PropTrackers ??= (() => { await clipboardCopy(text); }); - torrentTrackersTable.setup("torrentTrackersTableDiv", "torrentTrackersTableFixedHeaderDiv", torrentTrackersContextMenu, true); + torrentTrackersTable.setup("torrentTrackersTableDiv", "torrentTrackersTableFixedHeaderDiv", torrentTrackersContextMenu); return exports(); })(); diff --git a/src/webui/www/private/scripts/prop-webseeds.js b/src/webui/www/private/scripts/prop-webseeds.js index 495fed0f4b13..4175a5210ced 100644 --- a/src/webui/www/private/scripts/prop-webseeds.js +++ b/src/webui/www/private/scripts/prop-webseeds.js @@ -224,7 +224,7 @@ window.qBittorrent.PropWebseeds ??= (() => { await clipboardCopy(text); }); - torrentWebseedsTable.setup("torrentWebseedsTableDiv", "torrentWebseedsTableFixedHeaderDiv", torrentWebseedsContextMenu, true); + torrentWebseedsTable.setup("torrentWebseedsTableDiv", "torrentWebseedsTableFixedHeaderDiv", torrentWebseedsContextMenu); return exports(); })(); diff --git a/src/webui/www/private/scripts/torrent-content.js b/src/webui/www/private/scripts/torrent-content.js index 7aa5ea577956..06de39594506 100644 --- a/src/webui/www/private/scripts/torrent-content.js +++ b/src/webui/www/private/scripts/torrent-content.js @@ -512,7 +512,7 @@ window.qBittorrent.TorrentContent ??= (() => { }, }); - torrentFilesTable.setup(tableId, "torrentFilesTableFixedHeaderDiv", torrentFilesContextMenu, true); + torrentFilesTable.setup(tableId, "torrentFilesTableFixedHeaderDiv", torrentFilesContextMenu); // inject checkbox into table header const tableHeaders = document.querySelectorAll("#torrentFilesTableFixedHeaderDiv .dynamicTableHeader th"); if (tableHeaders.length > 0) { diff --git a/src/webui/www/private/views/log.html b/src/webui/www/private/views/log.html index e3530469a705..1c169f1f2154 100644 --- a/src/webui/www/private/views/log.html +++ b/src/webui/www/private/views/log.html @@ -222,8 +222,8 @@ } }); - tableInfo["main"].instance.setup("logMessageTableDiv", "logMessageTableFixedHeaderDiv", logTableContextMenu, true); - tableInfo["peer"].instance.setup("logPeerTableDiv", "logPeerTableFixedHeaderDiv", logTableContextMenu, true); + tableInfo["main"].instance.setup("logMessageTableDiv", "logMessageTableFixedHeaderDiv", logTableContextMenu); + tableInfo["peer"].instance.setup("logPeerTableDiv", "logPeerTableFixedHeaderDiv", logTableContextMenu); MUI.Panels.instances.LogPanel.contentEl.style.height = "100%"; diff --git a/src/webui/www/private/views/rss.html b/src/webui/www/private/views/rss.html index e6ea67be7b27..dc8187b4af21 100644 --- a/src/webui/www/private/views/rss.html +++ b/src/webui/www/private/views/rss.html @@ -637,8 +637,6 @@ }); } } - if (statusDiffers) - rssFeedTable.updateIcons(); // get currently opened feed let openedFeedPath = undefined; @@ -717,6 +715,8 @@ } } }); + if (statusDiffers) + rssFeedTable.updateTable(true); if (needsUpdate) { rssFeedTable.updateTable(true); rssArticleTable.updateTable(true); @@ -788,7 +788,6 @@ ++rowCount; } rssFeedTable.updateTable(false); - rssFeedTable.updateIcons(); } }); }; @@ -799,7 +798,7 @@ if (row.full_data.dataUid === feedUid) row.full_data.status = "isLoading"; } - rssFeedTable.updateIcons(); + rssFeedTable.updateTable(true); fetch("api/v2/rss/refreshItem", { method: "POST", diff --git a/src/webui/www/private/views/transferlist.html b/src/webui/www/private/views/transferlist.html index c6feebbb53ad..0692a06db83a 100644 --- a/src/webui/www/private/views/transferlist.html +++ b/src/webui/www/private/views/transferlist.html @@ -111,7 +111,7 @@ }); const setup = () => { - torrentsTable.setup("torrentsTableDiv", "torrentsTableFixedHeaderDiv", contextMenu, true); + torrentsTable.setup("torrentsTableDiv", "torrentsTableFixedHeaderDiv", contextMenu); }; return exports();