diff options
Diffstat (limited to 'client/src/Components')
| -rw-r--r-- | client/src/Components/Button.js | 11 | ||||
| -rw-r--r-- | client/src/Components/CopyButton.js | 24 | ||||
| -rw-r--r-- | client/src/Components/Form.js | 33 | ||||
| -rw-r--r-- | client/src/Components/Loader.js | 13 | ||||
| -rw-r--r-- | client/src/Components/Response.js | 50 | ||||
| -rw-r--r-- | client/src/Components/ResponseContainer.js | 12 | ||||
| -rw-r--r-- | client/src/Components/Title.js | 12 |
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 |
