Compare commits
4 Commits
9711cd163f
...
8ed804a922
Author | SHA1 | Date | |
---|---|---|---|
8ed804a922 | |||
02a352a122 | |||
e8e864bdfc | |||
2eddb77b74 |
@ -1,4 +1,4 @@
|
||||
HASHTAG_FILTER = ichlausche,music,musik,nowplaying,tunetuesday
|
||||
HASHTAG_FILTER = ichlausche,music,musik,nowplaying,tunetuesday,nowlistening
|
||||
URL_FILTER = song.link,album.link,spotify.com,music.apple.com,bandcamp.com
|
||||
YOUTUBE_API_KEY = CHANGE_ME
|
||||
VERBOSE = false
|
||||
|
@ -26,7 +26,7 @@
|
||||
gap: 10px;
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
background-color: #54546788;
|
||||
background-color: #bbbbcd73;
|
||||
padding: 0.3em 1em;
|
||||
margin: 0 -8px;
|
||||
border-radius: 3px;
|
||||
|
40
src/lib/components/LoadMoreComponent.svelte
Normal file
40
src/lib/components/LoadMoreComponent.svelte
Normal file
@ -0,0 +1,40 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
export let moreAvailable: boolean = false;
|
||||
export let isLoading: boolean = false;
|
||||
let displayText = '';
|
||||
let title = '';
|
||||
let disabled: boolean;
|
||||
|
||||
$: displayText = isLoading ? 'Loading...' : 'Load More';
|
||||
$: disabled = !moreAvailable || isLoading;
|
||||
$: title = moreAvailable ? 'Load More' : 'No more posts available';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
function loadOlderPosts() {
|
||||
dispatch('loadOlderPosts');
|
||||
}
|
||||
</script>
|
||||
|
||||
<button on:click={loadOlderPosts} {disabled} {title}>{displayText}</button>
|
||||
|
||||
<style>
|
||||
button {
|
||||
padding: 0.75em;
|
||||
border-radius: 3px;
|
||||
border: none;
|
||||
background-color: var(--color-link);
|
||||
color: white;
|
||||
cursor: grab;
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
button:hover:not(:disabled) {
|
||||
background-color: var(--color-link-visited);
|
||||
}
|
||||
button:disabled {
|
||||
cursor: not-allowed;
|
||||
background-color: grey;
|
||||
}
|
||||
</style>
|
@ -172,15 +172,19 @@ export function savePost(post: Post): void {
|
||||
});
|
||||
}
|
||||
|
||||
export async function getPosts(since: string | null, limit: number) {
|
||||
export async function getPosts(since: string | null, before: string | null, limit: number) {
|
||||
let promise = await new Promise<Post[]>((resolve, reject) => {
|
||||
let filter_query;
|
||||
let params: any = { $limit: limit };
|
||||
if (since === null) {
|
||||
if (since === null && before === null) {
|
||||
filter_query = '';
|
||||
} else {
|
||||
} else if (since !== null) {
|
||||
filter_query = 'WHERE posts.created_at > $since';
|
||||
params.$since = since;
|
||||
} else if (before !== null) {
|
||||
// Setting both, before and since doesn't make sense, so this case is not explicitly handled
|
||||
filter_query = 'WHERE posts.created_at < $before';
|
||||
params.$before = before;
|
||||
}
|
||||
const sql = `SELECT posts.id, posts.content, posts.created_at, posts.url,
|
||||
accounts.id AS account_id, accounts.acct, accounts.username, accounts.display_name,
|
||||
|
@ -3,7 +3,7 @@ import type { Post, Tag, TimelineEvent } from '$lib/mastodon/response';
|
||||
import { savePost } from '$lib/server/db';
|
||||
import { WebSocket } from "ws";
|
||||
|
||||
const YOUTUBE_REGEX = new RegExp(/https?:\/\/(www\.)?youtu((be.com\/.*v=)|(\.be\/))(?<videoId>[a-zA-Z_0-9-]+)/gm);
|
||||
const YOUTUBE_REGEX = new RegExp(/https?:\/\/(www\.)?youtu((be.com\/.*?v=)|(\.be\/))(?<videoId>[a-zA-Z_0-9-]+)/gm);
|
||||
|
||||
export class TimelineReader {
|
||||
private static _instance: TimelineReader;
|
||||
|
@ -4,10 +4,13 @@ import type { PageData } from './$types';
|
||||
import type { Post } from '$lib/mastodon/response';
|
||||
import { PUBLIC_REFRESH_INTERVAL } from '$env/static/public';
|
||||
import PostComponent from '$lib/components/PostComponent.svelte';
|
||||
import LoadMoreComponent from '$lib/components/LoadMoreComponent.svelte';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
let interval: NodeJS.Timer | null = null;
|
||||
let moreOlderPostsAvailable = true;
|
||||
let loadingOlderPosts = false;
|
||||
|
||||
onMount(async () => {
|
||||
interval = setInterval(async () => {
|
||||
@ -19,8 +22,13 @@ onMount(async () => {
|
||||
.then(r => r.json())
|
||||
.then((resp: Post[]) => {
|
||||
if (resp.length > 0) {
|
||||
data.posts = resp.concat(data.posts);
|
||||
console.log('updated data', resp, data.posts);
|
||||
// Prepend new posts, filter dupes
|
||||
// There shouldn't be any duplicates, but better be safe than sorry
|
||||
const combined = resp.concat(data.posts);
|
||||
const filteredPosts = combined.filter((obj, index, arr) => {
|
||||
return arr.map(mapObj => mapObj.url).indexOf(obj.url) === index;
|
||||
});
|
||||
data.posts = filteredPosts;
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
@ -33,7 +41,39 @@ onMount(async () => {
|
||||
clearInterval(interval)
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
async function loadOlderPosts() {
|
||||
loadingOlderPosts = true;
|
||||
const params = new URLSearchParams();
|
||||
if (data.posts.length > 0) {
|
||||
params.set('before', data.posts[data.posts.length - 1].created_at);
|
||||
}
|
||||
await fetch(`/api/posts?${params}`)
|
||||
.then(r => r.json())
|
||||
.then((resp: Post[]) => {
|
||||
if (resp.length > 0) {
|
||||
// Append old posts, filter dupes
|
||||
// There shouldn't be any duplicates, but better be safe than sorry
|
||||
const combined = data.posts.concat(resp);
|
||||
const filteredPosts = combined.filter((obj, index, arr) => {
|
||||
return arr.map(mapObj => mapObj.url).indexOf(obj.url) === index;
|
||||
});
|
||||
data.posts = filteredPosts;
|
||||
moreOlderPostsAvailable = true;
|
||||
} else {
|
||||
moreOlderPostsAvailable = false;
|
||||
}
|
||||
loadingOlderPosts = false;
|
||||
})
|
||||
.catch(e => {
|
||||
loadingOlderPosts = false;
|
||||
// TODO: Show error in UI
|
||||
console.error('Error loading older posts', e);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
</script>
|
||||
<svelte:head>
|
||||
<title>Metalhead.club music list</title>
|
||||
@ -45,9 +85,13 @@ onMount(async () => {
|
||||
{#if data.posts.length === 0}
|
||||
Sorry, no posts recommending music aave been found yet
|
||||
{/if}
|
||||
{#each data.posts as post (post.id)}
|
||||
{#each data.posts as post (post.url)}
|
||||
<div class="post"><PostComponent {post} /></div>
|
||||
{/each}
|
||||
<LoadMoreComponent
|
||||
on:loadOlderPosts={loadOlderPosts}
|
||||
moreAvailable={moreOlderPostsAvailable}
|
||||
isLoading={loadingOlderPosts}/>
|
||||
</div>
|
||||
<div></div>
|
||||
</div>
|
||||
@ -55,6 +99,7 @@ onMount(async () => {
|
||||
.posts {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
.post {
|
||||
width: 100%;
|
||||
|
@ -4,12 +4,13 @@ import { json } from '@sveltejs/kit';
|
||||
import type { RequestHandler } from './$types';
|
||||
|
||||
export const GET = (async ({ url }) => {
|
||||
const since = url.searchParams.get('since');
|
||||
let count = Number.parseInt(url.searchParams.get('count') || '');
|
||||
if (isNaN(count)) {
|
||||
count = 20;
|
||||
}
|
||||
count = Math.min(count, 100);
|
||||
const posts = await getPosts(since, count);
|
||||
return json(posts);
|
||||
const since = url.searchParams.get('since');
|
||||
const before = url.searchParams.get('before');
|
||||
let count = Number.parseInt(url.searchParams.get('count') || '');
|
||||
if (isNaN(count)) {
|
||||
count = 20;
|
||||
}
|
||||
count = Math.min(count, 100);
|
||||
const posts = await getPosts(since, before, count);
|
||||
return json(posts);
|
||||
}) satisfies RequestHandler;
|
Loading…
Reference in New Issue
Block a user