Align saved video actions and confirm storage removal
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user