Improve layout for smaller devices

This commit is contained in:
Max Nuding 2023-04-11 15:49:50 +02:00
parent e3c15be31c
commit 268128c2f4
Signed by: phlaym
GPG Key ID: A06651BAB6777237
3 changed files with 217 additions and 168 deletions

View File

@ -5,21 +5,25 @@
<div class="footer"> <div class="footer">
<div> <div>
<span>Made with &#x1F918; by&nbsp;</span> <span class="label"
<a href="https://metalhead.club/@aymm" rel="me">@aymm@metalhead.club</a> >Made<span class="secretIngredient">&nbsp;with &#x1F918;</span>&nbsp;by&nbsp;</span
>
<a href="https://metalhead.club/@aymm" rel="me"
>@aymm<span class="mastodonInstance">@metalhead.club</span></a
>
</div> </div>
| |
<div> <div>
<a href="https://phlaym.net/git/phlaym/moshing-mammut"> <a href="https://phlaym.net/git/phlaym/moshing-mammut">
<img alt="Git branch" src={git} class="icon" /> <img alt="Git branch" src={git} class="icon" />
Source Code <span class="label">Source Code</span>
</a> </a>
</div> </div>
| |
<div> <div>
<a href="/feed.xml"> <a href="/feed.xml">
<img alt="RSS" src={rss} class="icon" /> <img alt="RSS" src={rss} class="icon" />
RSS Feed <span class="label">RSS<span class="feedSuffix">&nbsp;Feed</span></span>
</a> </a>
</div> </div>
</div> </div>
@ -53,4 +57,27 @@
background-color: var(--color-grey-translucent); background-color: var(--color-grey-translucent);
} }
} }
@media only screen and (max-device-width: 620px) {
.mastodonInstance,
.feedSuffix {
display: none;
}
.footer {
justify-content: center;
}
}
@media only screen and (max-device-width: 430px) {
.mastodonInstance,
.feedSuffix,
.secretIngredient {
display: none;
}
}
@media only screen and (max-device-width: 370px) {
.label {
display: none;
}
}
</style> </style>

View File

@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import FooterComponent from '$lib/components/FooterComponent.svelte' import FooterComponent from '$lib/components/FooterComponent.svelte';
import { SvelteToast } from '@zerodevx/svelte-toast'; import { SvelteToast } from '@zerodevx/svelte-toast';
const options = { const options = {
@ -7,6 +7,7 @@
classes: ['toast'] classes: ['toast']
}; };
</script> </script>
<slot /> <slot />
<SvelteToast {options} /> <SvelteToast {options} />
<div class="footer"> <div class="footer">
@ -33,4 +34,9 @@
align-items: center; align-items: center;
gap: 10px; gap: 10px;
} }
@media only screen and (max-device-width: 620px) {
.footer {
width: calc(100% + 16px);
}
}
</style> </style>

View File

