aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile27
-rw-r--r--frontend/src/components/App.js2
-rw-r--r--frontend/src/components/Inputs.js233
-rw-r--r--frontend/src/components/NewPaste.js187
-rw-r--r--frontend/src/components/Options.js36
-rw-r--r--frontend/src/components/PasteInfo.js3
-rw-r--r--frontend/src/components/ViewPaste.js212
-rw-r--r--frontend/src/components/decorators/CharLimit.js10
-rw-r--r--frontend/src/components/decorators/FloatingLabel.js22
-rw-r--r--frontend/src/components/modals/PasswordModal.js72
-rw-r--r--frontend/src/components/renderers/Latex.js56
-rw-r--r--frontend/src/components/renderers/Raw.js75
-rw-r--r--frontend/src/helpers/httpHelper.js12
13 files changed, 418 insertions, 529 deletions
diff --git a/Makefile b/Makefile
index 7ce24d6..c1a1e18 100644
--- a/Makefile
+++ b/Makefile
@@ -1,22 +1,27 @@
-run:
+.DEFAULT_GOAL := help
+
+help: ## Show all Makefile targets
+ @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
+
+run: ## Start backend
cd backend && go run .
-lint:
+lint: ## Lint backend
cd backend && golangci-lint run
-docker-build:
+docker-build: ## Docker build backend
docker build -t jzhao2k19/ctrl-v:latest ./backend
-docker-run: docker-build
+docker-run: docker-build ## Start dockerized backend
docker run -p 8080:8080 jzhao2k19/ctrl-v:latest
-gcr: docker-build
+gcr: docker-build ## Push to GCR
docker tag jzhao2k19/ctrl-v:latest gcr.io/ctrl-v-278404/backend && docker push gcr.io/ctrl-v-278404/backend
-docker-push:
+docker-push: ## Push to Docker Hub
docker push jzhao2k19/ctrl-v:latest
-fe-run:
+fe-run: ## Start Frontend
cd frontend && yarn start
-fe-build:
+fe-build: ## Productionize Frontend
cd frontend && yarn build
-fe-deploy: fe-build
+fe-deploy: fe-build ## Deploy frontend to Firebase
cd frontend && firebase deploy
-dev:
+dev: ## Start backend and frontend
make -j 2 run fe-run
-deploy:
+deploy: ## Deploy backend and frontend
gcr && fe-deploy \ No newline at end of file
diff --git a/frontend/src/components/App.js b/frontend/src/components/App.js
index ae95dcb..0a5fb8b 100644
--- a/frontend/src/components/App.js
+++ b/frontend/src/components/App.js
@@ -38,7 +38,7 @@ const GetRawWithParam = () => {
);
}
-function App() {
+const App = () => {
return (
<Router>
<Switch>
diff --git a/frontend/src/components/Inputs.js b/frontend/src/components/Inputs.js
index 08cf3fc..a9b08b7 100644
--- a/frontend/src/components/Inputs.js
+++ b/frontend/src/components/Inputs.js
@@ -15,43 +15,33 @@ const FlexChild = styled.div`
margin-left: 2em;
`
-class TitleInput extends React.Component {
- render() {
- return (
- <RelPositioning>
- <FloatingLabel
- label="title"
- id={this.props.id}
- value={this.props.value} />
- <input
- name="title"
- readOnly={this.props.readOnly}
- className="lt-shadow"
- placeholder="Title"
- id={this.props.id}
- type="text"
- autoFocus
- autoComplete="off"
- onChange={this.props.onChange}
- value={this.props.value} />
- <CharLimit
- content={this.props.value}
- maxLength={this.props.maxLength} />
- </RelPositioning>
- );
- }
+const TitleInput = (props) => {
+ return (
+ <RelPositioning>
+ <FloatingLabel
+ label="title"
+ id={props.id}
+ value={props.value} />
+ <input
+ name="title"
+ readOnly={props.readOnly}
+ className="lt-shadow"
+ placeholder="Title"
+ id={props.id}
+ type="text"
+ autoFocus
+ autoComplete="off"
+ onChange={props.onChange}
+ value={props.value} />
+ <CharLimit
+ content={props.value}
+ maxLength={props.maxLength} />
+ </RelPositioning>
+ );
}
-class PasteInput extends React.Component {
-
- constructor(props) {
- super(props)
-
- this.textArea = React.createRef()
- this.handleKeyDown = this.handleKeyDown.bind(this)
- }
-
- handleKeyDown(e) {
+const PasteInput = (props) => {
+ function handleKeyDown(e) {
if (e.keyCode === 9) { // tab was pressed
// prevent autofocus on next intput
@@ -61,96 +51,83 @@ class PasteInput extends React.Component {
const start = e.target.selectionStart
const end = e.target.selectionEnd
- this.props.insertTabCallback(start, end)
+ props.insertTabCallback(start, end)
// set cursor position to be at start
e.target.selectionEnd = end + 4;
}
}
- render() {
- return (
+ return (
+ <RelPositioning>
+ <FloatingLabel
+ label="content"
+ id={props.id}
+ value={props.content} />
+ <textarea
+ name="content"
+ readOnly={props.readOnly}
+ placeholder="Paste your text here"
+ value={props.content}
+ id={props.id}
+ required
+ onChange={props.onChange}
+ onKeyDown={handleKeyDown}
+ className="lt-shadow" />
+ <CharLimit
+ content={props.content}
+ maxLength={props.maxLength} />
+ </RelPositioning>
+ );
+}
+
+const PassInput = (props) => {
+ return (
+ <FlexChild>
<RelPositioning>
<FloatingLabel
- label="content"
- id={this.props.id}
- value={this.props.content} />
- <textarea
- name="content"
- readOnly={this.props.readOnly}
- placeholder="Paste your text here"
- value={this.props.content}
- id={this.props.id}
- required
- onChange={this.props.onChange}
- onKeyDown={this.handleKeyDown}
- className="lt-shadow" />
- <CharLimit
- content={this.props.content}
- maxLength={this.props.maxLength} />
+ label="password"
+ id={props.id}
+ value={props.value} />
+ <input
+ name="pass"
+ className="lt-shadow"
+ placeholder="password"
+ type="password"
+ autoComplete="off"
+ onChange={props.onChange}
+ value={props.value}
+ id={props.id} />
</RelPositioning>
- );
- }
-}
-
-class PassInput extends React.Component {
- render() {
- return (
- <FlexChild>
- <RelPositioning>
- <FloatingLabel
- label="password"
- id={this.props.id}
- value={this.props.value} />
- <input
- name="pass"
- className="lt-shadow"
- placeholder="password"
- type="password"
- autoComplete="off"
- onChange={this.props.onChange}
- value={this.props.value}
- id={this.props.id} />
- </RelPositioning>
- </FlexChild>
- );
- }
+ </FlexChild>
+ );
}
-class GenericDropdown extends React.Component {
-
- constructor(props) {
- super(props)
-
- this._onSelect = this._onSelect.bind(this)
- }
-
- _onSelect(option) {
- this.props.onChange({
+const GenericDropdown = (props) => {
+ function _onSelect(option) {
+ props.onChange({
target: {
- name: this.props.label,
+ name: props.label,
value: option.label
}
});
}
- render() {
- return (
- <FlexChild>
- <Dropdown
- options={this.props.options}
- onChange={this._onSelect}
- callBackRef={this.props.onChange}
- value={this.props.value}
- placeholder={this.props.placeholder}
- id={this.props.id} />
- <FloatingLabel
- label={this.props.label}
- id={this.props.id}
- value={this.props.value} />
- </FlexChild>
- );
- }
+ return (
+ <FlexChild>
+ <Dropdown
+ options={props.options}
+ onChange={_onSelect}
+ callBackRef={props.onChange}
+ value={props.value}
+ placeholder={props.placeholder}
+ id={props.id} />
+ <FloatingLabel
+ label={props.label}
+ id={props.id}
+ value={props.value} />
+ </FlexChild>
+ );
}
const ExpiryInput = (props) => {
@@ -210,27 +187,25 @@ const ThemeInput = (props) => {
);
}
-class PasteURLInput extends React.Component {
- render() {
- return (
- <FlexChild>
- <RelPositioning>
- <FloatingLabel
- label="url"
- id={this.props.id}
- value={this.props.id} />
- <input
- name="paste_url"
- className="lt-shadow"
- type="text"
- readOnly
- autoFocus
- id={this.props.id}
- value={this.props.fullURL} />
- </RelPositioning>
- </FlexChild>
- );
- }
+const PasteURLInput = ({id, fullURL}) => {
+ return (
+ <FlexChild>
+ <RelPositioning>
+ <FloatingLabel
+ label="url"
+ id={id}
+ value={id} />
+ <input
+ name="paste_url"
+ className="lt-shadow"
+ type="text"
+ readOnly
+ autoFocus
+ id={id}
+ value={fullURL} />
+ </RelPositioning>
+ </FlexChild>
+ );
}
export { TitleInput, PasteInput, PassInput, ExpiryInput, PasteURLInput, LangInput, ThemeInput } \ No newline at end of file
diff --git a/frontend/src/components/NewPaste.js b/frontend/src/components/NewPaste.js
index afe1fc3..9729655 100644
--- a/frontend/src/components/NewPaste.js
+++ b/frontend/src/components/NewPaste.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { useEffect, useState, useRef } from 'react';
import { TitleInput, PasteInput } from './Inputs'
import OptionsContainer from './Options'
import Error from './Err'
@@ -35,114 +35,85 @@ const PreviewWrapper = styled.div`
margin: 2em;
`
-class NewPaste extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- title: '',
- content: '',
- pass: '',
- language: LANGS.raw,
- expiry: '',
- hash: '',
- error: '',
- preview: false,
- };
-
- this.handleChange = this.handleChange.bind(this);
- this.handleSubmit = this.handleSubmit.bind(this);
- this.togglePreview = this.togglePreview.bind(this);
- this.renderPreview = this.renderPreview.bind(this);
- this.insertTab = this.insertTab.bind(this);
- this.ErrorLabel = React.createRef();
- }
-
- componentDidUpdate() {
- if (this.state.title === "") {
+const NewPaste = () => {
+ const [title, setTitle] = useState('');
+ const [content, setContent] = useState('');
+ const [pass, setPass] = useState('');
+ const [language, setLanguage] = useState(LANGS.raw);
+ const [expiry, setExpiry] = useState('');
+ const [hash, setHash] = useState('');
+ const [isPreview, setIsPreview] = useState(false);
+ const ErrorLabel = useRef(null);
+
+ useEffect(() => {
+ if (title === "") {
document.title = `ctrl-v`;
} else {
- document.title = `ctrl-v | ${this.state.title}`;
+ document.title = `ctrl-v | ${title}`;
}
- }
-
- handleChange(event) {
- const target = event.target;
- const name = target.name;
+ }, [title])
- this.setState({
- [name]: target.value
- });
- }
-
- togglePreview() {
- const state = this.state.preview
- this.setState({ preview: !state })
- }
-
- handleSubmit(event) {
- event.preventDefault();
+ function handleSubmit(e) {
+ e.preventDefault();
// prevent resubmission
- if (!this.state.hash) {
- PostNewPaste(this.state)
+ if (!hash) {
+ PostNewPaste(title, content, language, pass, expiry)
.then((response) => {
// on success, redir
- this.setState({ hash: response.data.hash })
+ setHash(response.data.hash)
}).catch((error) => {
const resp = error.response
// some weird err
if (resp !== undefined) {
const errTxt = `${resp.status}: ${resp.data}`
- this.ErrorLabel.current.showMessage(errTxt)
+ ErrorLabel.current.showMessage(errTxt)
} else {
// some weird err (e.g. network)
- this.ErrorLabel.current.showMessage(error)
+ ErrorLabel.current.showMessage(error)
}
});
}
}
- insertTab(start, end) {
- const oldContent = this.state.content
- this.setState({
- content: oldContent.substring(0, start) + ' ' + oldContent.substring(end)
- })
+ function insertTab(start, end) {
+ setContent(content.substring(0, start) + ' ' + content.substring(end))
}
- renderPreview() {
+ function renderPreview() {
const pasteInput = <PasteInput
- onChange={this.handleChange}
- insertTabCallback={this.insertTab}
- content={this.state.content}
+ onChange={(e) => { setContent(e.target.value) }}
+ insertTabCallback={insertTab}
+ content={content}
maxLength="100000"
id="pasteInput" />
- var preview
- switch (this.state.language) {
- case 'latex':
- preview =
- <PreviewWrapper>
- <Latex
- content={this.state.content} />
- </PreviewWrapper>
- break
- case 'markdown':
- preview =
- <PreviewWrapper>
- <Markdown
- content={this.state.content} />
- </PreviewWrapper>
- break
- default:
- preview =
- <CodeRenderer
- lang={this.state.language}
- theme='atom'
- content={this.state.content} />
- }
+ if (isPreview) {
+ var preview
+ switch (language) {
+ case 'latex':
+ preview =
+ <PreviewWrapper>
+ <Latex
+ content={content} />
+ </PreviewWrapper>
+ break
+ case 'markdown':
+ preview =
+ <PreviewWrapper>
+ <Markdown
+ content={content} />
+ </PreviewWrapper>
+ break
+ default:
+ preview =
+ <CodeRenderer
+ lang={language}
+ theme='atom'
+ content={content} />
+ }
- if (this.state.preview) {
return (
<Flex>
<FlexLeft>
@@ -160,33 +131,33 @@ class NewPaste extends React.Component {
}
}
- render() {
- return (
- <form onSubmit={this.handleSubmit}>
- <PasteModal hash={this.state.hash} />
- <TitleInput
- onChange={this.handleChange}
- value={this.state.title}
- maxLength="100"
- id="titleInput" />
- {this.renderPreview()}
- <OptionsContainer
- pass={this.state.pass}
- expiry={this.state.expiry}
- lang={this.state.language}
- onChange={this.handleChange} />
- <input className="lt-button lt-shadow lt-hover" type="submit" value="new paste" />
- <Button
- className="lt-shadow lt-hover"
- type="button"
- onClick={this.togglePreview} >
- preview
- </Button>
- <br />
- <Error ref={this.ErrorLabel} />
- </form>
- );
- }
+ return (
+ <form onSubmit={handleSubmit}>
+ <PasteModal hash={hash} />
+ <TitleInput
+ onChange={(e) => {setTitle(e.target.value)}}
+ value={title}
+ maxLength="100"
+ id="titleInput" />
+ {renderPreview()}
+ <OptionsContainer
+ pass={pass}
+ expiry={expiry}
+ lang={language}
+ onPassChange={(e) => { setPass(e.target.value) }}
+ onLangChange={(e) => { setLanguage(e.target.value) }}
+ onExpiryChange={(e) => { setExpiry(e.target.value) }} />
+ <input className="lt-button lt-shadow lt-hover" type="submit" value="new paste" />
+ <Button
+ className="lt-shadow lt-hover"
+ type="button"
+ onClick={() => setIsPreview(!isPreview)}>
+ preview
+ </Button>
+ <br />
+ <Error ref={ErrorLabel} />
+ </form>
+ );
}
export default NewPaste \ No newline at end of file
diff --git a/frontend/src/components/Options.js b/frontend/src/components/Options.js
index 26391c1..09f92f3 100644
--- a/frontend/src/components/Options.js
+++ b/frontend/src/components/Options.js
@@ -14,25 +14,23 @@ const Flex = styled.div`
}
`
-class OptionsContainer extends React.Component {
- render() {
- return (
- <Flex>
- <PassInput
- value={this.props.pass}
- onChange={this.props.onChange}
- id="passwordInput" />
- <LangInput
- value={this.props.lang}
- onChange={this.props.onChange}
- id="langInput" />
- <ExpiryInput
- value={this.props.expiry}
- onChange={this.props.onChange}
- id="expiryInput" />
- </Flex>
- );
- }
+const OptionsContainer = ({pass, lang, expiry, onPassChange, onLangChange, onExpiryChange}) => {
+ return (
+ <Flex>
+ <PassInput
+ value={pass}
+ onChange={onPassChange}
+ id="passwordInput" />
+ <LangInput
+ value={lang}
+ onChange={onLangChange}
+ id="langInput" />
+ <ExpiryInput
+ value={expiry}
+ onChange={onExpiryChange}
+ id="expiryInput" />
+ </Flex>
+ );
}
export default OptionsContainer \ No newline at end of file
diff --git a/frontend/src/components/PasteInfo.js b/frontend/src/components/PasteInfo.js
index 8669b20..bab7e23 100644
--- a/frontend/src/components/PasteInfo.js
+++ b/frontend/src/components/PasteInfo.js
@@ -16,7 +16,8 @@ const StyledDiv = styled.div`
const Button = styled.button`
margin-right: 0 !important;
margin-left: 2em !important;
- height: calc(16px + 1.6em + 2px);
+ height: calc(16px + 1.6em);
+ margin-top: 1.6em !important;
`
const SpacedText = styled.span`
diff --git a/frontend/src/components/ViewPaste.js b/frontend/src/components/ViewPaste.js
index 117bb18..ebdc950 100644
--- a/frontend/src/components/ViewPaste.js
+++ b/frontend/src/components/ViewPaste.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { useEffect, useState, useRef } from 'react';
import Error from './Err';
import { TitleInput } from './Inputs';
import CodeRenderer from './renderers/Code'
@@ -8,173 +8,139 @@ import { FetchPaste, FetchPasswordPaste } from '../helpers/httpHelper'
import { LANGS } from './renderers/Code'
import RenderDispatch from './renderers/RenderDispatch'
-class ViewPaste extends React.Component {
-
- constructor(props) {
- super(props);
- this.state = {
- title: 'untitled paste',
- content: '',
- hasPass: false,
- enteredPass: '',
- validPass: false,
- expiry: 'no expiry',
- error: '',
- passError: '',
- theme: 'atom',
- inRenderMode: false,
- language: LANGS.raw,
- };
-
- this.handleChange = this.handleChange.bind(this);
- this.typedPass = this.typedPass.bind(this);
- this.toggleRender = this.toggleRender.bind(this);
- this.validatePass = this.validatePass.bind(this);
- this.ErrorLabel = React.createRef();
- this.PasswordModal = React.createRef();
- this.componentRef = React.createRef();
- }
-
- handleChange(event) {
- const target = event.target;
- const name = target.name;
-
- this.setState({
- [name]: target.value
- });
- }
-
- typedPass(event) {
- this.setState({ enteredPass: event.target.value });
- }
-
- toggleRender() {
- this.setState({ isRenderMode: !this.state.isRenderMode });
- }
+function fmtDateStr(dateString) {
+ const d = new Date(dateString)
+ const options = { hour: '2-digit', minute: '2-digit', year: 'numeric', month: 'long', day: 'numeric' }
+ return d.toLocaleDateString("en-US", options).toLocaleLowerCase()
+}
- validatePass(pass) {
- FetchPasswordPaste(this.props.hash, pass)
+const ViewPaste = (props) => {
+ const [title, setTitle] = useState('untitled paste');
+ const [content, setContent] = useState('');
+ const [hasPass, setHasPass] = useState(false);
+ const [enteredPass, setEnteredPass] = useState('');
+ const [validPass, setValidPass] = useState(false);
+ const [expiry, setExpiry] = useState('');
+ const [theme, setTheme] = useState('atom');
+ const [isRenderMode, setIsRenderMode] = useState(false);
+ const [language, setLanguage] = useState(LANGS.raw);
+
+ const ErrorLabelRef = useRef(null);
+ const PasswordModalRef = useRef(null);
+ const ComponentRef = useRef(null);
+
+ function validatePass(pass, onErrorCallBack) {
+ FetchPasswordPaste(props.hash, pass)
.then((response) => {
- this.setState({ validPass: true })
- this.setStateFromData(response.data)
+ setValidPass(true)
+ 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")
+ onErrorCallBack("incorrect pass")
return
}
// otherwise, just log it lmao
if (resp !== undefined) {
const errTxt = `${resp.status}: ${resp.data}`
- this.ErrorLabel.current.showMessage(errTxt)
+ onErrorCallBack(errTxt)
} else {
// some weird err (e.g. network)
- this.ErrorLabel.current.showMessage(error)
+ onErrorCallBack(error)
}
});
}
- render() {
-
- var display
- if (this.state.isRenderMode) {
- display =
- <RenderDispatch
- language={this.state.language}
- content={this.state.content}
- ref={this.componentRef}
- />
- } else {
- display =
- <CodeRenderer
- content={this.state.content}
- lang={this.state.language}
- theme={this.state.theme}
- ref={this.componentRef}
- id="pasteInput" />
- }
-
- return (
- <div>
- <PasswordModal
- ref={this.PasswordModal}
- hasPass={this.state.hasPass}
- validPass={this.state.validPass}
- value={this.state.enteredPass}
- onChange={this.typedPass}
- validateCallback={this.validatePass} />
- <TitleInput
- value={this.state.title}
- id="titleInput"
- readOnly />
- {display}
- <PasteInfo
- hash={this.props.hash}
- lang={this.state.language}
- theme={this.state.theme}
- expiry={this.state.expiry}
- toggleRenderCallback={this.toggleRender}
- isRenderMode={this.state.isRenderMode}
- onChange={this.handleChange}
- compref={this.componentRef}
- err={<Error ref={this.ErrorLabel} />}
- />
- </div>
- );
- }
-
- fmtDateStr(dateString) {
- const d = new Date(dateString)
- const options = { hour: '2-digit', minute: '2-digit', year: 'numeric', month: 'long', day: 'numeric' }
- return d.toLocaleDateString("en-US", options).toLocaleLowerCase()
- }
-
- setStateFromData(data) {
- console.log(data)
- this.setState({
- title: data.title,
- content: data.content,
- language: data.language,
- expiry: this.fmtDateStr(data.expiry),
- })
+ function setStateFromData(data) {
+ setTitle(data.title)
+ setContent(data.content)
+ setLanguage(data.language)
+ setExpiry(fmtDateStr(data.expiry))
}
- componentDidMount() {
- FetchPaste(this.props.hash)
+ useEffect(() => {
+ FetchPaste(props.hash)
.then((response) => {
const data = response.data
- this.setStateFromData(data)
+ setStateFromData(data)
}).catch((error) => {
const resp = error.response
// network err
if (!resp) {
- this.ErrorLabel.current.showMessage(error)
+ ErrorLabelRef.current.showMessage(error)
return
}
// catch 401 unauth (password protected)
if (resp.status === 401) {
- this.setState({hasPass: true})
+ setHasPass(true)
return
}
// some weird err
if (resp !== undefined) {
const errTxt = `${resp.status}: ${resp.data}`
- this.ErrorLabel.current.showMessage(errTxt, -1)
+ ErrorLabelRef.current.showMessage(errTxt, -1)
return
}
// some weird err (e.g. network)
- this.ErrorLabel.current.showMessage(error, -1)
+ ErrorLabelRef.current.showMessage(error, -1)
})
+ }, [props.hash])
+
+ function getDisplay() {
+ if (isRenderMode) {
+ return (
+ <RenderDispatch
+ language={language}
+ content={content}
+ ref={ComponentRef}
+ />
+ )
+ } else {
+ return (
+ <CodeRenderer
+ content={content}
+ lang={language}
+ theme={theme}
+ ref={ComponentRef}
+ id="pasteInput" />
+ )
+ }
}
+
+ return (
+ <div>
+ <PasswordModal
+ ref={PasswordModalRef}
+ hasPass={hasPass}
+ validPass={validPass}
+ value={enteredPass}
+ onChange={(e) => setEnteredPass(e.target.value)}
+ validateCallback={validatePass} />
+ <TitleInput
+ value={title}
+ id="titleInput"
+ readOnly />
+ {getDisplay()}
+ <PasteInfo
+ hash={props.hash}
+ lang={language}
+ theme={theme}
+ expiry={expiry}
+ toggleRenderCallback={() => setIsRenderMode(!isRenderMode)}
+ isRenderMode={isRenderMode}
+ onChange={(e) => setTheme(e.target.value)}
+ compref={ComponentRef}
+ err={<Error ref={ErrorLabelRef} />}
+ />
+ </div>
+ );
}
export default ViewPaste \ No newline at end of file
diff --git a/frontend/src/components/decorators/CharLimit.js b/frontend/src/components/decorators/CharLimit.js
index 623b378..5a6fdca 100644
--- a/frontend/src/components/decorators/CharLimit.js
+++ b/frontend/src/components/decorators/CharLimit.js
@@ -26,12 +26,10 @@ const Chars = styled.p`
`};
`;
-class CharLimit extends React.Component {
- render() {
- return (
- <Chars {...this.props} >{this.props.maxLength - this.props.content.length}/{this.props.maxLength}</Chars>
- );
- }
+const CharLimit = (props) => {
+ return (
+ <Chars {...props} >{props.maxLength - props.content.length}/{props.maxLength}</Chars>
+ );
}
export default CharLimit \ No newline at end of file
diff --git a/frontend/src/components/decorators/FloatingLabel.js b/frontend/src/components/decorators/FloatingLabel.js
index e1fc0ba..ef56b44 100644
--- a/frontend/src/components/decorators/FloatingLabel.js
+++ b/frontend/src/components/decorators/FloatingLabel.js
@@ -17,18 +17,16 @@ const StyledLabel = styled.label`
`};
`
-class FloatingLabel extends React.Component {
- render() {
- return (
- <StyledLabel
- label={this.props.label}
- value={this.props.value}
- className={this.props.id}
- htmlFor={this.props.id}>
- {this.props.label}
- </StyledLabel>
- );
- }
+const FloatingLabel = (props) => {
+ return (
+ <StyledLabel
+ label={props.label}
+ value={props.value}
+ className={props.id}
+ htmlFor={props.id}>
+ {props.label}
+ </StyledLabel>
+ );
}
export default FloatingLabel \ No newline at end of file
diff --git a/frontend/src/components/modals/PasswordModal.js b/frontend/src/components/modals/PasswordModal.js
index 527fc54..bf373cc 100644
--- a/frontend/src/components/modals/PasswordModal.js
+++ b/frontend/src/components/modals/PasswordModal.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { useRef } from 'react';
import Modal from 'react-modal';
import { PassInput } from '../Inputs'
import { RightPad, LeftPad, ModalHeader, Padding } from './shared'
@@ -15,49 +15,39 @@ const modalStyles = {
}
};
-class PasswordModal extends React.Component {
+const PasswordModal = (props) => {
+ const ErrorLabel = useRef(null);
+ Modal.setAppElement('body');
- componentWillMount() {
- Modal.setAppElement('body');
+ function submitPassword(e) {
+ e.preventDefault();
+ const password = props.value
+ props.validateCallback(password, ErrorLabel.current.showMessage)
}
- 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"
- >
- <form onSubmit={this.submitPassword}>
- <LeftPad>
- <ModalHeader><span role="img" aria-label="warning">🚧&nbsp;</span>err: password protected</ModalHeader>
- </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" />
- <Padding />
- <Error ref={this.ErrorLabel} />
- </LeftPad>
- </form>
- </Modal>
- );
- }
+ return(
+ <Modal
+ isOpen={props.hasPass && !props.validPass}
+ style={modalStyles}
+ contentLabel="enter paste password"
+ >
+ <form onSubmit={submitPassword}>
+ <LeftPad>
+ <ModalHeader><span role="img" aria-label="warning">🚧&nbsp;</span>err: password protected</ModalHeader>
+ </LeftPad>
+ <RightPad>
+ <PassInput
+ value={props.value}
+ onChange={props.onChange} />
+ </RightPad>
+ <LeftPad>
+ <input className="lt-button lt-shadow lt-hover" type="submit" value="continue" />
+ <Padding />
+ <Error ref={ErrorLabel} />
+ </LeftPad>
+ </form>
+ </Modal>
+ );
}
export default PasswordModal \ No newline at end of file
diff --git a/frontend/src/components/renderers/Latex.js b/frontend/src/components/renderers/Latex.js
index dcb9ea3..fd3d4e2 100644
--- a/frontend/src/components/renderers/Latex.js
+++ b/frontend/src/components/renderers/Latex.js
@@ -8,37 +8,35 @@ const StyledInlineLatex = styled.div`
margin-bottom: 1em;
`
-class Latex extends React.Component {
- render() {
- // split by \begin{...} and \end{...} flags
- const els = this.props.content.split(/(\\begin\{.*\}[\s\S]*?\\end\{.*\})/gm).map(line => {
- // line doesnt start with \begin{...}, safe to split on \\
- if (!line.match(/^(\\begin\{.*\})/)) {
- return line.split("\\\\")
- } else {
- return line
- }
- }).flat()
-
- // if <=1 lines, just render block
- if (els.length <= 1) {
- return (
- <BlockMath>
- {this.props.content}
- </BlockMath>
- );
+const Latex = (props) => {
+ // split by \begin{...} and \end{...} flags
+ const els = props.content.split(/(\\begin\{.*\}[\s\S]*?\\end\{.*\})/gm).map(line => {
+ // line doesnt start with \begin{...}, safe to split on \\
+ if (!line.match(/^(\\begin\{.*\})/)) {
+ return line.split("\\\\")
} else {
- // new inline block for every line
- const blocks = els.map(line =>
- <StyledInlineLatex>
- <InlineMath>
- {line}
- </InlineMath>
- </StyledInlineLatex>
- )
-
- return blocks;
+ return line
}
+ }).flat()
+
+ // if <=1 lines, just render block
+ if (els.length <= 1) {
+ return (
+ <BlockMath>
+ {props.content}
+ </BlockMath>
+ );
+ } else {
+ // new inline block for every line
+ const blocks = els.map(line =>
+ <StyledInlineLatex>
+ <InlineMath>
+ {line}
+ </InlineMath>
+ </StyledInlineLatex>
+ )
+
+ return blocks;
}
}
diff --git a/frontend/src/components/renderers/Raw.js b/frontend/src/components/renderers/Raw.js
index 7f8e7c1..d4dc830 100644
--- a/frontend/src/components/renderers/Raw.js
+++ b/frontend/src/components/renderers/Raw.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { useState, useEffect } from 'react';
import styled from 'styled-components'
import { FetchPaste } from '../../helpers/httpHelper'
@@ -10,48 +10,37 @@ const RawText = styled.pre`
padding: 0 1em;
`
-class Raw extends React.Component {
-
- constructor(props) {
- super(props);
- this.state = {
- content: '',
- };
- }
-
- render() {
- return (
- <RawText>
- {this.state.content}
- </RawText>
- );
- }
-
- componentDidMount() {
- FetchPaste(this.props.hash)
- .then((response) => {
- const data = response.data
- this.setState({ content: data.content })
- }).catch((error) => {
- const resp = error.response
-
- // catch 401 unauth (password protected)
- if (resp.status === 401) {
- this.setState({ content: 'err: password protected' })
- return
- }
-
- // some weird err
- if (resp !== undefined) {
- const errTxt = `${resp.statusText}: ${resp.data}`
- this.setState({ content: errTxt })
- return
- }
-
- // some weird err (e.g. network)
- this.setState({ content: error })
- })
- }
+const Raw = ({hash}) => {
+ const [content, setContent] = useState('');
+
+ useEffect(() => {
+ FetchPaste(hash)
+ .then((response) => {
+ const data = response.data
+ setContent(data.content)
+ }).catch((error) => {
+ const resp = error.response
+
+ // catch 401 unauth (password protected)
+ if (resp.status === 401) {
+ setContent('err: password protected')
+ return
+ }
+
+ // some weird err
+ if (resp !== undefined) {
+ const errTxt = `${resp.statusText}: ${resp.data}`
+ setContent(errTxt)
+ return
+ }
+
+ // some weird err (e.g. network)
+ setContent(error)
+ })}, [hash])
+
+ return (
+ <RawText>{content}</RawText>
+ );
}
export default Raw \ No newline at end of file
diff --git a/frontend/src/helpers/httpHelper.js b/frontend/src/helpers/httpHelper.js
index ca77ed7..99b9513 100644
--- a/frontend/src/helpers/httpHelper.js
+++ b/frontend/src/helpers/httpHelper.js
@@ -21,13 +21,13 @@ export function FetchPasswordPaste(hash, pass) {
})
}
-export function PostNewPaste(state) {
+export function PostNewPaste(title, content, language, pass, expiry) {
var bodyFormData = new FormData();
- bodyFormData.set('title', state.title);
- bodyFormData.set('content', state.content);
- bodyFormData.set('language', state.language);
- bodyFormData.set('password', state.pass);
- bodyFormData.set('expiry', parseExpiry(state.expiry));
+ bodyFormData.set('title', title);
+ bodyFormData.set('content', content);
+ bodyFormData.set('language', language);
+ bodyFormData.set('password', pass);
+ bodyFormData.set('expiry', parseExpiry(expiry));
return axios({
method: 'post',