177 lines
5.9 KiB
Svelte
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>
|