Files
pastabble/pastabble-frontend/src/lib/pages/Home.svelte
2024-02-01 15:52:03 +01:00

177 lines
5.9 KiB
Svelte

<script lang="ts">
import { push } from "svelte-spa-router";
import LanguageSelector from "../components/LanguageSelector.svelte";
let language: string | undefined;
let lookupNoteId: string = '';
let enteredUrl: string = '';
let createdUrl: string = '';
let newNoteId: string = '';
let newNoteCode: string = '';
let urlError = false;
let lookupNoteIdError = false;
let creatingNote = false;
let creatingUrl = false;
let copied = false;
let showAlert = false;
$: if(copied) {
setTimeout(() => {
copied = false;
}, 5000);
}
$: if(showAlert) {
window.scrollTo({
top: 0,
behavior: 'smooth'
})
}
async function createNote() {
creatingNote = true;
const res = await fetch(language ? `/${newNoteId}?lang=${language}` : `/${newNoteId}`, {
method: 'POST',
body: newNoteCode
});
if(res.ok) {
const id = await res.text();
push(`/${id}`);
}
}
async function lookupNote() {
if(lookupNoteId.length === 0) {
lookupNoteIdError = true;
}
push(`/${lookupNoteId}`);
}
async function copyUrl() {
await navigator.clipboard.writeText(createdUrl);
copied = true;
}
async function shortenUrl() {
if(!isValidUrl(enteredUrl)) {
urlError = true;
return;
}
creatingUrl = true;
const res = await fetch('/to', {
method: 'POST',
body: enteredUrl
});
if(res.status === 200) {
const id = await res.text();
createdUrl = `${location.origin}/to/${id}`;
enteredUrl = '';
showAlert = true;
}
creatingUrl = false;
}
const isValidUrl = (url: string) => {
var urlPattern = new RegExp('^(https?:\\/\\/)?'+ // validate protocol
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|'+ // validate domain name
'((\\d{1,3}\\.){3}\\d{1,3}))'+ // validate OR ip (v4) address
'(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'+ // validate port and path
'(\\?[;&a-z\\d%_.~+=-]*)?'+ // validate query string
'(\\#[-a-z\\d_]*)?$','i'); // validate fragment locator
return !!urlPattern.test(url);
}
</script>
<div role="alert" class="alert transition-opacity ease-in-out duration-500 my-5 {showAlert ? 'opacity-100' : 'opacity-0'}">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="stroke-current shrink-0 w-6 h-6"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
<div class="flex flex-col md:flex-row gap-3 items-center w-full">
<span>Link created: </span>
<a href={createdUrl} class="font-medium text-blue-600 dark:text-blue-500 hover:underline">{createdUrl}</a>
<button on:click={copyUrl} class="btn btn-info ml-auto">
{copied ? 'Copied' : 'Copy'}
<svg class:hidden={!copied} xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
</svg>
</button>
</div>
</div>
<div class="flex flex-col md:flex-row gap-5 justify-around">
<div class="mt-10">
<div>
<h2 class="text-2xl font-bold mb-3">Lookup existing note</h2>
<div class="flex gap-2">
<input
on:keydown={(e) => {
lookupNoteIdError = false;
e.key === 'Enter' && lookupNote();
}}
bind:value={lookupNoteId}
type="text"
placeholder="Note ID"
class:input-error={lookupNoteIdError}
class:input-primary={!lookupNoteIdError}
class="input input-bordered w-full max-w-xs"
/>
<button on:click={lookupNote} class="btn btn-primary">Find</button>
</div>
</div>
<div class="mt-10">
<h2 class="text-2xl font-bold mb-3">URL shortener</h2>
<div class="flex gap-2">
<input
bind:value={enteredUrl}
on:keydown={(e) => {
lookupNoteIdError = false;
e.key === 'Enter' && shortenUrl();
}}
type="text"
placeholder="Enter your long URL"
class:input-error={urlError}
class="input input-bordered w-full max-w-xs"
/>
<button disabled={creatingUrl} on:click={shortenUrl} class="btn btn-outline btn-primary">
Shorten
<span class:hidden={!creatingUrl} class="loading loading-spinner"></span>
</button>
</div>
</div>
</div>
<div class="mt-10">
<h2 class="text-2xl font-bold mb-3">Or create a new one</h2>
<div class="flex flex-col md:flex-row gap-3">
<input
type="text"
placeholder="Note ID (empty for random)"
class="input input-bordered w-full max-w-xs"
bind:value={newNoteId}
/>
<LanguageSelector bind:language />
</div>
<textarea
class="textarea textarea-bordered mt-5 w-full h-32"
placeholder="Paste your code here..."
bind:value={newNoteCode}
></textarea>
<button disabled={creatingNote} on:click={createNote} class="btn btn-outline btn-primary w-full mt-3">
Save your paste
<span class:hidden={!creatingNote} class="loading loading-spinner"></span>
</button>
</div>
</div>