Added loading more content

This commit is contained in:
WardPearce
2024-03-23 18:46:32 +13:00
parent f2aba0ef24
commit 3ccacf0528
7 changed files with 121 additions and 15 deletions
-1
View File
@@ -25,7 +25,6 @@ Modern material design for Invidious
- Add different search filters.
- Playlist support.
- History deletion.
- Load more for comments, history, subscriptions & search results.
- PWA support.
## Previews
+28 -2
View File
@@ -30,7 +30,19 @@ export async function getDislikes(videoId: string): Promise<ReturnYTDislikes> {
return await resp.json();
}
export async function getComments(videoId: string, parameters: { sort_by: "top" | "new", source: "youtube" | "reddit", continuation?: string; }): Promise<Comments> {
export async function getComments(videoId: string, parameters: {
sort_by?: "top" | "new",
source?: "youtube" | "reddit",
continuation?: string;
}): Promise<Comments> {
if (typeof parameters.sort_by === "undefined") {
parameters.sort_by = "top";
}
if (typeof parameters.source === "undefined") {
parameters.source = "youtube";
}
const path = new URL(buildPath(`comments/${videoId}`));
path.search = new URLSearchParams(parameters).toString();
const resp = await fetch(path);
@@ -50,8 +62,22 @@ export async function getSearchSuggestions(search: string): Promise<SearchSugges
}
export async function getSearch(search: string, options: {
sort_by: "relevance" | "rating" | "upload_date" | "view_count", type: "video" | "playlist" | "channel" | "all";
sort_by?: "relevance" | "rating" | "upload_date" | "view_count",
type?: "video" | "playlist" | "channel" | "all";
page?: string;
}): Promise<Video[]> {
if (typeof options.sort_by === "undefined") {
options.sort_by = "relevance";
}
if (typeof options.type === "undefined") {
options.type = "video";
}
if (typeof options.page === "undefined") {
options.page = "1";
}
const path = new URL(buildPath("search"));
path.search = new URLSearchParams({ ...options, q: search }).toString();
const resp = await fetch(path);
+22 -4
View File
@@ -2,7 +2,7 @@
import { getHistory, getVideo } from '$lib/Api';
import type { VideoPlay } from '$lib/Api/model';
import VideoList from '$lib/VideoList.svelte';
import { onMount } from 'svelte';
import { onDestroy, onMount } from 'svelte';
import { activePage } from '../../store';
activePage.set('history');
@@ -10,18 +10,36 @@
let history: VideoPlay[] = [];
let loaded = false;
async function loadPageHistory(page: number = 1) {
const videoIds = await getHistory(page);
let currentPage = 1;
async function loadPageHistory() {
const videoIds = await getHistory(currentPage);
let promises = [];
for (const videoId of videoIds) {
promises.push(getVideo(videoId));
}
history = [...history, ...(await Promise.all(promises))];
const loadedHistory = await Promise.all(promises);
history = [...history, ...loadedHistory];
}
async function handleScroll() {
const { scrollTop, clientHeight, scrollHeight } = document.documentElement;
if (scrollTop + clientHeight >= scrollHeight - 5) {
currentPage += 1;
await loadPageHistory();
}
}
onMount(async () => {
await loadPageHistory();
loaded = true;
window.addEventListener('scroll', handleScroll);
});
onDestroy(() => {
window.removeEventListener('scroll', handleScroll);
});
</script>
@@ -1,10 +1,32 @@
<script lang="ts">
import { getSearch } from '$lib/Api';
import VideoList from '$lib/VideoList.svelte';
import { onDestroy, onMount } from 'svelte';
import { activePage } from '../../../store';
export let data;
let currentPage = 1;
let search = data.search;
activePage.set(null);
async function handleScroll() {
const { scrollTop, clientHeight, scrollHeight } = document.documentElement;
if (scrollTop + clientHeight >= scrollHeight - 5) {
currentPage += 1;
search = [...search, ...(await getSearch(data.slug, { page: currentPage.toString() }))];
}
}
onMount(() => {
window.addEventListener('scroll', handleScroll);
});
onDestroy(() => {
window.removeEventListener('scroll', handleScroll);
});
</script>
<VideoList videos={data.search} />
<VideoList videos={search} />
@@ -2,6 +2,7 @@ import { getSearch } from '$lib/Api/index.js';
export async function load({ params }) {
return {
search: await getSearch(params.slug, { sort_by: "relevance", type: "video" })
search: await getSearch(params.slug, { type: "video" }),
slug: params.slug
};
}
@@ -1,10 +1,32 @@
<script lang="ts">
import { getFeed } from '$lib/Api/index.js';
import VideoList from '$lib/VideoList.svelte';
import { onDestroy, onMount } from 'svelte';
import { activePage } from '../../store.js';
export let data;
let currentPage = 1;
let videos = [...data.feed.videos, ...data.feed.notifications];
activePage.set('subscriptions');
async function handleScroll() {
const { scrollTop, clientHeight, scrollHeight } = document.documentElement;
if (scrollTop + clientHeight >= scrollHeight - 5) {
currentPage += 1;
const feed = await getFeed(100, currentPage);
videos = [...videos, ...feed.notifications];
}
}
onMount(() => {
window.addEventListener('scroll', handleScroll);
});
onDestroy(() => {
window.removeEventListener('scroll', handleScroll);
});
</script>
<VideoList videos={[...data.feed.videos, ...data.feed.notifications]} />
<VideoList bind:videos />
@@ -1,5 +1,5 @@
<script lang="ts">
import { deleteUnsubscribe, postSubscribe } from '$lib/Api/index.js';
import { deleteUnsubscribe, getComments, postSubscribe } from '$lib/Api/index.js';
import Comment from '$lib/Comment.svelte';
import PageLoading from '$lib/PageLoading.svelte';
import Player from '$lib/Player.svelte';
@@ -10,8 +10,24 @@
export let data;
let comments = data.comments;
activePage.set(null);
async function loadMoreComments() {
if (!comments) {
return;
}
const loadedComments = await getComments(data.video.videoId, {
continuation: comments?.continuation
});
comments.continuation = loadedComments.continuation;
comments.comments = [...comments.comments, ...loadedComments.comments];
}
async function toggleSubscribed() {
if (data.subscribed) {
await deleteUnsubscribe(data.video.authorId);
@@ -147,12 +163,14 @@
</article>
<div class="space"></div>
{#if data.comments && data.comments.comments.length > 0}
<h6>{numberWithCommas(data.comments.commentCount)} comments</h6>
{#each data.comments.comments as comment}
{#if comments && comments.comments.length > 0}
<h6>{numberWithCommas(comments.commentCount)} comments</h6>
{#each comments.comments as comment}
<Comment {comment} videoId={data.video.videoId}></Comment>
{/each}
<button class="margin">Load more</button>
{#if comments.continuation}
<button on:click={loadMoreComments} class="margin">Load more</button>
{/if}
{:else}
<h6>Comments disabled</h6>
{/if}