load more button and other device support
This commit is contained in:
@@ -112,6 +112,7 @@ async function loadVideos() {
|
||||
|
||||
try {
|
||||
isLoading = true;
|
||||
updateLoadMoreState();
|
||||
const response = await fetch('/api/videos', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
@@ -128,6 +129,7 @@ async function loadVideos() {
|
||||
console.error("Failed to load videos:", err);
|
||||
} finally {
|
||||
isLoading = false;
|
||||
updateLoadMoreState();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,6 +181,13 @@ async function initApp() {
|
||||
observer.observe(sentinel);
|
||||
}
|
||||
|
||||
const loadMoreBtn = document.getElementById('load-more-btn');
|
||||
if (loadMoreBtn) {
|
||||
loadMoreBtn.onclick = () => {
|
||||
loadVideos();
|
||||
};
|
||||
}
|
||||
|
||||
await loadVideos();
|
||||
}
|
||||
|
||||
@@ -263,6 +272,7 @@ function handleSearch(value) {
|
||||
renderedVideoIds.clear();
|
||||
const grid = document.getElementById('video-grid');
|
||||
if (grid) grid.innerHTML = "";
|
||||
updateLoadMoreState();
|
||||
loadVideos();
|
||||
}
|
||||
|
||||
@@ -371,6 +381,7 @@ function resetAndReload() {
|
||||
renderedVideoIds.clear();
|
||||
const grid = document.getElementById('video-grid');
|
||||
if (grid) grid.innerHTML = "";
|
||||
updateLoadMoreState();
|
||||
loadVideos();
|
||||
}
|
||||
|
||||
@@ -384,6 +395,13 @@ function ensureViewportFilled() {
|
||||
}
|
||||
}
|
||||
|
||||
function updateLoadMoreState() {
|
||||
const loadMoreBtn = document.getElementById('load-more-btn');
|
||||
if (!loadMoreBtn) return;
|
||||
loadMoreBtn.disabled = isLoading || !hasNextPage;
|
||||
loadMoreBtn.style.display = hasNextPage ? 'flex' : 'none';
|
||||
}
|
||||
|
||||
function renderMenu() {
|
||||
const session = getSession();
|
||||
const serverEntries = getServerEntries();
|
||||
|
||||
@@ -81,11 +81,14 @@
|
||||
|
||||
<main id="video-grid" class="grid-container"></main>
|
||||
<div id="sentinel"></div>
|
||||
<button id="load-more-btn" class="load-more-btn" title="Load More">
|
||||
<img class="icon-svg" src="https://cdn.jsdelivr.net/npm/heroicons@2.0.13/24/outline/chevron-down.svg" alt="Load More">
|
||||
</button>
|
||||
|
||||
<div id="video-modal" class="modal">
|
||||
<div class="modal-content">
|
||||
<span class="close" onclick="closePlayer()">×</span>
|
||||
<video id="player" controls autoplay></video>
|
||||
<video id="player" controls autoplay playsinline webkit-playsinline></video>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -122,6 +122,11 @@ body.theme-light {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
:focus-visible {
|
||||
outline: 2px solid var(--accent);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* CDN-served icon images (Heroicons) */
|
||||
.icon-svg {
|
||||
width: 24px;
|
||||
@@ -450,6 +455,34 @@ body.theme-light .setting-item select option {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.load-more-btn {
|
||||
position: sticky;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
margin: 16px auto 32px auto;
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid var(--border);
|
||||
background: var(--bg-secondary);
|
||||
color: var(--text-primary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.load-more-btn:hover {
|
||||
background: var(--bg-tertiary);
|
||||
}
|
||||
|
||||
.load-more-btn:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* Grid Container */
|
||||
.grid-container {
|
||||
display: grid;
|
||||
@@ -465,6 +498,88 @@ body.theme-light .setting-item select option {
|
||||
gap: 12px;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.top-bar {
|
||||
flex-wrap: wrap;
|
||||
height: auto;
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
.search-container {
|
||||
order: 3;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.actions {
|
||||
order: 2;
|
||||
width: 100%;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.logo {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.icon-btn {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1600px) {
|
||||
body {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.top-bar {
|
||||
height: 72px;
|
||||
padding: 0 36px;
|
||||
}
|
||||
|
||||
.grid-container {
|
||||
gap: 24px;
|
||||
padding: 32px 48px;
|
||||
}
|
||||
|
||||
.video-card h4 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.video-card p {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1920px) {
|
||||
.grid-container {
|
||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (pointer: coarse) {
|
||||
.icon-btn {
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
padding: 10px 14px;
|
||||
}
|
||||
|
||||
.setting-item select,
|
||||
.input-row input {
|
||||
padding: 10px 14px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
* {
|
||||
transition: none !important;
|
||||
animation: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Video Card */
|
||||
|
||||
Reference in New Issue
Block a user