aboutsummaryrefslogtreecommitdiff
path: root/frontend/src/components
diff options
context:
space:
mode:
authorRyan Mehri <[email protected]>2020-05-14 11:07:16 -0600
committerGitHub <[email protected]>2020-05-14 11:07:16 -0600
commitd4a75e378cb04d6244048e0d292e7b0d18194c21 (patch)
tree30ce52784ba2f2790043884f333bc1807fa50a3a /frontend/src/components
parentMerge pull request #19 from jackyzha0/password (diff)
parentuse var (diff)
downloadctrl-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.js19
-rw-r--r--frontend/src/components/Err.js50
-rw-r--r--frontend/src/components/Inputs.js2
-rw-r--r--frontend/src/components/NewPaste.js85
-rw-r--r--frontend/src/components/PasswordModal.js75
-rw-r--r--frontend/src/components/ViewPaste.js92
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">📋&nbsp;</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">🚧&nbsp;</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)
})
}
}