feat: reset all queue filters together
Add one contextual reset action that clears the upload sidebar, filename search, host filter, and status filter in a single render while restoring selection and scroll state consistently.
This commit is contained in:
@@ -3917,6 +3917,7 @@ function setUploadSidebarFilter(value) {
|
|||||||
_lastVisibleRange = { start: -1, end: -1 };
|
_lastVisibleRange = { start: -1, end: -1 };
|
||||||
const container = document.getElementById('queueContainer');
|
const container = document.getElementById('queueContainer');
|
||||||
if (container) container.scrollTop = 0;
|
if (container) container.scrollTop = 0;
|
||||||
|
syncQueueFilterResetAction();
|
||||||
renderQueueTable();
|
renderQueueTable();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3927,6 +3928,40 @@ function applyQueueDetailFilters() {
|
|||||||
_queueFilterCache = { filter: '', source: null, result: [] };
|
_queueFilterCache = { filter: '', source: null, result: [] };
|
||||||
_normalizeQueueSelectionToVisible();
|
_normalizeQueueSelectionToVisible();
|
||||||
_lastVisibleRange = { start: -1, end: -1 };
|
_lastVisibleRange = { start: -1, end: -1 };
|
||||||
|
syncQueueFilterResetAction();
|
||||||
|
renderQueueTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
function _hasActiveQueueFilters() {
|
||||||
|
return uploadSidebarFilter !== 'all' || Boolean(queueSearchQuery || queueHosterFilter || queueStatusFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncQueueFilterResetAction() {
|
||||||
|
const button = document.getElementById('queueFilterResetBtn');
|
||||||
|
if (!button) return;
|
||||||
|
const active = _hasActiveQueueFilters();
|
||||||
|
button.hidden = !active;
|
||||||
|
button.disabled = !active;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetQueueFilters() {
|
||||||
|
uploadSidebarFilter = 'all';
|
||||||
|
queueSearchQuery = '';
|
||||||
|
queueHosterFilter = '';
|
||||||
|
queueStatusFilter = '';
|
||||||
|
const search = document.getElementById('queueSearchInput');
|
||||||
|
const hoster = document.getElementById('queueHosterFilter');
|
||||||
|
const status = document.getElementById('queueStatusFilter');
|
||||||
|
if (search) search.value = '';
|
||||||
|
if (hoster) hoster.value = '';
|
||||||
|
if (status) status.value = '';
|
||||||
|
_syncSidebarFilterButtons('[data-upload-sidebar-target]', 'uploadSidebarTarget', 'all');
|
||||||
|
_queueFilterCache = { filter: '', source: null, result: [] };
|
||||||
|
_normalizeQueueSelectionToVisible();
|
||||||
|
_lastVisibleRange = { start: -1, end: -1 };
|
||||||
|
const container = document.getElementById('queueContainer');
|
||||||
|
if (container) container.scrollTop = 0;
|
||||||
|
syncQueueFilterResetAction();
|
||||||
renderQueueTable();
|
renderQueueTable();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3938,6 +3973,7 @@ function syncQueueHosterFilterOptions() {
|
|||||||
select.innerHTML = `<option value="">Alle Hoster</option>${names.map(name => `<option value="${escapeAttr(name)}">${escapeHtml(getHosterLabel(name))}</option>`).join('')}`;
|
select.innerHTML = `<option value="">Alle Hoster</option>${names.map(name => `<option value="${escapeAttr(name)}">${escapeHtml(getHosterLabel(name))}</option>`).join('')}`;
|
||||||
select.value = names.includes(current) ? current : '';
|
select.value = names.includes(current) ? current : '';
|
||||||
queueHosterFilter = select.value;
|
queueHosterFilter = select.value;
|
||||||
|
syncQueueFilterResetAction();
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateUploadSidebarSummary(stats = _computeQueueStats()) {
|
function updateUploadSidebarSummary(stats = _computeQueueStats()) {
|
||||||
@@ -7237,6 +7273,8 @@ function setupListeners() {
|
|||||||
document.getElementById('queueSearchInput').addEventListener('input', applyQueueDetailFilters);
|
document.getElementById('queueSearchInput').addEventListener('input', applyQueueDetailFilters);
|
||||||
document.getElementById('queueHosterFilter').addEventListener('change', applyQueueDetailFilters);
|
document.getElementById('queueHosterFilter').addEventListener('change', applyQueueDetailFilters);
|
||||||
document.getElementById('queueStatusFilter').addEventListener('change', applyQueueDetailFilters);
|
document.getElementById('queueStatusFilter').addEventListener('change', applyQueueDetailFilters);
|
||||||
|
document.getElementById('queueFilterResetBtn').addEventListener('click', resetQueueFilters);
|
||||||
|
syncQueueFilterResetAction();
|
||||||
|
|
||||||
const historyRetentionPicker = document.getElementById('historyRetentionPicker');
|
const historyRetentionPicker = document.getElementById('historyRetentionPicker');
|
||||||
const historyRetentionTrigger = document.getElementById('historyRetentionTrigger');
|
const historyRetentionTrigger = document.getElementById('historyRetentionTrigger');
|
||||||
|
|||||||
@@ -107,6 +107,7 @@
|
|||||||
['Eine neue Version ist verfügbar!', 'A new version is available!'],
|
['Eine neue Version ist verfügbar!', 'A new version is available!'],
|
||||||
['Bereit zum Download', 'Ready to download'],
|
['Bereit zum Download', 'Ready to download'],
|
||||||
['Abbrechen', 'Cancel'],
|
['Abbrechen', 'Cancel'],
|
||||||
|
['Filter zurücksetzen', 'Reset filters'],
|
||||||
['Jetzt installieren', 'Install now'],
|
['Jetzt installieren', 'Install now'],
|
||||||
['Jetzt updaten', 'Install now'],
|
['Jetzt updaten', 'Install now'],
|
||||||
['Alle Dateien', 'All files'],
|
['Alle Dateien', 'All files'],
|
||||||
|
|||||||
@@ -297,6 +297,7 @@
|
|||||||
<select class="hs-input" id="queueStatusFilter" aria-label="Nach Status filtern" style="width:auto;min-width:150px">
|
<select class="hs-input" id="queueStatusFilter" aria-label="Nach Status filtern" style="width:auto;min-width:150px">
|
||||||
<option value="">Alle Status</option><option value="error">Nur Fehler</option><option value="active">Aktiv</option><option value="waiting">Wartend</option><option value="done">Erfolgreich</option>
|
<option value="">Alle Status</option><option value="error">Nur Fehler</option><option value="active">Aktiv</option><option value="waiting">Wartend</option><option value="done">Erfolgreich</option>
|
||||||
</select>
|
</select>
|
||||||
|
<button class="btn btn-xs btn-secondary queue-filter-reset" id="queueFilterResetBtn" hidden>Filter zurücksetzen</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="queue-container" id="queueContainer">
|
<div class="queue-container" id="queueContainer">
|
||||||
|
|||||||
@@ -401,6 +401,13 @@ body {
|
|||||||
background: var(--border);
|
background: var(--border);
|
||||||
margin: 0 4px;
|
margin: 0 4px;
|
||||||
}
|
}
|
||||||
|
.queue-filter-reset {
|
||||||
|
margin-left: auto;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.queue-filter-reset[hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
.queue-container {
|
.queue-container {
|
||||||
flex: 1 1 0;
|
flex: 1 1 0;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ test('rare account, backup, update, and confirmation states translate in both di
|
|||||||
['Ein Update wird bereits vorbereitet', 'An update is already being prepared'],
|
['Ein Update wird bereits vorbereitet', 'An update is already being prepared'],
|
||||||
['Die Anwendung ist noch nicht bereit, das Update sicher zu installieren', 'The application is not yet ready to install the update safely'],
|
['Die Anwendung ist noch nicht bereit, das Update sicher zu installieren', 'The application is not yet ready to install the update safely'],
|
||||||
['Update abgebrochen', 'Update canceled'],
|
['Update abgebrochen', 'Update canceled'],
|
||||||
|
['Filter zurücksetzen', 'Reset filters'],
|
||||||
['Download abbrechen', 'Cancel download'],
|
['Download abbrechen', 'Cancel download'],
|
||||||
['Abbrechen…', 'Cancelling…'],
|
['Abbrechen…', 'Cancelling…'],
|
||||||
['Download abgebrochen', 'Download canceled'],
|
['Download abgebrochen', 'Download canceled'],
|
||||||
|
|||||||
@@ -177,6 +177,7 @@ test('header occupies its final geometry before asynchronous initialization', ()
|
|||||||
assert.match(css, /\.update-dialog\s*\{[^}]*width:\s*min\(576px,\s*100%\);/su);
|
assert.match(css, /\.update-dialog\s*\{[^}]*width:\s*min\(576px,\s*100%\);/su);
|
||||||
assert.match(css, /\.update-release-notes\s*\{[^}]*height:\s*min\(264px,\s*48vh\);/su);
|
assert.match(css, /\.update-release-notes\s*\{[^}]*height:\s*min\(264px,\s*48vh\);/su);
|
||||||
assert.match(html, /class="update-progress-footer"[\s\S]*id="updateProgressDetails"[\s\S]*id="updateProgressSize"[\s\S]*id="updateProgressSpeed"[\s\S]*id="updateProgressEta"[\s\S]*id="updateProgressText"/u);
|
assert.match(html, /class="update-progress-footer"[\s\S]*id="updateProgressDetails"[\s\S]*id="updateProgressSize"[\s\S]*id="updateProgressSpeed"[\s\S]*id="updateProgressEta"[\s\S]*id="updateProgressText"/u);
|
||||||
|
assert.match(html, /id="queueFilterResetBtn"[^>]*hidden[^>]*>Filter zurücksetzen</u);
|
||||||
assert.match(css, /#updateProgressDetails\s*\{[^}]*display:\s*grid;[^}]*grid-template-columns:\s*19ch 2ch 11ch 2ch 10ch;[^}]*column-gap:\s*0;[^}]*font-variant-numeric:\s*tabular-nums;/su);
|
assert.match(css, /#updateProgressDetails\s*\{[^}]*display:\s*grid;[^}]*grid-template-columns:\s*19ch 2ch 11ch 2ch 10ch;[^}]*column-gap:\s*0;[^}]*font-variant-numeric:\s*tabular-nums;/su);
|
||||||
assert.match(css, /\.update-progress-separator\s*\{[^}]*display:\s*grid;[^}]*place-items:\s*center;/su);
|
assert.match(css, /\.update-progress-separator\s*\{[^}]*display:\s*grid;[^}]*place-items:\s*center;/su);
|
||||||
assert.match(css, /#updateProgressSpeed\s*\{[^}]*text-align:\s*right;/su);
|
assert.match(css, /#updateProgressSpeed\s*\{[^}]*text-align:\s*right;/su);
|
||||||
|
|||||||
@@ -723,6 +723,39 @@ setTimeout(async () => {
|
|||||||
check('Status changes drop selections that leave the upload filter', uploadSelectionScope.statusChangeSelected.length === 0 && uploadSelectionScope.statusChangeVisible.join('|') === 'scope-active-z');
|
check('Status changes drop selections that leave the upload filter', uploadSelectionScope.statusChangeSelected.length === 0 && uploadSelectionScope.statusChangeVisible.join('|') === 'scope-active-z');
|
||||||
check('Selected upload actions ignore hidden stale selections', uploadSelectionScope.hiddenAction.selected.length === 0 && uploadSelectionScope.hiddenAction.retryDisabled && uploadSelectionScope.hiddenAction.moveDisabled);
|
check('Selected upload actions ignore hidden stale selections', uploadSelectionScope.hiddenAction.selected.length === 0 && uploadSelectionScope.hiddenAction.retryDisabled && uploadSelectionScope.hiddenAction.moveDisabled);
|
||||||
|
|
||||||
|
const queueFilterReset = await wc.executeJavaScript(\`(() => {
|
||||||
|
queueJobs = [
|
||||||
|
{ id: 'filter-a', file: 'C:/ui/filter-a.bin', fileName: 'alpha.bin', hoster: 'byse.sx', status: 'uploading', bytesUploaded: 10, bytesTotal: 100, progress: .1 },
|
||||||
|
{ id: 'filter-b', file: 'C:/ui/filter-b.bin', fileName: 'beta.bin', hoster: 'doodstream.com', status: 'error', bytesUploaded: 0, bytesTotal: 100, progress: 0 }
|
||||||
|
];
|
||||||
|
selectedJobIds.clear();
|
||||||
|
rebuildJobIndex();
|
||||||
|
renderQueueTable();
|
||||||
|
setUploadSidebarFilter('active');
|
||||||
|
document.getElementById('queueSearchInput').value = 'alpha';
|
||||||
|
document.getElementById('queueHosterFilter').value = 'byse.sx';
|
||||||
|
document.getElementById('queueStatusFilter').value = 'active';
|
||||||
|
applyQueueDetailFilters();
|
||||||
|
const button = document.getElementById('queueFilterResetBtn');
|
||||||
|
const before = { hidden: button.hidden, disabled: button.disabled };
|
||||||
|
button.click();
|
||||||
|
const after = {
|
||||||
|
hidden: button.hidden,
|
||||||
|
disabled: button.disabled,
|
||||||
|
sidebar: uploadSidebarFilter,
|
||||||
|
search: document.getElementById('queueSearchInput').value,
|
||||||
|
hoster: document.getElementById('queueHosterFilter').value,
|
||||||
|
status: document.getElementById('queueStatusFilter').value,
|
||||||
|
allPressed: document.querySelector('[data-upload-sidebar-target="all"]').getAttribute('aria-pressed'),
|
||||||
|
visible: _sortedJobsCache.map(job => job.id).join('|')
|
||||||
|
};
|
||||||
|
queueJobs = [];
|
||||||
|
rebuildJobIndex();
|
||||||
|
renderQueueTable();
|
||||||
|
return { before, after };
|
||||||
|
})()\`);
|
||||||
|
check('Queue filter reset clears sidebar, search, host, and status together', queueFilterReset.before.hidden === false && queueFilterReset.before.disabled === false && queueFilterReset.after.hidden === true && queueFilterReset.after.disabled === true && queueFilterReset.after.sidebar === 'all' && queueFilterReset.after.search === '' && queueFilterReset.after.hoster === '' && queueFilterReset.after.status === '' && queueFilterReset.after.allPressed === 'true' && queueFilterReset.after.visible === 'filter-a|filter-b');
|
||||||
|
|
||||||
const queueSelectionAnchor = await wc.executeJavaScript(\`(() => {
|
const queueSelectionAnchor = await wc.executeJavaScript(\`(() => {
|
||||||
queueJobs = ['a', 'b', 'c', 'd'].map(id => ({ id: 'anchor-' + id, file: 'C:/ui/anchor-' + id + '.bin', fileName: 'anchor-' + id + '.bin', hoster: 'byse.sx', status: 'queued', bytesUploaded: 0, bytesTotal: 100, progress: 0 }));
|
queueJobs = ['a', 'b', 'c', 'd'].map(id => ({ id: 'anchor-' + id, file: 'C:/ui/anchor-' + id + '.bin', fileName: 'anchor-' + id + '.bin', hoster: 'byse.sx', status: 'queued', bytesUploaded: 0, bytesTotal: 100, progress: 0 }));
|
||||||
selectedJobIds.clear();
|
selectedJobIds.clear();
|
||||||
|
|||||||
Reference in New Issue
Block a user