Align saved video actions and confirm storage removal

This commit is contained in:
Jonathan Sykes
2026-10-03 16:10:46 +08:00
parent cf9b6f4133
commit ca836e0cd2
8 changed files with 143 additions and 18 deletions

View File

@@ -9177,7 +9177,9 @@ async function renderSaved() {
resetListHeaderExtras();
els.listActions.innerHTML = '';
els.status.classList.add('hidden');
$('batchBar').classList.add('hidden');
const c = els.cards;
c.classList.remove('select-mode');
c.innerHTML = '<div class="status">Loading saved videos…</div>';
let res;
@@ -9185,6 +9187,7 @@ async function renderSaved() {
if (view.type !== 'saved') return; // navigated away
if (!res || !res.ok) {
$('listFilterBar').classList.add('hidden');
c.innerHTML = '';
const empty = document.createElement('div');
empty.className = 'empty-state';
@@ -9200,6 +9203,7 @@ async function renderSaved() {
const total = res.total || 0;
if (!items.length) {
$('listFilterBar').classList.add('hidden');
c.innerHTML = '';
const empty = document.createElement('div');
empty.className = 'empty-state';
@@ -9214,12 +9218,18 @@ async function renderSaved() {
// Header actions: total + clear all.
const clearAll = document.createElement('button');
clearAll.type = 'button';
clearAll.className = 'saved-clear';
clearAll.textContent = 'Clear all';
clearAll.setAttribute('aria-label', 'Clear all saved videos');
clearAll.onclick = () => {
showModal('Clear all saved videos?', document.createTextNode('Frees disk space. Normal videos re-download on demand, but edited videos are removed for good (they exist only here).'), [
{ label: 'Cancel', onClick: closeModal },
{ label: 'Clear all', danger: true, onClick: async () => {
try { await API.cacheClear(); } catch {}
try {
const result = await API.cacheClear();
if (result?.ok === false) throw new Error(result.error || 'Storage could not be cleared');
} catch { toast('Could not clear saved videos. Try again.'); closeModal(); return; }
cachedIds.clear();
// Custom cuts live only in the cache — clearing it destroys their media,
// so drop their registry entries and playlist references too.
@@ -9237,12 +9247,14 @@ async function renderSaved() {
} },
]);
};
els.listActions.appendChild(clearAll);
c.innerHTML = '';
const summary = document.createElement('div');
summary.className = 'saved-summary';
summary.innerHTML = `<span class="saved-total">${fmtBytes(total)}</span><span class="saved-sub">${items.length} video${items.length === 1 ? '' : 's'} stored offline</span>`;
summary.className = 'saved-summary saved-storage-summary';
summary.innerHTML = `<span class="saved-storage-text">${fmtBytes(total)} · ${items.length} video${items.length === 1 ? '' : 's'} stored offline</span>`;
summary.appendChild(clearAll);
$('listFilterBar').classList.remove('hidden');
c.appendChild(summary);
items.forEach((it) => {
@@ -9252,6 +9264,7 @@ async function renderSaved() {
const isCustom = (known && known.custom) || String(it.id).startsWith('edit_');
const fallbackThumb = isCustom ? '' : `https://i.ytimg.com/vi/${it.id}/mqdefault.jpg`;
const v = known || { id: it.id, title: videoTitleById(it.id), thumbnail: fallbackThumb };
if (!SavedPage.matches(v, listFilter)) return;
const row = document.createElement('div');
row.className = 'card saved-card' + (isCustom ? ' custom' : '');
row.dataset.id = it.id;
@@ -9261,27 +9274,41 @@ async function renderSaved() {
<div class="card-info">
<div class="card-title"></div>
<div class="card-channel saved-size">${isCustom ? '✂ edited · ' : ''}${fmtBytes(it.size)}</div>
</div>
<button class="card-act" data-act="export" title="Save to gallery" aria-label="Save to gallery">⤓</button>
<button class="card-del" title="Delete saved file">✕</button>`;
</div>`;
row.querySelector('.card-title').textContent = v.title || it.id;
row.addEventListener('click', (e) => {
if (e.target.closest('[data-act="export"]')) { e.stopPropagation(); exportToDevice(v); return; }
if (e.target.closest('.card-del')) return;
if (e.target.closest('.saved-file-actions')) return;
playFromList([v], 0, 'saved');
});
row.querySelector('.card-del').addEventListener('click', async (e) => {
e.stopPropagation();
if (isCustom) { await deleteCustomVideo(it.id); renderSaved(); return; }
try { await API.cacheDelete(it.id); } catch {}
cachedIds.delete(it.id);
if (current && current.meta && current.meta.id === it.id) updateNowPlayingActions();
markCardCacheState(it.id, 'none');
toast('Removed from cache');
renderSaved();
SavedPage.actions(row, v, () => exportToDevice(v), () => {
showModal(`Delete saved file “${v.title || v.id}”?`, document.createTextNode(isCustom
? 'This edited copy exists only on this device and will be removed permanently.'
: 'Frees space on this device. You can save this video again later.'), [
{ label: 'Cancel', onClick: closeModal },
{ label: 'Delete saved file', danger: true, onClick: async () => {
try {
const result = await API.cacheDelete(it.id);
if (result?.ok === false) throw new Error(result.error || 'Storage could not be updated');
} catch { toast('Could not delete this saved file. Try again.'); closeModal(); return; }
cachedIds.delete(it.id);
if (isCustom) {
data.customVideos = (data.customVideos || []).filter(x => x.id !== it.id);
data.playlists.forEach(pl => { pl.videos = pl.videos.filter(x => x.id !== it.id); });
data.queue = (data.queue || []).filter(x => x.id !== it.id);
persist();
}
if (current?.meta?.id === it.id) updateNowPlayingActions();
markCardCacheState(it.id, 'none'); closeModal();
toast('Removed saved file'); renderSaved();
} },
]);
});
c.appendChild(row);
});
if (!c.querySelector('.saved-card')) {
const message = document.createElement('p'); message.className = 'status';
message.textContent = 'No saved videos match your filter.'; c.appendChild(message);
}
markPlayingCard();
}