Added guestbook
continuous-integration/drone/push Build is passing Details

This commit is contained in:
DutchEllie 2022-07-12 17:05:51 +02:00
parent 2cbc24f293
commit 5997105fed
Signed by: DutchEllie
SSH Key Fingerprint: SHA256:dKq6ZSgN5E3Viqrw/+xAdf2VdR6hdRGNyrYqXXwfjTY
10 changed files with 331 additions and 60 deletions

21
package-lock.json generated
View File

@ -8,6 +8,7 @@
"name": "svelte-testing",
"version": "0.0.1",
"dependencies": {
"js-sha256": "^0.9.0",
"svelte-gestures": "^1.4.1",
"svelte-language-server": "^0.14.29"
},
@ -724,6 +725,11 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"node_modules/Base64": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/Base64/-/Base64-1.1.0.tgz",
"integrity": "sha512-qeacf8dvGpf+XAT27ESHMh7z84uRzj/ua2pQdJg483m3bEXv/kVFtDnMgvf70BQGqzbZhR9t6BmASzKvqfJf3Q=="
},
"node_modules/binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
@ -2111,6 +2117,11 @@
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
"dev": true
},
"node_modules/js-sha256": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz",
"integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA=="
},
"node_modules/js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
@ -4286,6 +4297,11 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"Base64": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/Base64/-/Base64-1.1.0.tgz",
"integrity": "sha512-qeacf8dvGpf+XAT27ESHMh7z84uRzj/ua2pQdJg483m3bEXv/kVFtDnMgvf70BQGqzbZhR9t6BmASzKvqfJf3Q=="
},
"binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
@ -5222,6 +5238,11 @@
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
"dev": true
},
"js-sha256": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz",
"integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA=="
},
"js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",

View File

@ -36,6 +36,7 @@
},
"type": "module",
"dependencies": {
"js-sha256": "^0.9.0",
"svelte-gestures": "^1.4.1",
"svelte-language-server": "^0.14.29"
}

View File

