WIP: storage-fix-local-nas work before playlist controls feature

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
ghassanandClaude Sonnet 4.6 committed 2026-05-16 11:15:20 +03:00
1 parent 6b3ab5b65e
commit c160242dbc
46 files changed
+5365 -815

No files matched your search

@@ -4,18 +4,33 @@
.action-btn {
border: none;
border-radius: 8px;
padding: 8px 14px;
padding: 0 14px;
height: 36px;
font-size: 0.82rem;
font-weight: 500;
cursor: pointer;
background: var(--bg-secondary);
border: 1px solid var(--border-color);
color: var(--text-primary);
display: flex;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
transition: all 0.2s ease;
text-decoration: none;
white-space: nowrap;
line-height: 1;
box-sizing: border-box;
}
.action-btn i,
.action-btn svg {
font-size: 14px;
width: 14px;
height: 14px;
flex-shrink: 0;
display: inline-flex;
align-items: center;
}
.action-btn:hover {
@@ -113,6 +128,11 @@
<i class="bi bi-pencil"></i>
<span>Edit</span>
</button>
<button class="action-btn desktop-action" onclick="showDeleteModal('{{ $video->getRouteKey() }}', {{ json_encode($video->title) }})"
style="color:#ef4444;border-color:rgba(239,68,68,.35);">
<i class="bi bi-trash"></i>
<span>Delete</span>
</button>
@elseif (Auth::id() !== $video->user_id)
@php $isSubscribed = Auth::user()->isSubscribedTo($video->user); @endphp
<button type="button"
@@ -149,16 +169,13 @@
@if ($video->isShareable())
<button class="action-btn desktop-action"
onclick="openShareModal('{{ $video->share_url }}', '{{ addslashes($video->title) }}', '{{ route('videos.recordShare', $video) }}')">
<i class="bi bi-share"></i> Share
<i class="bi bi-share"></i><span>Share</span>
</button>
@endif
<!-- Save to Playlist Button -->
<button class="action-btn desktop-action" onclick="openAddToPlaylistModal({{ $video->id }})">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round">
<path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"></path>
</svg>
<i class="bi bi-bookmark"></i>
<span>Save</span>
</button>
@@ -590,4 +607,110 @@ if (!window._slideshowDlInit) {
});
};
}
// ── Video delete dialog ──────────────────────────────────────────────
@auth
@if(Auth::id() === $video->user_id)
(function () {
var _videoDeleteUrl = '{{ route('videos.destroy', $video) }}';
var _videoCsrf = '{{ csrf_token() }}';
var _owner2fa = {{ (Auth::user()->two_factor_enabled && Auth::user()->two_factor_secret) ? 'true' : 'false' }};
var _dlgId = 'vaDlg_{{ $video->id }}';
var _dlgHtml = '<div id="' + _dlgId + '" style="display:none;position:fixed;inset:0;z-index:9999;background:rgba(0,0,0,.75);backdrop-filter:blur(4px);align-items:center;justify-content:center;padding:16px;" onclick="if(event.target===this)_closeVaDlg()">'
+ '<div style="background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:16px;width:100%;max-width:420px;overflow:hidden;box-shadow:0 24px 60px rgba(0,0,0,.6);">'
+ '<div style="padding:20px 24px 16px;border-bottom:1px solid var(--border-color);display:flex;align-items:center;justify-content:space-between;">'
+ '<div style="display:flex;align-items:center;gap:10px;font-size:16px;font-weight:600;color:#ef4444;"><i class="bi bi-trash"></i> Delete Video</div>'
+ '<button onclick="_closeVaDlg()" style="background:none;border:none;color:var(--text-secondary);cursor:pointer;font-size:18px;line-height:1;padding:4px;"><i class="bi bi-x-lg"></i></button>'
+ '</div>'
+ '<div style="padding:20px 24px;">'
+ '<p id="' + _dlgId + '_title" style="margin:0 0 12px;font-size:14px;color:var(--text-primary);"></p>'
+ '<div style="background:rgba(239,68,68,.1);border:1px solid rgba(239,68,68,.25);border-radius:8px;padding:10px 14px;font-size:13px;color:#fca5a5;display:flex;gap:8px;align-items:flex-start;">'
+ '<i class="bi bi-exclamation-triangle-fill" style="flex-shrink:0;margin-top:1px;"></i>'
+ '<span>All views, likes, comments and HLS files will be deleted. This cannot be undone.</span>'
+ '</div>'
+ (_owner2fa ? '<div style="margin-top:16px;"><label style="font-size:13px;color:var(--text-secondary);display:flex;align-items:center;gap:6px;margin-bottom:8px;"><i class="bi bi-shield-lock-fill" style="color:#a78bfa;"></i>Enter your <strong style=\'color:var(--text-primary);\'>2FA code</strong> to confirm</label>'
+ '<input type="text" id="' + _dlgId + '_otp" inputmode="numeric" pattern="[0-9]*" maxlength="6" autocomplete="one-time-code" placeholder="000000"'
+ ' style="width:100%;background:var(--bg-primary);border:1px solid var(--border-color);border-radius:8px;padding:10px 14px;color:#fff;font-size:22px;letter-spacing:.3em;text-align:center;box-sizing:border-box;"></div>' : '')
+ '<div id="' + _dlgId + '_err" style="display:none;margin-top:12px;padding:8px 12px;background:rgba(239,68,68,.12);border:1px solid rgba(239,68,68,.3);border-radius:6px;color:#f87171;font-size:13px;"></div>'
+ '</div>'
+ '<div style="padding:12px 24px 20px;display:flex;gap:10px;justify-content:flex-end;">'
+ '<button onclick="_closeVaDlg()" class="action-btn">Cancel</button>'
+ '<button id="' + _dlgId + '_btn" onclick="_confirmVaDlg()" class="action-btn" style="background:#ef4444;color:#fff;border-color:#ef4444;"><i class="bi bi-trash"></i> Delete</button>'
+ '</div>'
+ '</div></div>';
var _dlgInserted = false;
function _ensureDlg() {
if (_dlgInserted) return;
document.body.insertAdjacentHTML('beforeend', _dlgHtml);
_dlgInserted = true;
}
window.showDeleteModal = function (routeKey, title) {
_ensureDlg();
var dlg = document.getElementById(_dlgId);
document.getElementById(_dlgId + '_title').textContent = 'You are about to permanently delete "' + title + '".';
document.getElementById(_dlgId + '_err').style.display = 'none';
if (_owner2fa) { document.getElementById(_dlgId + '_otp').value = ''; }
dlg.style.display = 'flex';
dlg.style.alignItems = 'center';
dlg.style.justifyContent = 'center';
if (_owner2fa) setTimeout(function () { document.getElementById(_dlgId + '_otp').focus(); }, 100);
document.addEventListener('keydown', _escHandler);
};
window._closeVaDlg = function () {
var dlg = document.getElementById(_dlgId);
if (dlg) dlg.style.display = 'none';
document.removeEventListener('keydown', _escHandler);
};
function _escHandler(e) { if (e.key === 'Escape') _closeVaDlg(); }
window._confirmVaDlg = function () {
var btn = document.getElementById(_dlgId + '_btn');
var errEl = document.getElementById(_dlgId + '_err');
var otp = _owner2fa ? document.getElementById(_dlgId + '_otp').value.replace(/\s/g, '') : '';
if (_owner2fa && otp.length !== 6) {
errEl.textContent = 'Please enter your 6-digit 2FA code.';
errEl.style.display = 'block';
document.getElementById(_dlgId + '_otp').focus();
return;
}
btn.disabled = true;
btn.innerHTML = '<i class="bi bi-hourglass-split"></i> Deleting…';
errEl.style.display = 'none';
var body = new URLSearchParams({ '_token': _videoCsrf, '_method': 'DELETE' });
if (_owner2fa) body.append('otp_code', otp);
fetch(_videoDeleteUrl, {
method: 'POST',
headers: { 'Accept': 'application/json', 'Content-Type': 'application/x-www-form-urlencoded' },
body: body.toString()
})
.then(function (r) { return r.json(); })
.then(function (data) {
if (data.success) {
window.location.href = '/';
} else {
errEl.textContent = data.message || 'Delete failed.';
errEl.style.display = 'block';
btn.disabled = false;
btn.innerHTML = '<i class="bi bi-trash"></i> Delete';
if (_owner2fa) { document.getElementById(_dlgId + '_otp').value = ''; document.getElementById(_dlgId + '_otp').focus(); }
}
})
.catch(function () {
errEl.textContent = 'An error occurred. Please try again.';
errEl.style.display = 'block';
btn.disabled = false;
btn.innerHTML = '<i class="bi bi-trash"></i> Delete';
});
};
})();
@endif
@endauth
</script>