diff --git a/package.json b/package.json index 902ef81..67c79ab 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "waifu-board", - "version": "v1.6.3", + "version": "v1.6.4", "description": "An image board app to store and browse your favorite waifus!", "main": "main.js", "scripts": { diff --git a/src/hamburger.js b/src/hamburger.js new file mode 100644 index 0000000..28c0595 --- /dev/null +++ b/src/hamburger.js @@ -0,0 +1,19 @@ +const btn = document.getElementById("hamburger-btn"); +const sidebar = document.querySelector(".sidebar"); + +btn.addEventListener("click", (e) => { + e.stopPropagation(); + sidebar.classList.toggle("active"); + btn.textContent = sidebar.classList.contains("active") ? "✕" : "☰"; +}); + +document.addEventListener("click", (e) => { + if (window.innerWidth <= 767) { + const outsideSidebar = !sidebar.contains(e.target); + const outsideBtn = !btn.contains(e.target); + if (outsideSidebar && outsideBtn) { + sidebar.classList.remove("active"); + btn.textContent = "☰"; + } + } +}); \ No newline at end of file diff --git a/src/renderer.js b/src/renderer.js index 71d7613..e0c89be 100644 --- a/src/renderer.js +++ b/src/renderer.js @@ -7,99 +7,100 @@ import { showMessage as uiShowMessage } from './modules/ui-utils.js'; import { applyLayoutToGallery } from './modules/layout-manager.js'; document.addEventListener('DOMContentLoaded', async () => { - const domRefs = getDomElements(); - - const currentLayout = 'grid'; - let currentSource = ''; - let currentPage = 1; - let isFetching = false; - const favoriteIds = new Set(); + const domRefs = getDomElements(); - try { - if (window.api && window.api.getFavorites) { - const favs = await window.api.getFavorites(); - favs.forEach(f => favoriteIds.add(String(f.id))); - } - } catch (e) { console.error(e); } + const currentLayout = 'grid'; + let currentSource = ''; + let currentPage = 1; + let isFetching = false; + const favoriteIds = new Set(); + const isBooksPage = window.location.pathname.includes('books.html'); - function showMessage(msg, type = 'success') { if (domRefs.messageBar) uiShowMessage(domRefs.messageBar, msg, type); } - function showTagModal(tags) { if (domRefs.tagInfoContent) { populateTagModal(domRefs.tagInfoContent, tags); domRefs.tagInfoModal.classList.remove('hidden'); } } - function localCreateImageCard(id, tags, img, thumb, type) { - return createImageCard(id, tags, img, thumb, type, { currentLayout, showMessage, showTagModal, applyLayoutToGallery, favoritesGallery: document.getElementById('favorites-gallery'), favoriteIds }); - } - function updateHeader() { if (domRefs.headerContext) domRefs.headerContext.classList.add('hidden'); } + try { + if (window.api && window.api.getFavorites) { + const favs = await window.api.getFavorites(); + favs.forEach(f => favoriteIds.add(String(f.id))); + } + } catch (e) { console.error(e); } - const callbacks = { showMessage, applyLayoutToGallery, updateHeader, createImageCard: localCreateImageCard }; + function showMessage(msg, type = 'success') { if (domRefs.messageBar) uiShowMessage(domRefs.messageBar, msg, type); } + function showTagModal(tags) { if (domRefs.tagInfoContent) { populateTagModal(domRefs.tagInfoContent, tags); domRefs.tagInfoModal.classList.remove('hidden'); } } + function localCreateImageCard(id, tags, img, thumb, type) { + return createImageCard(id, tags, img, thumb, type, { currentLayout, showMessage, showTagModal, applyLayoutToGallery, favoritesGallery: document.getElementById('favorites-gallery'), favoriteIds }); + } + function updateHeader() { if (domRefs.headerContext) domRefs.headerContext.classList.add('hidden'); } - let currentChapters = []; - let currentChapterPage = 1; - const CHAPTERS_PER_PAGE = 10; + const callbacks = { showMessage, applyLayoutToGallery, updateHeader, createImageCard: localCreateImageCard }; - function renderChapterPage() { - const listContainer = document.getElementById('chapter-list-container'); - if (!listContainer) return; - listContainer.innerHTML = ''; + let currentChapters = []; + let currentChapterPage = 1; + const CHAPTERS_PER_PAGE = 10; - const start = (currentChapterPage - 1) * CHAPTERS_PER_PAGE; - const end = start + CHAPTERS_PER_PAGE; - const slice = currentChapters.slice(start, end); + function renderChapterPage() { + const listContainer = document.getElementById('chapter-list-container'); + if (!listContainer) return; + listContainer.innerHTML = ''; - if (slice.length === 0) { - listContainer.innerHTML = '
Loading content...
No content found.
'; - return; - } - - const isTextMode = response.data[0].type === 'text'; - - if (isTextMode) { - const pageData = response.data[0]; - const textDiv = document.createElement('div'); - textDiv.className = 'reader-text-content'; - textDiv.innerHTML = pageData.content; - readerContent.appendChild(textDiv); - } else { - response.data.forEach(page => { - const img = document.createElement('img'); - img.className = 'reader-page-img'; - img.src = page.url; - img.loading = "lazy"; - readerContent.appendChild(img); - }); - } - - } catch (err) { - console.error(err); - showMessage('Failed to load content', 'error'); - } - } - - if (domRefs.searchModal) setupGlobalKeybinds(domRefs.searchModal); - if (domRefs.tagInfoCloseButton) domRefs.tagInfoCloseButton.onclick = () => domRefs.tagInfoModal.classList.add('hidden'); - if (domRefs.searchIconButton) { - domRefs.searchIconButton.onclick = () => { domRefs.searchModal.classList.remove('hidden'); domRefs.searchInput?.focus(); }; - domRefs.searchCloseButton.onclick = () => domRefs.searchModal.classList.add('hidden'); - } - - if (domRefs.sourceList) { - if (domRefs.contentGallery) applyLayoutToGallery(domRefs.contentGallery, currentLayout); - - const isBooksPage = window.location.pathname.includes('books.html'); - const contentType = isBooksPage ? 'book-board' : 'image-board'; - - let initialSource = await populateSources(domRefs.sourceList, contentType); - currentSource = initialSource; - updateHeader(); - - domRefs.sourceList.addEventListener('click', (e) => { - const button = e.target.closest('.source-button'); - if (button) { - domRefs.sourceList.querySelectorAll('.source-button').forEach(b => b.classList.remove('active')); - button.classList.add('active'); - currentSource = button.dataset.source; - updateHeader(); - currentPage = 1; - if (domRefs.searchInput?.value.trim()) performSearch(currentSource, domRefs.searchInput, currentLayout, domRefs, callbacks); - else if (domRefs.searchInput) performSearch(currentSource, { value: "" }, currentLayout, domRefs, callbacks); - } - }); - - if (domRefs.contentGallery) { - domRefs.contentGallery.addEventListener('click', (e) => { - const card = e.target.closest('.image-entry'); - if (card && isBooksPage) { - if (e.target.closest('button')) return; - e.preventDefault(); e.stopPropagation(); - - const bookId = card.dataset.id; - const img = card.querySelector('img'); - const title = card.dataset.title || "Unknown"; - - if (bookId) openBookDetails(bookId, img ? img.src : '', title, []); - } - }); - } - - const scrollContainer = document.querySelector('.content-view'); - if (scrollContainer) { - scrollContainer.addEventListener('scroll', async () => { - if (scrollContainer.scrollTop + scrollContainer.clientHeight >= scrollContainer.scrollHeight - 600) { - if (isFetching) return; - isFetching = true; - currentPage++; - if (domRefs.infiniteLoadingSpinner) domRefs.infiniteLoadingSpinner.classList.remove('hidden'); - try { await loadMoreResults(currentSource, currentPage, currentLayout, domRefs, callbacks); } - catch (error) { currentPage--; } - finally { isFetching = false; if (domRefs.infiniteLoadingSpinner) domRefs.infiniteLoadingSpinner.classList.add('hidden'); } + let highResCover = null; + try { + const aniRes = await window.api.getMetadata(title); + if (aniRes.success && aniRes.data && aniRes.data.coverImage.extraLarge) { + highResCover = aniRes.data.coverImage.extraLarge; } - }); - } + } catch (e) {} - if (domRefs.searchButton) { - domRefs.searchButton.onclick = () => { currentPage = 1; performSearch(currentSource, domRefs.searchInput, currentLayout, domRefs, callbacks); }; - } - } + try { + const response = await window.api.getChapters(currentSource, id); + currentChapters = response.success ? response.data.chapters : []; + currentChapterPage = 1; - if (document.getElementById('favorites-gallery')) { - const favGallery = document.getElementById('favorites-gallery'); - const rawFavorites = await window.api.getFavorites(); - favGallery.innerHTML = ''; - if (!rawFavorites || rawFavorites.length === 0) favGallery.innerHTML = 'No favorites found.
Loading content...
No content found.
'; + return; + } + + const isTextMode = response.data[0].type === 'text'; + + if (isTextMode) { + const pageData = response.data[0]; + const textDiv = document.createElement('div'); + textDiv.className = 'reader-text-content'; + textDiv.innerHTML = pageData.content; + readerContent.appendChild(textDiv); + } else { + response.data.forEach(page => { + const img = document.createElement('img'); + img.className = 'reader-page-img'; + img.src = page.url; + img.loading = "lazy"; + readerContent.appendChild(img); + }); + } + + } catch (err) { + console.error(err); + showMessage('Failed to load content', 'error'); + } + } + + if (domRefs.searchModal) setupGlobalKeybinds(domRefs.searchModal); + if (domRefs.tagInfoCloseButton) domRefs.tagInfoCloseButton.onclick = () => domRefs.tagInfoModal.classList.add('hidden'); + if (domRefs.searchIconButton) { + domRefs.searchIconButton.onclick = () => { domRefs.searchModal.classList.remove('hidden'); domRefs.searchInput?.focus(); }; + domRefs.searchCloseButton.onclick = () => domRefs.searchModal.classList.add('hidden'); + } + + if (domRefs.sourceList) { + if (domRefs.contentGallery) { + applyLayoutToGallery(domRefs.contentGallery, currentLayout); + } + + const contentType = isBooksPage ? 'book-board' : 'image-board'; + + let initialSource = await populateSources(domRefs.sourceList, contentType); + currentSource = initialSource; + updateHeader(); + + domRefs.sourceList.addEventListener('click', (e) => { + const button = e.target.closest('.source-button'); + if (button) { + domRefs.sourceList.querySelectorAll('.source-button').forEach(b => b.classList.remove('active')); + button.classList.add('active'); + currentSource = button.dataset.source; + updateHeader(); + currentPage = 1; + + // Apply books-only class when searching on books page + if (isBooksPage && domRefs.contentGallery) { + domRefs.contentGallery.classList.add('books-only'); + } + + if (domRefs.searchInput?.value.trim()) performSearch(currentSource, domRefs.searchInput, currentLayout, domRefs, callbacks); + else if (domRefs.searchInput) performSearch(currentSource, { value: "" }, currentLayout, domRefs, callbacks); + } + }); + + if (domRefs.contentGallery) { + domRefs.contentGallery.addEventListener('click', (e) => { + const card = e.target.closest('.image-entry'); + if (card && isBooksPage) { + if (e.target.closest('button')) return; + e.preventDefault(); e.stopPropagation(); + + const bookId = card.dataset.id; + const img = card.querySelector('img'); + const title = card.dataset.title || "Unknown"; + + if (bookId) openBookDetails(bookId, img ? img.src : '', title, []); + } + }); + } + + const scrollContainer = document.querySelector('.content-view'); + if (scrollContainer) { + scrollContainer.addEventListener('scroll', async () => { + if (scrollContainer.scrollTop + scrollContainer.clientHeight >= scrollContainer.scrollHeight - 600) { + if (isFetching) return; + isFetching = true; + currentPage++; + if (domRefs.infiniteLoadingSpinner) domRefs.infiniteLoadingSpinner.classList.remove('hidden'); + try { await loadMoreResults(currentSource, currentPage, currentLayout, domRefs, callbacks); } + catch (error) { currentPage--; } + finally { isFetching = false; if (domRefs.infiniteLoadingSpinner) domRefs.infiniteLoadingSpinner.classList.add('hidden'); } + } + }); + } + + if (domRefs.searchButton) { + domRefs.searchButton.onclick = () => { currentPage = 1; performSearch(currentSource, domRefs.searchInput, currentLayout, domRefs, callbacks); }; + } + } + + if (document.getElementById('favorites-gallery')) { + const favGallery = document.getElementById('favorites-gallery'); + const rawFavorites = await window.api.getFavorites(); + favGallery.innerHTML = ''; + if (!rawFavorites || rawFavorites.length === 0) favGallery.innerHTML = 'No favorites found.