183 lines
4.8 KiB
Svelte
183 lines
4.8 KiB
Svelte
<script lang="ts" context="module">
|
|
export type Comment = {
|
|
id: string;
|
|
name: string;
|
|
email: string;
|
|
website: string;
|
|
message: string;
|
|
time: string;
|
|
uuid: string;
|
|
};
|
|
</script>
|
|
|
|
<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 { mode } from '$app/env';
|
|
|
|
//const ApiURL = dev ? 'https://api.quenten.nl/api/testing' : 'https://api.quenten.nl/api';
|
|
//const ApiURL = import.meta.env.APIURL;
|
|
const ApiURL = mode == 'production' ? 'https://api.quenten.nl/api' : 'https://api.quenten.nl/api/testing';
|
|
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(ApiURL + '/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(ApiURL + '/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(ApiURL + '/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 dark:bg-slate-700 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 dark:bg-slate-800 dark:ring-slate-400 dark:ring-2 rounded-xl mx-1" on:click={() => {if (commentPage > 0) commentPage -= 1}}>Previous</button>
|
|
<button class="px-5 py-2 bg-slate-400 dark:bg-slate-800 dark:ring-slate-400 dark:ring-2 rounded-xl mx-1" 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>
|