aboutsummaryrefslogtreecommitdiff
path: root/src/components/YouTubeVideos.svelte
blob: a6c1c1820204716997fff067763a452c5f8d8e58 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
<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);
            const data = await response.json();
            if (!response.ok) {
                console.error(`HTTP Error ${data.error.code}: ${data.error.message}`);
                error = `HTTP Error ${data.error.code}: ${data.error.message}`
                return
            }
            videos = [...videos, ...data.items];
            nextPageToken = data.nextPageToken || '';
        } catch (e) {
            console.error(e);
            error = e.error.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: .5em;
        margin-bottom: .3em;
    }

    img {
        width: 100%;
        height: auto;
        border-radius: 1em;
    }

    .card small {
        display: block;
    }

    .zoom {
        transition: transform 0.2s ease; /* Smooth transition for the transform property */
        max-width: 100%; /* Ensures the image does not exceed its container's width */
    }

    .zoom:hover {
        transform: scale(1.04); /* 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>
    {#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">
                            <img src={video.snippet.thumbnails.medium.url} class="zoom" loading="lazy" alt={video.snippet.title} width={video.snippet.thumbnails.medium.width} height={video.snippet.thumbnails.medium.height} />
                            <h3 class="title">{video.snippet.title}</h3>
                            <small>{formatDate(video.snippet.publishedAt)}</small>
                        </a>
                    </div>
                {/each}
            </div>
        {/if}
    {/if}
    {#if isLoading && videos.length > 0}
        <Loader />
    {/if}
</div>