@ -1,45 +1,48 @@
<script lang="ts"> <script lang="ts">
import { onMount } from "svelte"; import { onMount } from 'svelte';
import type { PageData } from './$types'; import type { PageData } from './$types';
import type { Post } from '$lib/mastodon/response'; import type { Post } from '$lib/mastodon/response';
import { PUBLIC_REFRESH_INTERVAL, PUBLIC_MASTODON_INSTANCE_DISPLAY_NAME } from '$env/static/public'; import {
import PostComponent from '$lib/components/PostComponent.svelte'; PUBLIC_REFRESH_INTERVAL,
import LoadMoreComponent from '$lib/components/LoadMoreComponent.svelte'; PUBLIC_MASTODON_INSTANCE_DISPLAY_NAME
import { fly, type FlyParams } from 'svelte/transition'; } from '$env/static/public';
import { cubicInOut } from 'svelte/easing'; import PostComponent from '$lib/components/PostComponent.svelte';
import { errorToast } from '$lib/errorToast' import LoadMoreComponent from '$lib/components/LoadMoreComponent.svelte';
import { fly, type FlyParams } from 'svelte/transition';
import { cubicInOut } from 'svelte/easing';
import { errorToast } from '$lib/errorToast';
export let data: PageData;
export let data: PageData; interface FetchOptions {
since?: string;
before?: string;
count?: number;
}
interface FetchOptions { interface EdgeFlyParams extends FlyParams {
since?: string, created_at: string;
before?: string, }
count?: number
}
interface EdgeFlyParams extends FlyParams { const refreshInterval = parseInt(PUBLIC_REFRESH_INTERVAL);
created_at: string let interval: NodeJS.Timer | null = null;
} let moreOlderPostsAvailable = true;
let loadingOlderPosts = false;
const refreshInterval = parseInt(PUBLIC_REFRESH_INTERVAL); // Needed, so that edgeFly() can do its thing:
let interval: NodeJS.Timer | null = null; // To determine whether a newly loaded post is older than the existing ones, is required to know what the oldest
let moreOlderPostsAvailable = true; // post was, before the fetch happened.
let loadingOlderPosts = false; let oldestBeforeLastFetch: number | null = null;
// Needed, so that edgeFly() can do its thing: /**
// To determine whether a newly loaded post is older than the existing ones, is required to know what the oldest
// post was, before the fetch happened.
let oldestBeforeLastFetch: number | null = null;
/**
* Animate either from the top, or the bottom of the window, depending if the post is * Animate either from the top, or the bottom of the window, depending if the post is
* newer than the existing ones or older. * newer than the existing ones or older.
*/ */
function edgeFly(node: Element, opts: EdgeFlyParams) { function edgeFly(node: Element, opts: EdgeFlyParams) {
const createdAt = new Date(opts.created_at).getTime(); const createdAt = new Date(opts.created_at).getTime();
const diffNewest = Math.abs(new Date(data.posts[0].created_at).getTime() - createdAt); const diffNewest = Math.abs(new Date(data.posts[0].created_at).getTime() - createdAt);
const oldest = oldestBeforeLastFetch !== null const oldest =
oldestBeforeLastFetch !== null
? oldestBeforeLastFetch ? oldestBeforeLastFetch
: new Date(data.posts[data.posts.length - 1].created_at).getTime(); : new Date(data.posts[data.posts.length - 1].created_at).getTime();
const diffOldest = Math.abs(oldest - createdAt); const diffOldest = Math.abs(oldest - createdAt);
@ -50,9 +53,9 @@ function edgeFly(node: Element, opts: EdgeFlyParams) {
let offset = isNaN(paramY) ? 0 : paramY + rect.height; let offset = isNaN(paramY) ? 0 : paramY + rect.height;
opts.y = fromTop ? -offset : window.innerHeight + offset; opts.y = fromTop ? -offset : window.innerHeight + offset;
return fly(node, opts); return fly(node, opts);
} }
async function fetchPosts(options: FetchOptions): Promise<Post[]> { async function fetchPosts(options: FetchOptions): Promise<Post[]> {
const params = new URLSearchParams(); const params = new URLSearchParams();
if (options?.since !== undefined) { if (options?.since !== undefined) {
params.set('since', options.since); params.set('since', options.since);
@ -66,20 +69,21 @@ async function fetchPosts(options: FetchOptions): Promise<Post[]> {
const response = await fetch(`/api/posts?${params}`); const response = await fetch(`/api/posts?${params}`);
return await response.json(); return await response.json();
} }
function filterDuplicates(posts: Post[]): Post[] { function filterDuplicates(posts: Post[]): Post[] {
return posts.filter((obj, index, arr) => { return posts.filter((obj, index, arr) => {
return arr.map(mapObj => mapObj.url).indexOf(obj.url) === index; return arr.map((mapObj) => mapObj.url).indexOf(obj.url) === index;
}); });
} }
function refresh() { function refresh() {
let filter: FetchOptions = {}; let filter: FetchOptions = {};
if (data.posts.length > 0) { if (data.posts.length > 0) {
filter = { since: data.posts[0].created_at }; filter = { since: data.posts[0].created_at };
} }
fetchPosts(filter).then(resp => { fetchPosts(filter)
.then((resp) => {
if (resp.length > 0) { if (resp.length > 0) {
// Prepend new posts, filter dupes // Prepend new posts, filter dupes
// There shouldn't be any duplicates, but better be safe than sorry // There shouldn't be any duplicates, but better be safe than sorry
@ -89,9 +93,9 @@ function refresh() {
.catch((e: Error) => { .catch((e: Error) => {
errorToast('Error loading newest posts: ' + e.message); errorToast('Error loading newest posts: ' + e.message);
}); });
} }
onMount(async () => { onMount(async () => {
if (data.posts.length > 0) { if (data.posts.length > 0) {
oldestBeforeLastFetch = new Date(data.posts[data.posts.length - 1].created_at).getTime(); oldestBeforeLastFetch = new Date(data.posts[data.posts.length - 1].created_at).getTime();
} }
@ -109,12 +113,12 @@ onMount(async () => {
return () => { return () => {
if (interval !== null) { if (interval !== null) {
clearInterval(interval) clearInterval(interval);
} }
} };
}); });
function loadOlderPosts() { function loadOlderPosts() {
loadingOlderPosts = true; loadingOlderPosts = true;
const filter: FetchOptions = { count: 20 }; const filter: FetchOptions = { count: 20 };
if (data.posts.length > 0) { if (data.posts.length > 0) {
@ -123,8 +127,8 @@ function loadOlderPosts() {
oldestBeforeLastFetch = new Date(before).getTime(); oldestBeforeLastFetch = new Date(before).getTime();
} }
fetchPosts(filter)
fetchPosts(filter).then(resp => { .then((resp) => {
if (resp.length > 0) { if (resp.length > 0) {
// Append old posts, filter dupes // Append old posts, filter dupes
// There shouldn't be any duplicates, but better be safe than sorry // There shouldn't be any duplicates, but better be safe than sorry
@ -136,20 +140,19 @@ function loadOlderPosts() {
} }
loadingOlderPosts = false; loadingOlderPosts = false;
}) })
.catch(e => { .catch((e) => {
loadingOlderPosts = false; loadingOlderPosts = false;
errorToast('Error loading older posts: ' + e.message); errorToast('Error loading older posts: ' + e.message);
}); });
}
}
</script> </script>
<svelte:head> <svelte:head>
<title>{PUBLIC_MASTODON_INSTANCE_DISPLAY_NAME} music list</title> <title>{PUBLIC_MASTODON_INSTANCE_DISPLAY_NAME} music list</title>
</svelte:head> </svelte:head>
<h2>{PUBLIC_MASTODON_INSTANCE_DISPLAY_NAME} music list</h2> <h2>{PUBLIC_MASTODON_INSTANCE_DISPLAY_NAME} music list</h2>
<div class="wrapper"> <div class="wrapper">
<div></div> <div />
<div class="posts"> <div class="posts">
{#if data.posts.length === 0} {#if data.posts.length === 0}
Sorry, no posts recommending music aave been found yet Sorry, no posts recommending music aave been found yet
@ -157,7 +160,12 @@ function loadOlderPosts() {
{#each data.posts as post (post.url)} {#each data.posts as post (post.url)}
<div <div
class="post" class="post"
transition:edgeFly="{{ y: 10, created_at: post.created_at, duration: 300, easing: cubicInOut }}" transition:edgeFly={{
y: 10,
created_at: post.created_at,
duration: 300,
easing: cubicInOut
}}
> >
<PostComponent {post} /> <PostComponent {post} />
</div> </div>
@ -165,10 +173,12 @@ function loadOlderPosts() {
<LoadMoreComponent <LoadMoreComponent
on:loadOlderPosts={loadOlderPosts} on:loadOlderPosts={loadOlderPosts}
moreAvailable={moreOlderPostsAvailable} moreAvailable={moreOlderPostsAvailable}
isLoading={loadingOlderPosts}/> isLoading={loadingOlderPosts}
</div> />
<div></div>
</div> </div>
<div />
</div>
<style> <style>
.posts { .posts {
display: flex; display: flex;
@ -191,4 +201,10 @@ function loadOlderPosts() {
text-align: center; text-align: center;
z-index: 100; z-index: 100;
} }
@media only screen and (max-device-width: 650px) {
.post {
max-width: 100vw;
}
}
</style> </style>