diff options
| author | Ryan Mehri <[email protected]> | 2020-05-14 11:07:16 -0600 |
|---|---|---|
| committer | GitHub <[email protected]> | 2020-05-14 11:07:16 -0600 |
| commit | d4a75e378cb04d6244048e0d292e7b0d18194c21 (patch) | |
| tree | 30ce52784ba2f2790043884f333bc1807fa50a3a /frontend/src/components | |
| parent | Merge pull request #19 from jackyzha0/password (diff) | |
| parent | use var (diff) | |
| download | ctrl-v-d4a75e378cb04d6244048e0d292e7b0d18194c21.tar.xz ctrl-v-d4a75e378cb04d6244048e0d292e7b0d18194c21.zip | |
Merge pull request #20 from jackyzha0/pass-rendering
rendering password protected pastes
Diffstat (limited to 'frontend/src/components')
| -rw-r--r-- | frontend/src/components/App.js | 19 | ||||
| -rw-r--r-- | frontend/src/components/Err.js | 50 | ||||
| -rw-r--r-- | frontend/src/components/Inputs.js | 2 | ||||
| -rw-r--r-- | frontend/src/components/NewPaste.js | 85 | ||||
| -rw-r--r-- | frontend/src/components/PasswordModal.js | 75 | ||||
| -rw-r--r-- | frontend/src/components/ViewPaste.js | 92 |
6 files changed, 217 insertions, 106 deletions
diff --git a/frontend/src/components/App.js b/frontend/src/components/App.js index 605903e..3b147db 100644 --- a/frontend/src/components/App.js +++ b/frontend/src/components/App.js @@ -39,19 +39,20 @@ function App() { <span role="img" aria-label="clipboard">📋 </span> <Link to="/">ctrl-v</Link> </h1> - <Desc /> </nav> </SpacedTitle> - <Switch> - <Route path="/:hash" - children={<GetPasteWithParam />} - /> - <Route path="/"> - <NewPaste /> - </Route> - </Switch> + <main id="appElement"> + <Switch> + <Route path="/:hash" + children={<GetPasteWithParam />} + /> + <Route path="/"> + <NewPaste /> + </Route> + </Switch> + </main> <Footer /> </div> diff --git a/frontend/src/components/Err.js b/frontend/src/components/Err.js index 8562a74..dc78398 100644 --- a/frontend/src/components/Err.js +++ b/frontend/src/components/Err.js @@ -1,18 +1,54 @@ import React from 'react'; -import styled from 'styled-components' +import styled, { css } from 'styled-components' const ErrMsg = styled.p` display: inline; font-weight: 700; margin-left: 2em; - color: #ff3333 + color: #ff3333; + opacity: 0; + transition: opacity 0.3s cubic-bezier(.25,.8,.25,1); + + ${props => + (props.active) && css` + opacity: 1 + ` + }; ` -const Error = (props) => { - const msg = props.msg.toString().toLowerCase() - return ( - <ErrMsg> {msg} </ErrMsg> - ); +class Error extends React.Component { + + constructor(props) { + super(props); + + this.state = { + active: false, + msg: '', + }; + + this.showMessage = this.showMessage.bind(this); + } + + showMessage(msg, duration = 3000) { + this.setState({ + active: true, + msg: msg + }) + + // fadeout after duration ms if duration != -1 + if (duration !== -1) { + setTimeout(() => { + this.setState({ active: false }) + }, duration); + } + } + + render() { + const msg = this.state.msg.toString().toLowerCase() + return ( + <ErrMsg active={this.state.active}> {msg} </ErrMsg> + ); + } } export default Error
\ No newline at end of file diff --git a/frontend/src/components/Inputs.js b/frontend/src/components/Inputs.js index b61869f..da540d0 100644 --- a/frontend/src/components/Inputs.js +++ b/frontend/src/components/Inputs.js @@ -77,7 +77,7 @@ class PassInput extends React.Component { <input name="pass" className="lt-shadow" - placeholder="password (optional)" + placeholder="password" type="password" autoComplete="off" onChange={this.props.onChange} diff --git a/frontend/src/components/NewPaste.js b/frontend/src/components/NewPaste.js index 3bdd41a..f48f48b 100644 --- a/frontend/src/components/NewPaste.js +++ b/frontend/src/components/NewPaste.js @@ -1,9 +1,9 @@ import React from 'react'; import { TitleInput, PasteInput } from './Inputs' import OptionsContainer from './Options' -import axios from 'axios'; import { Redirect } from 'react-router-dom' import Error from './Err' +import { PostNewPaste } from '../helpers/httpHelper' class NewPaste extends React.Component { constructor(props) { @@ -19,13 +19,7 @@ class NewPaste extends React.Component { this.handleChange = this.handleChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); - } - - newErr(msg, duration = 5000) { - this.setState({ error: msg }) - setTimeout(() => { - this.setState({ error: '' }) - }, duration); + this.ErrorLabel = React.createRef(); } renderRedirect = () => { @@ -52,65 +46,24 @@ class NewPaste extends React.Component { }); } - parseExpiry(e) { - var cur = new Date(); - var inSeconds = 0 - switch (e) { - case '5 years': - inSeconds = 600 * 6 * 24 * 7 * 4 * 12 * 5 - break; - case '1 year': - inSeconds = 600 * 6 * 24 * 7 * 4 * 12 - break; - case '1 month': - inSeconds = 600 * 6 * 24 * 7 * 4 - break; - case '1 day': - inSeconds = 600 * 6 * 24 - break; - case '1 hour': - inSeconds = 600 * 6 - break; - case '10 min': - inSeconds = 600 - break; - case '1 week': - default: - inSeconds = 600 * 6 * 24 * 7 - break; - } - return new Date(cur.getTime() + inSeconds * 1000).toISOString(); - } - handleSubmit(event) { - var bodyFormData = new FormData(); - bodyFormData.set('title', this.state.title); - bodyFormData.set('content', this.state.content); - bodyFormData.set('password', this.state.pass); - bodyFormData.set('expiry', this.parseExpiry(this.state.expiry)); - - axios({ - method: 'post', - url: 'http://localhost:8080/api', - data: bodyFormData, - headers: { 'Content-Type': 'multipart/form-data' }, - }).then((response) => { - // on success, redir - this.setState({ hash: response.data.hash }) - }).catch((error) => { - const resp = error.response - - // some weird err - if (resp !== undefined) { - const errTxt = `${resp.statusText}: ${resp.data}` - this.newErr(errTxt) - } else { - // some weird err (e.g. network) - this.newErr(error) - } - }); - event.preventDefault(); + PostNewPaste(this.state) + .then((response) => { + // on success, redir + this.setState({ hash: response.data.hash }) + }).catch((error) => { + const resp = error.response + + // some weird err + if (resp !== undefined) { + const errTxt = `${resp.statusText}: ${resp.data}` + this.ErrorLabel.current.showMessage(errTxt) + } else { + // some weird err (e.g. network) + this.ErrorLabel.current.showMessage(error) + } + }); } render() { @@ -128,7 +81,7 @@ class NewPaste extends React.Component { maxLength="100000" id="pasteInput" /> <input className="lt-button lt-shadow lt-hover" type="submit" value="new paste" /> - <Error msg={this.state.error} /> + <Error ref={this.ErrorLabel} /> <OptionsContainer pass={this.state.pass} expiry={this.state.expiry} diff --git a/frontend/src/components/PasswordModal.js b/frontend/src/components/PasswordModal.js new file mode 100644 index 0000000..ff4a0df --- /dev/null +++ b/frontend/src/components/PasswordModal.js @@ -0,0 +1,75 @@ +import React from 'react'; +import Modal from 'react-modal'; +import styled from 'styled-components' +import { PassInput } from './Inputs' +import Error from './Err'; + +const modalStyles = { + content: { + top: '50%', + left: '50%', + transform: 'translate(-50%, -50%)', + width: '400px', + height: '250px', + border: '1px solid #11111188' + } +}; + +const PassProtected = styled.h3` + font-weight: 700 +` + +const RightPad = styled.div` + margin-right: 3em; +` + +const LeftPad = styled.div` + margin-left: 2em; +` + +class PasswordModal extends React.Component { + + componentWillMount() { + Modal.setAppElement('body'); + } + + constructor(props) { + super(props); + this.submitPassword = this.submitPassword.bind(this); + this.ErrorLabel = React.createRef(); + } + + submitPassword(event) { + const password = this.props.value + this.props.validateCallback(password) + event.preventDefault(); + } + + render() { + return( + <Modal + isOpen={this.props.hasPass && !this.props.validPass} + style={modalStyles} + contentLabel="enter paste password" + classNames + > + <form onSubmit={this.submitPassword}> + <LeftPad> + <PassProtected><span role="img" aria-label="warning">🚧 </span>err: password protected</PassProtected> + </LeftPad> + <RightPad> + <PassInput + value={this.props.value} + onChange={this.props.onChange} /> + </RightPad> + <LeftPad> + <input className="lt-button lt-shadow lt-hover" type="submit" value="continue" /> + <Error ref={this.ErrorLabel} /> + </LeftPad> + </form> + </Modal> + ); + } +} + +export default PasswordModal
\ No newline at end of file diff --git a/frontend/src/components/ViewPaste.js b/frontend/src/components/ViewPaste.js index 79b1840..7232825 100644 --- a/frontend/src/components/ViewPaste.js +++ b/frontend/src/components/ViewPaste.js @@ -1,8 +1,9 @@ import React from 'react'; -import axios from 'axios'; import Error from './Err'; import { TitleInput, PasteInput } from './Inputs'; import PasteInfo from './PasteInfo'; +import PasswordModal from './PasswordModal' +import { FetchPaste, FetchPasswordPaste } from '../helpers/httpHelper' const RENDER_MODES = Object.freeze({ RAW: 'raw text', @@ -19,21 +20,22 @@ class ViewPaste extends React.Component { title: 'untitled paste', content: '', hasPass: false, + enteredPass: '', + validPass: false, expiry: 'no expiry', error: '', + passError: '', mode: RENDER_MODES.RAW, }; - } - newErr(msg, duration = 5000) { - this.setState({ error: msg }) + this.handleChange = this.handleChange.bind(this); + this.validatePass = this.validatePass.bind(this); + this.ErrorLabel = React.createRef(); + this.PasswordModal = React.createRef(); + } - // if duration -1, dont clear - if (duration !== -1) { - setTimeout(() => { - this.setState({ error: '' }) - }, duration); - } + handleChange(event) { + this.setState({ enteredPass: event.target.value }); } drawRightMode() { @@ -50,9 +52,43 @@ class ViewPaste extends React.Component { } } + validatePass(pass) { + FetchPasswordPaste(this.props.hash, pass) + .then((response) => { + this.setState({ validPass: true }) + this.setStateFromData(response.data) + }).catch((error) => { + const resp = error.response + + // 401 unauth (bad pass) + if (resp.status === 401) { + this.PasswordModal.current + .ErrorLabel.current + .showMessage("incorrect pass") + return + } + + // otherwise, just log it lmao + if (resp !== undefined) { + const errTxt = `${resp.statusText}: ${resp.data}` + this.ErrorLabel.current.showMessage(errTxt) + } else { + // some weird err (e.g. network) + this.ErrorLabel.current.showMessage(error) + } + }); + } + render() { return ( <div> + <PasswordModal + ref={this.PasswordModal} + hasPass={this.state.hasPass} + validPass={this.state.validPass} + value={this.state.enteredPass} + onChange={this.handleChange} + validateCallback={this.validatePass} /> <TitleInput value={this.state.title} id="titleInput" @@ -63,7 +99,7 @@ class ViewPaste extends React.Component { <PasteInfo expiry={this.state.expiry} mode={this.state.mode} /> - <Error msg={this.state.error} /> + <Error ref={this.ErrorLabel} /> </div> ); } @@ -74,28 +110,38 @@ class ViewPaste extends React.Component { return d.toLocaleDateString("en-US", options).toLocaleLowerCase() } - componentDidMount() { - const serverURL = `http://localhost:8080/api/${this.props.hash}` + setStateFromData(data) { + console.log(data) + this.setState({ + title: data.title, + content: data.content, + expiry: this.fmtDateStr(data.expiry), + }) + } - axios.get(serverURL) + componentDidMount() { + FetchPaste(this.props.hash) .then((response) => { const data = response.data - this.setState({ - title: data.title, - content: data.content, - expiry: this.fmtDateStr(data.expiry), - }) + this.setStateFromData(data) }).catch((error) => { const resp = error.response + // catch 401 unauth (password protected) + if (resp.status === 401) { + this.setState({hasPass: true}) + return + } + // some weird err if (resp !== undefined) { const errTxt = `${resp.statusText}: ${resp.data}` - this.newErr(errTxt, -1) - } else { - // some weird err (e.g. network) - this.newErr(error, -1) + this.ErrorLabel.current.showMessage(errTxt, -1) + return } + + // some weird err (e.g. network) + this.ErrorLabel.current.showMessage(error, -1) }) } } |