aboutsummaryrefslogtreecommitdiff
path: root/client/src/Components
diff options
context:
space:
mode:
authorMelonai <einebeere@gmail.com>2020-05-20 19:52:18 +0200
committerMelonai <einebeere@gmail.com>2020-05-20 19:52:18 +0200
commite58b453f24c8b4081361112862d29c34eb22009d (patch)
tree3437fb38ecb8b22ad5475d88f7e9ca901b320917 /client/src/Components
parenta00a8a867cae381982c7b8b77f07836ab4a504ed (diff)
downloadshorest-e58b453f24c8b4081361112862d29c34eb22009d.tar.zst
shorest-e58b453f24c8b4081361112862d29c34eb22009d.zip
port to react and better error handling in backend
Diffstat (limited to 'client/src/Components')
-rw-r--r--client/src/Components/Button.js11
-rw-r--r--client/src/Components/CopyButton.js24
-rw-r--r--client/src/Components/Form.js33
-rw-r--r--client/src/Components/Loader.js13
-rw-r--r--client/src/Components/Response.js50
-rw-r--r--client/src/Components/ResponseContainer.js12
-rw-r--r--client/src/Components/Title.js12
7 files changed, 155 insertions, 0 deletions
diff --git a/client/src/Components/Button.js b/client/src/Components/Button.js
new file mode 100644
index 0000000..46c0c27
--- /dev/null
+++ b/client/src/Components/Button.js
@@ -0,0 +1,11 @@
+import React from 'react';
+
+function Button(props) {
+ return (
+ <div className="button-container">
+ <input type="submit" value={props.valid ? "→" : ""} className="button" id="btn" onClick={props.submit}/>
+ </div>
+ )
+}
+
+export default Button; \ No newline at end of file
diff --git a/client/src/Components/CopyButton.js b/client/src/Components/CopyButton.js
new file mode 100644
index 0000000..0ae8e83
--- /dev/null
+++ b/client/src/Components/CopyButton.js
@@ -0,0 +1,24 @@
+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" 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
new file mode 100644
index 0000000..4d10f98
--- /dev/null
+++ b/client/src/Components/Form.js
@@ -0,0 +1,33 @@
+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});
+ };
+
+ return (
+ <form id="form" onSubmit={(e) => e.preventDefault()}>
+ <div className={"input-group" + (state.valid ? "" : " disabled")}>
+ <div className={"input-container" + (state.valid ? "" : " border-r-none")}>
+ <span className="input-field-text">https://</span>
+ <input className="input-field" required onChange={handleChange}/>
+ </div>
+ <Button valid={state.valid} submit={handleSubmit}/>
+ </div>
+ </form>
+ )
+}
+
+export default Form; \ No newline at end of file
diff --git a/client/src/Components/Loader.js b/client/src/Components/Loader.js
new file mode 100644
index 0000000..3f4c47f
--- /dev/null
+++ b/client/src/Components/Loader.js
@@ -0,0 +1,13 @@
+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
new file mode 100644
index 0000000..83c6ff1
--- /dev/null
+++ b/client/src/Components/Response.js
@@ -0,0 +1,50 @@
+import React, {useEffect, useState} from 'react';
+import axios from "axios";
+import Loader from "./Loader";
+import CopyButton from "./CopyButton";
+
+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;
+ if (!requestState.loading) {
+ if (!requestState.error) {
+ 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 {
+ 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>
+ {requestState.error || requestState.loading ? "" : <CopyButton hash={requestState.hash}/>}
+ </div>
+ )
+}
+
+export default Response; \ No newline at end of file
diff --git a/client/src/Components/ResponseContainer.js b/client/src/Components/ResponseContainer.js
new file mode 100644
index 0000000..8fad4bd
--- /dev/null
+++ b/client/src/Components/ResponseContainer.js
@@ -0,0 +1,12 @@
+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/Title.js b/client/src/Components/Title.js
new file mode 100644
index 0000000..8ea96a9
--- /dev/null
+++ b/client/src/Components/Title.js
@@ -0,0 +1,12 @@
+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