aboutsummaryrefslogtreecommitdiff
path: root/client/src/lib
diff options
context:
space:
mode:
authorMelonai <einebeere@gmail.com>2021-03-24 09:51:36 +0100
committerMelonai <einebeere@gmail.com>2021-03-24 09:51:36 +0100
commit42860fa15985401825d8d51e73ec497fe5876710 (patch)
treedaa43dd5f4ac77e46dbdd78b1a07811fa5db755c /client/src/lib
parent5dde1f55d818a74e838afa37b0e20217b1549a83 (diff)
downloadshorest-42860fa15985401825d8d51e73ec497fe5876710.tar.zst
shorest-42860fa15985401825d8d51e73ec497fe5876710.zip
Update SvelteKit to public beta
Diffstat (limited to 'client/src/lib')
-rw-r--r--client/src/lib/actions/shorten.ts50
-rw-r--r--client/src/lib/components/Form.svelte64
-rw-r--r--client/src/lib/components/Response.svelte33
-rw-r--r--client/src/lib/components/Responses.svelte24
-rw-r--r--client/src/lib/components/Title.svelte22
-rw-r--r--client/src/lib/components/icons/ArrowIcon.svelte10
-rw-r--r--client/src/lib/components/icons/CrossIcon.svelte10
-rw-r--r--client/src/lib/data/links.ts18
-rw-r--r--client/src/lib/utils/addProtocol.ts6
-rw-r--r--client/src/lib/utils/checkUrl.ts10
-rw-r--r--client/src/lib/utils/debounce.ts12
11 files changed, 259 insertions, 0 deletions
diff --git a/client/src/lib/actions/shorten.ts b/client/src/lib/actions/shorten.ts
new file mode 100644
index 0000000..ca685c5
--- /dev/null
+++ b/client/src/lib/actions/shorten.ts
@@ -0,0 +1,50 @@
+interface ShortenResponse {
+ hash: string;
+}
+
+export interface ShortenRequest {
+ url: string;
+ nonce: string;
+ response: Promise<ShortenResponse>;
+}
+
+async function makeRequest(url: string): Promise<ShortenResponse> {
+ let body;
+
+ try {
+ const response = await fetch("/", {
+ headers: {
+ Accept: "application/json",
+ "Content-Type": "application/json",
+ },
+ method: "post",
+ body: JSON.stringify({ url }),
+ });
+
+ body = await response.json();
+ } catch (err) {
+ throw {
+ error: "Error!",
+ };
+ }
+
+ if (body.hash) {
+ return {
+ hash: body.hash,
+ };
+ } else {
+ throw {
+ message: body.error || "Error!",
+ };
+ }
+}
+
+export default function shorten(url: string): ShortenRequest {
+ const nonce = Math.random().toString(36).substr(2, 5);
+
+ return {
+ url,
+ nonce,
+ response: makeRequest(url),
+ };
+}
diff --git a/client/src/lib/components/Form.svelte b/client/src/lib/components/Form.svelte
new file mode 100644
index 0000000..a05e868
--- /dev/null
+++ b/client/src/lib/components/Form.svelte
@@ -0,0 +1,64 @@
+<script lang="ts">
+ import shorten from "$lib/actions/shorten";
+ import { links } from "$lib/data/links";
+ import checkUrl from "$lib/utils/checkUrl";
+ import debounce from "$lib/utils/debounce";
+ import ArrowIcon from "./icons/ArrowIcon.svelte";
+ import CrossIcon from "./icons/CrossIcon.svelte";
+
+ let value = "";
+ let valid = false;
+
+ function submit() {
+ const url = checkUrl(value);
+ if (url !== null) {
+ links.add(shorten(url));
+ }
+ }
+
+ const check = debounce(() => valid = !!checkUrl(value), 100);
+
+ // @ts-ignore: Value is a dependency
+ $: value, check();
+</script>
+
+<style>
+ form {
+ position: relative;
+ border: 1px solid #aaaabb;
+ box-shadow: 0 4px 6px #aaaabb30;
+ box-sizing: border-box;
+ border-radius: 5px;
+ }
+
+ .field {
+ box-sizing: border-box;
+ width: 100%;
+ border: none;
+ padding: 15px 50px 15px 20px;
+ background: transparent;
+ font-size: 1rem;
+ }
+
+ .button {
+ position: absolute;
+ right: 10px;
+ margin: auto;
+ top: 0;
+ bottom: 0;
+ background: transparent;
+ border: none;
+ cursor: pointer;
+ }
+</style>
+
+<form on:submit|preventDefault={submit}>
+ <input class="field" bind:value type="text"/>
+ <button class="button" type="submit">
+ {#if valid}
+ <ArrowIcon/>
+ {:else}
+ <CrossIcon/>
+ {/if}
+ </button>
+</form>
diff --git a/client/src/lib/components/Response.svelte b/client/src/lib/components/Response.svelte
new file mode 100644
index 0000000..0ed8cc9
--- /dev/null
+++ b/client/src/lib/components/Response.svelte
@@ -0,0 +1,33 @@
+<script lang="ts">
+ import type { ShortenRequest } from "$lib/actions/shorten";
+
+ export let info: ShortenRequest;
+</script>
+
+<style>
+ div {
+ display: flex;
+ justify-content: space-between;
+ }
+
+ .url {
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
+ .output {
+ margin-left: 50px;
+ }
+</style>
+
+<div>
+ <span class="url">{info.url}</span>
+ {#await info.response}
+ <span class="output">Loading...</span>
+ {:then { hash }}
+ <a class="output" href="https://sho.rest/{hash}">sho.rest/{hash}</a>
+ {:catch { error }}
+ <span class="output">{error}</span>
+ {/await}
+</div> \ No newline at end of file
diff --git a/client/src/lib/components/Responses.svelte b/client/src/lib/components/Responses.svelte
new file mode 100644
index 0000000..6e83658
--- /dev/null
+++ b/client/src/lib/components/Responses.svelte
@@ -0,0 +1,24 @@
+<script lang="ts">
+ import Response from "./Response.svelte"
+ import { slide } from 'svelte/transition';
+ import { links } from "$lib/data/links";
+</script>
+
+<style>
+ ul {
+ list-style: none;
+ padding: 0 10px;
+ }
+
+ li {
+ margin-bottom: 10px;
+ }
+</style>
+
+<ul>
+ {#each $links as info (info.nonce)}
+ <li transition:slide >
+ <Response {info}/>
+ </li>
+ {/each}
+</ul> \ No newline at end of file
diff --git a/client/src/lib/components/Title.svelte b/client/src/lib/components/Title.svelte
new file mode 100644
index 0000000..4266eb1
--- /dev/null
+++ b/client/src/lib/components/Title.svelte
@@ -0,0 +1,22 @@
+<style>
+ p {
+ color: #212121;
+ margin: 0 0 5px 0;
+ }
+
+ .text {
+ margin: 10px 0 15px 0;
+ }
+
+ img {
+ width: 25px;
+ }
+</style>
+
+<div>
+ <img src="/shorest.svg" alt=""/>
+ <div class="text">
+ <p><b>sho.rest</b></p>
+ <p>Made with ❤ by <b>Mel</b></p>
+ </div>
+</div>
diff --git a/client/src/lib/components/icons/ArrowIcon.svelte b/client/src/lib/components/icons/ArrowIcon.svelte
new file mode 100644
index 0000000..52c79ae
--- /dev/null
+++ b/client/src/lib/components/icons/ArrowIcon.svelte
@@ -0,0 +1,10 @@
+<style>
+ svg {
+ width: 20px;
+ color: #212121;
+ }
+</style>
+
+<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7m0 0l-7 7m7-7H3" />
+</svg> \ No newline at end of file
diff --git a/client/src/lib/components/icons/CrossIcon.svelte b/client/src/lib/components/icons/CrossIcon.svelte
new file mode 100644
index 0000000..55525d8
--- /dev/null
+++ b/client/src/lib/components/icons/CrossIcon.svelte
@@ -0,0 +1,10 @@
+<style>
+ svg {
+ width: 20px;
+ color: #212121;
+ }
+</style>
+
+<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
+</svg> \ No newline at end of file
diff --git a/client/src/lib/data/links.ts b/client/src/lib/data/links.ts
new file mode 100644
index 0000000..0f0a9ce
--- /dev/null
+++ b/client/src/lib/data/links.ts
@@ -0,0 +1,18 @@
+import type { ShortenRequest } from "$lib/actions/shorten";
+import type { Writable } from "svelte/store";
+import { writable } from "svelte/store";
+
+function createLinks() {
+ const { subscribe, update }: Writable<ShortenRequest[]> = writable([]);
+
+ function add(request: ShortenRequest) {
+ update((l) => [request, ...l.slice(0, 2)]);
+ }
+
+ return {
+ subscribe,
+ add,
+ };
+}
+
+export const links = createLinks();
diff --git a/client/src/lib/utils/addProtocol.ts b/client/src/lib/utils/addProtocol.ts
new file mode 100644
index 0000000..75c6214
--- /dev/null
+++ b/client/src/lib/utils/addProtocol.ts
@@ -0,0 +1,6 @@
+export default function (url: string) {
+ if (!/^https?:\/\//.test(url)) {
+ url = "https://" + url;
+ }
+ return url;
+}
diff --git a/client/src/lib/utils/checkUrl.ts b/client/src/lib/utils/checkUrl.ts
new file mode 100644
index 0000000..8ed747f
--- /dev/null
+++ b/client/src/lib/utils/checkUrl.ts
@@ -0,0 +1,10 @@
+import addProtocol from "./addProtocol";
+
+export default function (url: string): string | null {
+ try {
+ const normalizedUrl = new URL(addProtocol(url));
+ return normalizedUrl.toString();
+ } catch (e) {
+ return null;
+ }
+} \ No newline at end of file
diff --git a/client/src/lib/utils/debounce.ts b/client/src/lib/utils/debounce.ts
new file mode 100644
index 0000000..86ef3db
--- /dev/null
+++ b/client/src/lib/utils/debounce.ts
@@ -0,0 +1,12 @@
+type Procedure = (...args: any[]) => any;
+
+export default function <F extends Procedure>(f: F, duration: number) {
+ let timeout: ReturnType<typeof setTimeout> | null = null;
+ return function (...args: Parameters<F>) {
+ if (timeout !== null) {
+ clearTimeout(timeout);
+ timeout = null;
+ }
+ timeout = setTimeout(() => f(...args), duration);
+ };
+}