aboutsummaryrefslogtreecommitdiff
path: root/src/components/YouTubeVideos.svelte
diff options
context:
space:
mode:
authorAndrew Lee <alee14498@protonmail.com>2024-07-07 12:09:08 -0400
committerAndrew Lee <alee14498@protonmail.com>2024-07-07 12:09:08 -0400
commit7205caf6afb58bee230a04a5830842ea6e5b10ff (patch)
treec3a3eaeb0b1907be6d7ab909793b87fa0090cba4 /src/components/YouTubeVideos.svelte
parentb659ac111c8c3ea16be0d856b095cb6a85c2beef (diff)
downloadpersonal-website-7205caf6afb58bee230a04a5830842ea6e5b10ff.tar.gz
personal-website-7205caf6afb58bee230a04a5830842ea6e5b10ff.tar.bz2
personal-website-7205caf6afb58bee230a04a5830842ea6e5b10ff.zip
Added videos page
Diffstat (limited to 'src/components/YouTubeVideos.svelte')
-rw-r--r--src/components/YouTubeVideos.svelte133
1 files changed, 133 insertions, 0 deletions
diff --git a/src/components/YouTubeVideos.svelte b/src/components/YouTubeVideos.svelte
new file mode 100644
index 0000000..677e502
--- /dev/null
+++ b/src/components/YouTubeVideos.svelte
@@ -0,0 +1,133 @@
+<script>
+ import { onMount, onDestroy } from 'svelte';
+ import Loader from './Loader.svelte';
+ import { formatDate } from "../util";
+
+ let error = null;
+ let isLoading = true;
+ let videos = [];
+ let nextPageToken = '';
+
+ const fetchVideos = async (pageToken = '') => {
+ isLoading = true;
+ const url = `${import.meta.env.PUBLIC_API_URL}/youtube?pageToken=${pageToken}`;
+ try {
+ const response = await fetch(url);
+ if (!response.ok) {
+ console.error('HTTP Error: ' + response.statusText);
+ error = 'HTTP Error: ' + response.statusText;
+ return;
+ }
+ const data = await response.json();
+ videos = [...videos, ...data.items];
+ nextPageToken = data.nextPageToken || '';
+ } catch (e) {
+ console.error(e);
+ error = e.message;
+ } finally {
+ isLoading = false;
+ }
+ };
+
+ function onScroll() {
+ const nearBottom = window.innerHeight + window.scrollY >= document.body.offsetHeight - 2;
+ if (nearBottom && !isLoading && nextPageToken) {
+ fetchVideos(nextPageToken);
+ }
+ }
+
+ onMount(() => {
+ if (typeof window !== 'undefined') {
+ window.addEventListener('scroll', onScroll);
+ fetchVideos().then(() => isLoading = false);
+ }
+ });
+
+ onDestroy(() => {
+ if (typeof window !== 'undefined') {
+ window.removeEventListener('scroll', onScroll);
+ }
+ });
+</script>
+
+<style>
+ .title {
+ margin-top: .2em;
+ margin-bottom: .2em;
+ }
+
+ img {
+ width: 100%;
+ height: auto;
+ border-radius: 1em;
+ object-fit: cover;
+ }
+
+ .card small {
+ display: block;
+ margin-bottom: 1em;
+ }
+
+ .zoom {
+ transition: transform 0.2s ease; /* Smooth transition for the transform property */
+ display: block; /* Ensures the image is block-level for proper scaling */
+ margin: auto; /* Keeps the image centered */
+ max-width: 100%; /* Ensures the image does not exceed its container's width */
+ }
+
+ .zoom:hover {
+ transform: scale(1.1); /* Scales the image to 110% of its original size on hover */
+ }
+
+ .container {
+ display: grid;
+ gap: 1em; /* Adjusts the gap between grid items */
+ }
+
+ @media (min-width: 992px) {
+ .container {
+ grid-template-columns: repeat(3, 1fr); /* Original setting for large screens */
+ }
+ }
+
+ @media (max-width: 991px) and (min-width: 768px) {
+ .container {
+ grid-template-columns: repeat(2, 1fr); /* Adjusts to 2 columns for tablets */
+ }
+ }
+
+ @media (max-width: 767px) {
+ .container {
+ grid-template-columns: 1fr; /* Adjusts to a single column for mobile */
+ }
+ }
+
+</style>
+
+<div>
+ <div>
+ {#if isLoading && videos.length === 0}
+ <Loader />
+ {:else}
+ {#if error}
+ <div class="error">{error}</div>
+ {:else}
+ <div class="container">
+ {#each videos as video}
+ <div class="card">
+ <a href={`https://youtu.be/${video.snippet.resourceId.videoId}`} target="_blank">
+ <h3 class="title">{video.snippet.title}</h3>
+ <small>{formatDate(video.snippet.publishedAt)}</small>
+ <img src={video.snippet.thumbnails.medium.url} alt={video.snippet.title} class="zoom" />
+ </a>
+ </div>
+ {/each}
+ </div>
+ <div class="load-more"></div>
+ {/if}
+ {/if}
+ {#if isLoading && videos.length > 0}
+ <Loader />
+ {/if}
+ </div>
+</div>