'use strict'; /* Library overview: a grid of covers. */ (async function () { let me, data; try { [me, data] = await Promise.all([api('api/me'), api('api/library')]); } catch (e) { return showError(e); } topNav(me); const main = $('main'); main.textContent = ''; if (!data.mangas.length) { main.append(h('div', { class: 'empty' }, h('h1', {}, data.scanning ? 'Scanning the library…' : 'The library is empty'), h('p', { class: 'muted' }, data.scanning ? 'This page refreshes by itself.' : 'Put manga folders into the library folder (see README.md for the format)' + (me.admin ? ', then rescan it in the settings.' : '.')))); if (data.scanning) setTimeout(() => location.reload(), 2000); return; } const filter = h('input', { type: 'search', placeholder: 'Filter…', 'aria-label': 'Filter', value: store.get('filter') || '' }); const sort = h('select', { 'aria-label': 'Sort' }, h('option', { value: 'title' }, 'A–Z'), h('option', { value: 'updated' }, 'Recently updated'), h('option', { value: 'read' }, 'Recently read')); sort.value = store.get('sort') || 'title'; const grid = h('div', { class: 'grid' }); main.append(h('div', { class: 'toolbar' }, filter, sort), grid); function render() { store.set('filter', filter.value); store.set('sort', sort.value); const q = filter.value.trim().toLowerCase(); const list = data.mangas.filter(m => !q || m.title.toLowerCase().includes(q)); if (sort.value === 'updated') list.sort((a, b) => b.updated - a.updated); if (sort.value === 'read') list.sort((a, b) => b.lastRead - a.lastRead); grid.textContent = ''; for (const m of list) { grid.append(h('a', { class: 'tile', href: mangaUrl(m.id) }, h('div', { class: 'cv' }, m.cover ? h('img', { loading: 'lazy', src: m.cover, alt: '' }) : null), m.unread ? h('span', { class: 'badge' }, m.unread + ' new') : null, h('div', { class: 't' }, m.title), h('div', { class: 's' }, m.read ? `${m.read} / ${m.chapters} read` : `${m.chapters} chapter${m.chapters === 1 ? '' : 's'}`), m.read ? h('div', { class: 'prog' }, h('i', { style: `width:${Math.round((100 * m.read) / m.chapters)}%` })) : null)); } if (!list.length) grid.append(h('p', { class: 'muted' }, 'Nothing matches.')); } filter.addEventListener('input', render); sort.addEventListener('change', render); render(); })();