import { api } from '../api.js';
import { showToast } from '../components/toast.js';
import { esc } from '../utils.js';
import { t } from '../i18n.js';
function formatFileSize(bytes) {
if (!bytes) return '--';
if (bytes >= 1073741824) return `${(bytes / 1073741824).toFixed(1)} GB`;
if (bytes >= 1048576) return `${(bytes / 1048576).toFixed(1)} MB`;
if (bytes >= 1024) return `${(bytes / 1024).toFixed(0)} KB`;
return `${bytes} B`;
}
// #157: classify a content item's expiry state for the card. `expired` when the server
// deactivated it (is_active===0) or its expires_at has passed; `dateLabel` is the local
// expiry date/time (present whenever expires_at is set, past or future).
function expiryInfo(c) {
const hasExpiry = c.expires_at != null && c.expires_at !== '';
const ts = hasExpiry ? Number(c.expires_at) * 1000 : null;
const past = ts != null && ts <= Date.now();
const expired = c.is_active === 0 || past;
const dateLabel = ts != null
? new Date(ts).toLocaleString([], { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' })
: '';
return { expired, dateLabel };
}
// Epoch seconds -> a value in the viewer's LOCAL wall-clock
// (YYYY-MM-DDTHH:MM). Empty string for no expiry.
function toLocalDatetimeInput(epochSec) {
if (epochSec == null || epochSec === '') return '';
const d = new Date(Number(epochSec) * 1000);
const p = (n) => String(n).padStart(2, '0');
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}T${p(d.getHours())}:${p(d.getMinutes())}`;
}
// Lazy-load authenticated thumbnails/previews. A plain can't send the
// Bearer token, and the content thumbnail/file endpoints require auth (or a
// playlist/widget reference) - so a just-uploaded item's thumbnail 403'd. We fetch
// with the token and swap in an object URL. IntersectionObserver keeps it lazy so
// we stay under the /api/content rate limit; the object URL is revoked after load.
let _authImgObserver = null;
function loadAuthImage(img) {
const url = img.dataset.authSrc;
if (!url) return;
delete img.dataset.authSrc;
fetch(url, { headers: { Authorization: `Bearer ${localStorage.getItem('token')}` } })
.then(r => (r.ok ? r.blob() : Promise.reject(r.status)))
.then(blob => {
const obj = URL.createObjectURL(blob);
img.addEventListener('load', () => URL.revokeObjectURL(obj), { once: true });
img.src = obj;
})
.catch(() => { img.style.opacity = '0.25'; });
}
function hydrateAuthImages(root) {
const imgs = root.querySelectorAll('img[data-auth-src]');
if (typeof IntersectionObserver === 'undefined') { imgs.forEach(loadAuthImage); return; }
if (!_authImgObserver) {
_authImgObserver = new IntersectionObserver((entries, obs) => {
for (const e of entries) if (e.isIntersecting) { obs.unobserve(e.target); loadAuthImage(e.target); }
}, { rootMargin: '300px' });
}
imgs.forEach(img => _authImgObserver.observe(img));
}
export function render(container) {
container.innerHTML = `
${state.currentFolderId ? t('content.empty_folder_desc') : t('content.no_content_desc')}
${esc(err.message)}
${t('content.expires_hint')}
${t('content.replace_file_hint')}