@ -1,63 +1,78 @@
<script>
import Guestbook from "../misc/guestbook.svelte";
</script>
<h1
class="font-roboto_slab text-center text-2xl lg:text-6xl xl:text-7xl w-auto md:w-max mt-10 lg:mt-32 mx-auto font-bold h1-shadow"
>Welcome to my website</h1>
<h1
class="font-roboto_slab text-center text-2xl lg:text-6xl xl:text-7xl w-auto md:w-max mt-10 lg:mt-32 mx-auto font-bold h1-shadow"
>
Welcome to my website
</h1>
<!-->
<br class="hidden lg:block" />
</!-->
<p class="font-roboto_slab text-center lg:text-2xl text-md lg:mt-5">The modern web meets 2000</p>
<hr class="border-black dark:border-[#ffc0f5]" />
<br />
<div class="md:text-center lg:px-40">
<p class="">
Welcome! This is my personal website that I made as a hobby project. You are entirely welcome!
I was inspired to make this page thanks to a couple of friends of mine, whose pages you can
check out under the "Galaxies" page.
</p>
</div>
<div class="flex gap-3 mt-2 md:mt-0 lg:px-20 xl:px-40 xl:mt-10">
<img
class="h-40 md:h-60 rounded "
src="https://dutchellie.nl/DutchEllie/proper-website-2/raw/branch/main/web/static/images/rin-2.gif"
alt="Kagamine Rin excitedly jumping! yay!"
/>
<div class="w-full grid md:grid-cols-2 gap-2">
<div class="col-span-1">
<h2 class="font-semibold text-left ml-10 mb-2">Tech</h2>
<ul class="ml-10 list-disc marker:text-blue-400">
<li>Kubernetes</li>
<li>Svelte</li>
<li>SvelteKit</li>
<li>Lot's of Japanese music</li>
</ul>
</div>
<div class="col-span-1">
<h2 class="font-semibold text-left ml-10 mb-2">Misc</h2>
<ul class="ml-10 list-disc marker:text-blue-400">
<li class="underline text-[#002896] dark:text-blue-200 dark:hover:text-blue-50 hover:text-blue-500">
<a href="https://dutchellie.nl/DutchEllie/svelte-website">Gitea Repo</a>
</li>
<li class="underline text-[#002896] dark:text-blue-200 dark:hover:text-blue-50 hover:text-blue-500">
<a href="https://drone.dutchellie.nl/DutchEllie/svelte-website"
>Drone CI/CD
<img
class="ml-2 mb-1 inline"
src="https://drone.dutchellie.nl/api/badges/DutchEllie/svelte-website/status.svg?ref=refs/heads/main"
alt="Drone build status"
/>
</a>
</li>
</ul>
<br />
<p>
This entire project is open source (GPL-3) and the source code is linked above. In
addition, a CI/CD pipeline deploys this website on every push.
</p>
</div>
<p class="font-roboto_slab text-center lg:text-2xl text-md lg:mt-5">The modern web meets 2000</p>
<hr class="border-black dark:border-[#ffc0f5]" />
<br />
<div class="md:text-center lg:px-40">
<p class="">
Welcome! This is my personal website that I made as a hobby project. You are entirely welcome! I
was inspired to make this page thanks to a couple of friends of mine, whose pages you can check
out under the "Galaxies" page.
</p>
</div>
<div class="flex gap-3 mt-2 md:mt-0 lg:px-20 xl:px-40 xl:mt-10">
<img
class="h-40 md:h-60 rounded "
src="https://dutchellie.nl/DutchEllie/proper-website-2/raw/branch/main/web/static/images/rin-2.gif"
alt="Kagamine Rin excitedly jumping! yay!"
/>
<div class="w-full grid md:grid-cols-2 gap-2">
<div class="col-span-1">
<h2 class="font-semibold text-left ml-10 mb-2">Tech</h2>
<ul class="ml-10 list-disc marker:text-blue-400">
<li>Kubernetes</li>
<li>Svelte</li>
<li>SvelteKit</li>
<li>Lot's of Japanese music</li>
</ul>
</div>
<div class="col-span-1">
<h2 class="font-semibold text-left ml-10 mb-2">Misc</h2>
<ul class="ml-10 list-disc marker:text-blue-400">
<li
class="underline text-[#002896] dark:text-blue-200 dark:hover:text-blue-50 hover:text-blue-500"
>
<a href="https://dutchellie.nl/DutchEllie/svelte-website">Gitea Repo</a>
</li>
<li
class="underline text-[#002896] dark:text-blue-200 dark:hover:text-blue-50 hover:text-blue-500"
>
<a href="https://drone.dutchellie.nl/DutchEllie/svelte-website"
>Drone CI/CD
<img
class="ml-2 mb-1 inline"
src="https://drone.dutchellie.nl/api/badges/DutchEllie/svelte-website/status.svg?ref=refs/heads/main"
alt="Drone build status"
/>
</a>
</li>
</ul>
</div>
</div>
</div>
<div class="grid md:grid-cols-2 md:gap-2 mt-3">
<div>
<h2 class="font-semibold">Details</h2>
<p>
This entire project is open source (GPL-3) and the source code is linked above. In addition, a
CI/CD pipeline deploys this website on every push.
</p>
</div>
</div>
<div class="mt-6">
<Guestbook/>
</div>
<!-- How to do the animation and reverse it: https://stackoverflow.com/a/44742399 -->
<style>

View File

