diff options
| author | Melonai <einebeere@gmail.com> | 2021-01-20 23:18:09 +0100 |
|---|---|---|
| committer | Melonai <einebeere@gmail.com> | 2021-01-20 23:18:09 +0100 |
| commit | 826c7c47785ee01d2b9267919132ada696425344 (patch) | |
| tree | 901cc90be9a953a6c3f968b6c1abe33cc13774b4 /client/src/Components | |
| parent | 2953dec527cedaabaa5f0eb48637c5ddd4a4103b (diff) | |
| download | shorest-826c7c47785ee01d2b9267919132ada696425344.tar.zst shorest-826c7c47785ee01d2b9267919132ada696425344.zip | |
Remade the client in SvelteKit
Diffstat (limited to 'client/src/Components')
| -rw-r--r-- | client/src/Components/Button.js | 15 | ||||
| -rw-r--r-- | client/src/Components/CopyButton.js | 24 | ||||
| -rw-r--r-- | client/src/Components/Form.js | 39 | ||||
| -rw-r--r-- | client/src/Components/Form.svelte | 64 | ||||
| -rw-r--r-- | client/src/Components/Loader.js | 13 | ||||
| -rw-r--r-- | client/src/Components/Response.js | 55 | ||||
| -rw-r--r-- | client/src/Components/Response.svelte | 33 | ||||
| -rw-r--r-- | client/src/Components/ResponseContainer.js | 12 | ||||
| -rw-r--r-- | client/src/Components/Responses.svelte | 24 | ||||
| -rw-r--r-- | client/src/Components/Title.js | 12 | ||||
| -rw-r--r-- | client/src/Components/Title.svelte | 22 | ||||
| -rw-r--r-- | client/src/Components/icons/ArrowIcon.svelte | 10 | ||||
| -rw-r--r-- | client/src/Components/icons/CrossIcon.svelte | 10 |
13 files changed, 163 insertions, 170 deletions
diff --git a/client/src/Components/Button.js b/client/src/Components/Button.js deleted file mode 100644 index b6ed91b..0000000 --- a/client/src/Components/Button.js +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import {faArrowRight, faTimes} from '@fortawesome/free-solid-svg-icons'; - -function Button(props) { - return ( - <div className="button-container"> - <button type="submit" className={"button" + (props.valid ? "" : " disabled")} id="btn" onClick={props.submit}> - <FontAwesomeIcon icon={props.valid ? faArrowRight : faTimes}/> - </button> - </div> - ) -} - -export default Button; \ No newline at end of file diff --git a/client/src/Components/CopyButton.js b/client/src/Components/CopyButton.js deleted file mode 100644 index 65c4cb9..0000000 --- a/client/src/Components/CopyButton.js +++ /dev/null @@ -1,24 +0,0 @@ -import React, {useState} from 'react'; -import copy from 'clipboard-copy'; - -function CopyButton(props) { - const [copied, setCopied] = useState(false); - - const handleClick = async () => { - await copy("https://sho.rest/" + props.hash); - setCopied(true); - }; - - let content; - if (copied) { - content = <span>Link Copied!</span>; - } else { - content = <strong>Copy Link</strong>; - } - - return ( - <span className="copy-text right-item" onClick={handleClick}>{content}</span> - ) -} - -export default CopyButton; \ No newline at end of file diff --git a/client/src/Components/Form.js b/client/src/Components/Form.js deleted file mode 100644 index 979d9c9..0000000 --- a/client/src/Components/Form.js +++ /dev/null @@ -1,39 +0,0 @@ -import React, {useState} from 'react'; -import Button from './Button'; -import isURL from "validator/lib/isURL"; - -function Form(props) { - const [state, setState] = useState({value: '', valid: false}); - - const handleSubmit = () => { - if (state.valid) { - props.addRequest(state.value); - } - }; - - const handleChange = e => { - const userInput = e.target.value; - const valid = isURL('https://' + userInput); - setState({value: userInput, valid: valid}); - }; - - const handlePaste = e => { - e.preventDefault(); - const pattern = /^https?:\/\//; - setState({value: e.clipboardData.getData('Text').replace(pattern, ''), valid: false}); - }; - - return ( - <form id="form" onSubmit={(e) => e.preventDefault()}> - <div className="input-group"> - <div className="input-container"> - <span className="input-field-text">https://</span> - <input className="input-field" required value={state.value} onChange={handleChange} onPaste={handlePaste}/> - </div> - <Button valid={state.valid} submit={handleSubmit}/> - </div> - </form> - ) -} - -export default Form; \ No newline at end of file diff --git a/client/src/Components/Form.svelte b/client/src/Components/Form.svelte new file mode 100644 index 0000000..327d25d --- /dev/null +++ b/client/src/Components/Form.svelte @@ -0,0 +1,64 @@ +<script lang="ts"> + import shorten from "$actions/shorten"; + import { links } from "$data/links"; + import checkUrl from "$utils/checkUrl"; + import debounce from "$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/Components/Loader.js b/client/src/Components/Loader.js deleted file mode 100644 index 3f4c47f..0000000 --- a/client/src/Components/Loader.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -function Loader() { - return ( - <div className="ball-pulse"> - <div/> - <div/> - <div/> - </div> - ) -} - -export default Loader; \ No newline at end of file diff --git a/client/src/Components/Response.js b/client/src/Components/Response.js deleted file mode 100644 index f69000a..0000000 --- a/client/src/Components/Response.js +++ /dev/null @@ -1,55 +0,0 @@ -import React, {useEffect, useState} from 'react'; -import axios from "axios"; -import Loader from "./Loader"; -import CopyButton from "./CopyButton"; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faBomb } from '@fortawesome/free-solid-svg-icons'; - -function Response(props){ - const CancelToken = axios.CancelToken; - const [requestState, setRequestState] = useState({loading: true, cancel: CancelToken.source()}); - - useEffect(() => { - axios.post('/', {url: "https://" + props.url}, {cancelToken: requestState.cancel.token}) - .then((r) => { - setRequestState({loading: false, hash: r.data.hash, cancel: requestState.cancel}); - }).catch((e) => { - if (!axios.isCancel(e)) { - setRequestState({loading: false, error: true, cancel: requestState.cancel}); - } - }); - - return () => { - requestState.cancel.cancel(); - }; - }, [props.url, requestState.cancel]) - - let text; - let rightItem; - if (!requestState.loading) { - if (!requestState.error) { - rightItem = <CopyButton hash={requestState.hash}/>; - if (props.url.length < 20) { - text = - <span>The short link for <strong>{props.url}</strong> is<br/><strong>sho.rest/{requestState.hash}</strong></span>; - } else { - text = - <span>The short link for your URL is<br/><strong>sho.rest/{requestState.hash}</strong></span>; - } - } else { - rightItem = <FontAwesomeIcon className="right-item" icon={faBomb}/>; - text = <span>There was an error.</span> - } - } else { - text = <Loader/> - } - - return ( - <div className={"response-container" + (requestState.error ? " disabled" : "")}> - <div className={"title response-text" + (requestState.error ? " disabled" : "")}>{text}</div> - {rightItem} - </div> - ) -} - -export default Response; \ No newline at end of file diff --git a/client/src/Components/Response.svelte b/client/src/Components/Response.svelte new file mode 100644 index 0000000..215af1b --- /dev/null +++ b/client/src/Components/Response.svelte @@ -0,0 +1,33 @@ +<script lang="ts"> + import type { ShortenRequest } from "$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/Components/ResponseContainer.js b/client/src/Components/ResponseContainer.js deleted file mode 100644 index 8fad4bd..0000000 --- a/client/src/Components/ResponseContainer.js +++ /dev/null @@ -1,12 +0,0 @@ -import React from 'react'; -import Response from "./Response"; - -function ResponseContainer(props){ - const responseContent = props.requests.map((r) => <Response key={r.key} url={r.url}/>); - - return ( - responseContent - ) -} - -export default ResponseContainer; \ No newline at end of file diff --git a/client/src/Components/Responses.svelte b/client/src/Components/Responses.svelte new file mode 100644 index 0000000..7124f1e --- /dev/null +++ b/client/src/Components/Responses.svelte @@ -0,0 +1,24 @@ +<script lang="ts"> + import Response from "./Response.svelte" + import { slide } from 'svelte/transition'; + import { links } from "$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/Components/Title.js b/client/src/Components/Title.js deleted file mode 100644 index 8ea96a9..0000000 --- a/client/src/Components/Title.js +++ /dev/null @@ -1,12 +0,0 @@ -import React from 'react'; - -function Title() { - return ( - <div className="title"> - <span><b>sho.rest</b><br/></span> - <span>Made with ❤ by <b>Mel</b></span> - </div> - ) -} - -export default Title; \ No newline at end of file diff --git a/client/src/Components/Title.svelte b/client/src/Components/Title.svelte new file mode 100644 index 0000000..4266eb1 --- /dev/null +++ b/client/src/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/Components/icons/ArrowIcon.svelte b/client/src/Components/icons/ArrowIcon.svelte new file mode 100644 index 0000000..52c79ae --- /dev/null +++ b/client/src/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/Components/icons/CrossIcon.svelte b/client/src/Components/icons/CrossIcon.svelte new file mode 100644 index 0000000..55525d8 --- /dev/null +++ b/client/src/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 |
