more upgrade
This commit is contained in:
@@ -2,7 +2,8 @@
|
|||||||
let currentPage = 1;
|
let currentPage = 1;
|
||||||
const perPage = 12;
|
const perPage = 12;
|
||||||
const renderedVideoIds = new Set();
|
const renderedVideoIds = new Set();
|
||||||
let currentQuery = "";
|
let hasNextPage = true;
|
||||||
|
let isLoading = false;
|
||||||
|
|
||||||
// 2. Observer Definition (Must be defined before initApp uses it)
|
// 2. Observer Definition (Must be defined before initApp uses it)
|
||||||
const observer = new IntersectionObserver((entries) => {
|
const observer = new IntersectionObserver((entries) => {
|
||||||
@@ -74,11 +75,15 @@ async function InitializeServerStatus() {
|
|||||||
async function loadVideos() {
|
async function loadVideos() {
|
||||||
const session = JSON.parse(localStorage.getItem('session'));
|
const session = JSON.parse(localStorage.getItem('session'));
|
||||||
if (!session) return;
|
if (!session) return;
|
||||||
|
if (isLoading || !hasNextPage) return;
|
||||||
|
|
||||||
|
const searchInput = document.getElementById('search-input');
|
||||||
|
const query = searchInput ? searchInput.value : "";
|
||||||
|
|
||||||
// Build the request body
|
// Build the request body
|
||||||
let body = {
|
let body = {
|
||||||
channel: session.channel.id,
|
channel: session.channel.id,
|
||||||
query: currentQuery,
|
query: query || "",
|
||||||
page: currentPage,
|
page: currentPage,
|
||||||
perPage: perPage,
|
perPage: perPage,
|
||||||
server: session.server
|
server: session.server
|
||||||
@@ -94,6 +99,7 @@ async function loadVideos() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
isLoading = true;
|
||||||
const response = await fetch('/api/videos', {
|
const response = await fetch('/api/videos', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
@@ -101,9 +107,13 @@ async function loadVideos() {
|
|||||||
});
|
});
|
||||||
const videos = await response.json();
|
const videos = await response.json();
|
||||||
renderVideos(videos);
|
renderVideos(videos);
|
||||||
|
hasNextPage = videos && videos.pageInfo ? videos.pageInfo.hasNextPage !== false : true;
|
||||||
currentPage++;
|
currentPage++;
|
||||||
|
ensureViewportFilled();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Failed to load videos:", err);
|
console.error("Failed to load videos:", err);
|
||||||
|
} finally {
|
||||||
|
isLoading = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,7 +121,8 @@ function renderVideos(videos) {
|
|||||||
const grid = document.getElementById('video-grid');
|
const grid = document.getElementById('video-grid');
|
||||||
if (!grid) return;
|
if (!grid) return;
|
||||||
|
|
||||||
videos.items.forEach(v => {
|
const items = videos && Array.isArray(videos.items) ? videos.items : [];
|
||||||
|
items.forEach(v => {
|
||||||
if (renderedVideoIds.has(v.id)) return;
|
if (renderedVideoIds.has(v.id)) return;
|
||||||
|
|
||||||
const card = document.createElement('div');
|
const card = document.createElement('div');
|
||||||
@@ -170,8 +181,8 @@ function closePlayer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleSearch(value) {
|
function handleSearch(value) {
|
||||||
currentQuery = value || "";
|
|
||||||
currentPage = 1;
|
currentPage = 1;
|
||||||
|
hasNextPage = true;
|
||||||
renderedVideoIds.clear();
|
renderedVideoIds.clear();
|
||||||
const grid = document.getElementById('video-grid');
|
const grid = document.getElementById('video-grid');
|
||||||
if (grid) grid.innerHTML = "";
|
if (grid) grid.innerHTML = "";
|
||||||
@@ -224,12 +235,23 @@ function buildDefaultOptions(channel) {
|
|||||||
|
|
||||||
function resetAndReload() {
|
function resetAndReload() {
|
||||||
currentPage = 1;
|
currentPage = 1;
|
||||||
|
hasNextPage = true;
|
||||||
renderedVideoIds.clear();
|
renderedVideoIds.clear();
|
||||||
const grid = document.getElementById('video-grid');
|
const grid = document.getElementById('video-grid');
|
||||||
if (grid) grid.innerHTML = "";
|
if (grid) grid.innerHTML = "";
|
||||||
loadVideos();
|
loadVideos();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ensureViewportFilled() {
|
||||||
|
if (!hasNextPage || isLoading) return;
|
||||||
|
const grid = document.getElementById('video-grid');
|
||||||
|
if (!grid) return;
|
||||||
|
const contentHeight = grid.getBoundingClientRect().bottom;
|
||||||
|
if (contentHeight < window.innerHeight + 120) {
|
||||||
|
window.setTimeout(() => loadVideos(), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function renderMenu() {
|
function renderMenu() {
|
||||||
const session = getSession();
|
const session = getSession();
|
||||||
const serverEntries = getServerEntries();
|
const serverEntries = getServerEntries();
|
||||||
@@ -239,6 +261,7 @@ function renderMenu() {
|
|||||||
const sourcesList = document.getElementById('sources-list');
|
const sourcesList = document.getElementById('sources-list');
|
||||||
const addSourceBtn = document.getElementById('add-source-btn');
|
const addSourceBtn = document.getElementById('add-source-btn');
|
||||||
const sourceInput = document.getElementById('source-input');
|
const sourceInput = document.getElementById('source-input');
|
||||||
|
const reloadChannelBtn = document.getElementById('reload-channel-btn');
|
||||||
|
|
||||||
if (!sourceSelect || !channelSelect || !filtersContainer) return;
|
if (!sourceSelect || !channelSelect || !filtersContainer) return;
|
||||||
|
|
||||||
@@ -389,6 +412,12 @@ function renderMenu() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (reloadChannelBtn) {
|
||||||
|
reloadChannelBtn.onclick = () => {
|
||||||
|
resetAndReload();
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderFilters(container, session) {
|
function renderFilters(container, session) {
|
||||||
|
|||||||
BIN
frontend/favicon.ico
Normal file
BIN
frontend/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 523 B |
@@ -13,6 +13,9 @@
|
|||||||
<input type="text" id="search-input" placeholder="Search videos..." oninput="handleSearch(this.value)">
|
<input type="text" id="search-input" placeholder="Search videos..." oninput="handleSearch(this.value)">
|
||||||
</div>
|
</div>
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
|
<button class="icon-btn reload-toggle" id="reload-channel-btn" title="Reload Channel">
|
||||||
|
<img class="icon-svg" src="https://cdn.jsdelivr.net/npm/heroicons@2.0.13/24/outline/arrow-path.svg" alt="Reload">
|
||||||
|
</button>
|
||||||
<button class="icon-btn menu-toggle" onclick="toggleDrawer('menu')" title="Menu">
|
<button class="icon-btn menu-toggle" onclick="toggleDrawer('menu')" title="Menu">
|
||||||
<img class="icon-svg" src="https://cdn.jsdelivr.net/npm/heroicons@2.0.13/24/outline/bars-3.svg" alt="Menu">
|
<img class="icon-svg" src="https://cdn.jsdelivr.net/npm/heroicons@2.0.13/24/outline/bars-3.svg" alt="Menu">
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -327,6 +327,8 @@ body.theme-light .input-row input:focus {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-secondary:hover {
|
.btn-secondary:hover {
|
||||||
|
|||||||
Reference in New Issue
Block a user