@ -0,0 +1,32 @@
<script>
export let show = false;
export let title = 'Error';
export let message = 'Error';
function onClose() {
show = false;
}
</script>
{#if show}
<div
on:click={() => {
onClose();
}}
class="h-screen w-screen bg-opacity-60 bg-black fixed top-0 left-0 z-10 py-20 "
>
<div
on:click|stopPropagation
class="h-full w-3/4 bg-red-500 dark:bg-red-800 dark:text-slate-200 mx-auto rounded-xl pb-3 overflow-y-auto"
>
<div class="bg-zinc-800 font-bold text-white dark:bg-slate-700 rounded-t-xl p-3">
<span>{title}</span>
</div>
<div class="p-3">
<p class="text-white">
{message}
</p>
</div>
</div>
</div>
{/if}

View File

@ -1,3 +1,180 @@
<div class="">
<script lang="ts" context="module">
export type Comment = {
id: string;
name: string;
email: string;
website: string;
message: string;
time: string;
uuid: string;
};
</script>
</div>
<script lang="ts">
import Errormodal from './errormodal.svelte';
import { onMount } from 'svelte';
import { sha256 } from 'js-sha256';
import GuestbookEntry from './guestbook/guestbook-entry.svelte';
import { page } from '$app/stores';
import { comment } from 'postcss';
let showError = false;
let errorTitle = 'Error';
let errorMessage = 'Error message';
let name = '';
let email = '';
let website = '';
let message = '';
let DisplayComments: Comment[];
let commentPage = 0;
let pageSize = 10;
async function onSubmit(event: SubmitEvent) {
// Validation
if (name === '' || message === '') {
errorMessage = 'You must enter a name and a message!';
showError = true;
return;
}
if (
name.length >= 40 ||
email.length >= 100 ||
website.length >= 200 ||
message.length >= 360
) {
errorMessage =
'Your name must be < 40 characters. Email < 100, website < 200 and message < 360.';
showError = true;
return;
}
const data = {
name,
email,
website,
message
};
const jsonData = JSON.stringify(data);
const res = await fetch('https://api.quenten.nl/api/testing/comment', {
method: 'POST',
body: jsonData
});
if (res.status >= 300) {
errorMessage = await res.text();
showError = true;
return;
}
clear();
getComments();
}
function clear() {
name = '';
email = '';
website = '';
message = '';
}
async function getComments() {
// Check if in localstorage already
let commentLS = localStorage.getItem('comments');
let comments: Comment[];
if (commentLS === null) {
// Not yet in storage
comments = await updateComments();
DisplayComments = comments;
return;
}
let newHash = await fetch('https://api.quenten.nl/api/testing/commenthash')
.then((res) => res.text())
.then((t) => {
return t;
});
let oldHash = localStorage.getItem('commenthash');
if (oldHash != newHash) {
DisplayComments = await updateComments();
return;
}
DisplayComments = JSON.parse(decodeURIComponent(window.atob(commentLS)));
}
async function fetchComments() {
let comments: Comment[];
comments = await fetch('https://api.quenten.nl/api/testing/comment')
.then((res) => res.json())
.then((data) => {
return data;
});
return comments;
}
async function updateComments() {
let comments = await fetchComments();
localStorage.setItem('comments', window.btoa(encodeURIComponent(JSON.stringify(comments))));
localStorage.setItem('commenthash', sha256(JSON.stringify(comments)));
return comments;
}
onMount(() => {
getComments();
});
</script>
<Errormodal bind:show={showError} bind:title={errorTitle} bind:message={errorMessage} />
<div class="">
<h2 class="font-semibold">Guestbook</h2>
<p>You can leave any comment you want right here in this guestbook! No signing up necessary.</p>
<form on:submit|preventDefault={onSubmit} class="mt-2 w-full grid gap-2">
<input
class="p-1 rounded bg- border-2 focus:border-pink-400 dark:focus:border-pink-700 outline-none"
type="text"
name="name"
placeholder="Nickname (max. 40 chars)"
bind:value={name}
/>
<input
class="p-1 rounded bg- border-2 focus:border-pink-400 dark:focus:border-pink-700 outline-none"
type="text"
name="email"
placeholder="Email (optional)"
bind:value={email}
/>
<input
class="p-1 rounded bg- border-2 focus:border-pink-400 dark:focus:border-pink-700 outline-none"
type="text"
name="website"
placeholder="Website (optional)"
bind:value={website}
/>
<textarea
class="p-1 rounded bg- border-2 focus:border-pink-400 dark:focus:border-pink-700 outline-none"
type="text"
name="message"
placeholder="Message (max. 360 chars)"
bind:value={message}
/>
<button
class="bg-blue-100 border-slate-400 border-2 rounded-full py-2 w-fit px-10"
type="submit">Submit</button
>
</form>
<div>
{#if DisplayComments != null}
<div class="mt-4">
<button class="px-5 py-2 bg-slate-400 rounded-xl" on:click={() => {if (commentPage > 0) commentPage -= 1}}>Previous</button>
<button class="px-5 py-2 bg-slate-400 rounded-xl" on:click={() => commentPage += 1}>Next Page</button>
<p class="ml-2 mt-1 w-full md:w-max">Current page: {commentPage + 1}</p>
</div>
{#each DisplayComments.slice(commentPage * pageSize, commentPage * pageSize + pageSize) as c}
<GuestbookEntry comment={c} />
{/each}
{/if}
</div>
</div>

View File

@ -0,0 +1,24 @@
<script lang="ts">
import type { Comment } from '../guestbook.svelte';
export let comment: Comment;
</script>
<div class="rounded bg-red-100 mt-2 break-words">
<div class="rounded-t bg-slate-400 grid grid-cols-2 gap-1 min-h-fit px-3 py-1">
<p class="text-xs">Author: {comment.name}</p>
<p class="text-xs">
Time:
{#if comment.time != null}
{new Date(comment.time).toLocaleString('en-US', {dateStyle: 'long', timeStyle: 'short'})}
{:else}
brokn
{/if}
</p>
{#if comment.website != null}
<p class="text-xs col-span-2">Site: {comment.website}</p>
{/if}
</div>
<div class="px-3 ">
<p class="">{comment.message}</p>
</div>
</div>

View File

@ -3,7 +3,7 @@
import Button from './navbutton.svelte';
</script>
<div class="grid grid-cols-7 grid-rows-1 sm:grid-rows-1 gap-y-2 mt-2 sm:mt-4 sm:pl-4 align-middle h-24 md:h-16">
<div class="grid grid-cols-7 grid-rows-1 sm:grid-rows-1 gap-y-2 mt-2 sm:mt-4 sm:pl-4 align-middle h-10 sm:h-24 md:h-16">
<div class="lg:col-start-2 col-span-7 md:col-span-4 lg:col-span-3 flex flex-row items-start">
<div class="h-full sm:aspect-square">
<img

View File

@ -52,7 +52,8 @@
<slot />
</div>
<!-- Footer -->
<div bind:clientHeight={footerHeight} class="bg-slate-100 dark:bg-purple-900 dark:text-pink-50 w-full h-min md:h-10 lg:pl-20 absolute bottom-0">
<!-- To make this stick to the bottom, actually make it absolute, instead of fixed -->
<div bind:clientHeight={footerHeight} class="bg-slate-100 dark:bg-purple-900 dark:text-pink-50 w-full h-min md:h-10 lg:pl-20 fixed bottom-0">
<div class="h-full">
<button on:click={onMusicToggle} class="bg-white dark:bg-[#B68BD6] dark:text-[#040033] border-black border rounded h-min w-20 mt-1">
{#if musicPaused}

View File

@ -20,8 +20,7 @@
<Content />
</div>
<div class="hidden md:block col-start-7 mt-10 mr-2">
<Rightbar />
</div>
<div class="hidden md:block col-start-7 mt-10 mr-2">
<Rightbar />
</div>
</Maincontent>

View File

@ -86,6 +86,7 @@ async function fetchAndCache(request: Request) {
worker.addEventListener("fetch", (event) => {
if (event.request.method !== "GET") return;
if (new URL(event.request.url).hostname === "api.quenten.nl") return;
//console.log("fetch event intercepted")