Added loading more content
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user