aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjackyzha0 <[email protected]>2021-04-11 10:27:27 -0700
committerjackyzha0 <[email protected]>2021-04-11 10:27:27 -0700
commit0144bfc9cc6c616a00a8171f3950a75ec948427e (patch)
tree101d6c12471d411e9266cffa8e90176aff2e6fdb
parentbase next bump (diff)
downloadctrl-v-0144bfc9cc6c616a00a8171f3950a75ec948427e.tar.xz
ctrl-v-0144bfc9cc6c616a00a8171f3950a75ec948427e.zip
base next refactor
-rw-r--r--.gitignore1
-rw-r--r--frontend/.babelrc15
-rw-r--r--frontend/package.json4
-rw-r--r--frontend/src/App.js5
-rw-r--r--frontend/src/components/PasteInfo.js6
-rw-r--r--frontend/src/components/modals/PasteModal.js8
-rw-r--r--frontend/src/components/pages/NewPaste.js150
-rw-r--r--frontend/src/components/pages/Raw.js16
-rw-r--r--frontend/src/components/pages/ViewPaste.js65
-rw-r--r--frontend/src/components/renderers/Code.js2
-rw-r--r--frontend/src/components/renderers/InlineCode.js2
-rw-r--r--frontend/src/css/index.css150
-rw-r--r--frontend/src/http/shared.js (renamed from frontend/src/components/hooks/shared.js)0
-rw-r--r--frontend/src/http/useFetchPaste.js (renamed from frontend/src/components/hooks/useFetchPaste.js)8
-rw-r--r--frontend/src/pages/[hash].js65
-rw-r--r--frontend/src/pages/_app.js23
-rw-r--r--frontend/src/pages/_document.js30
-rw-r--r--frontend/src/pages/index.js150
-rw-r--r--frontend/src/pages/raw/index.js14
-rw-r--r--frontend/src/theme/GlobalStyle.js10
-rw-r--r--frontend/src/theme/ThemeProvider.js4
-rw-r--r--frontend/src/theme/style.css150
-rw-r--r--frontend/yarn.lock99
23 files changed, 488 insertions, 489 deletions
diff --git a/.gitignore b/.gitignore
index cac890e..447e98e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,7 @@ backend/.env
frontend/node_modules
frontend/build
frontend/.pnp
+frontend/.next
.pnp.js
npm-debug.log*
diff --git a/frontend/.babelrc b/frontend/.babelrc
new file mode 100644
index 0000000..4722ed5
--- /dev/null
+++ b/frontend/.babelrc
@@ -0,0 +1,15 @@
+{
+ "presets": [
+ "next/babel"
+ ],
+ "plugins": [
+ [
+ "styled-components",
+ {
+ "ssr": true,
+ "displayName": true,
+ "preprocess": false
+ }
+ ]
+ ]
+}
diff --git a/frontend/package.json b/frontend/package.json
index e50ce30..1851591 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -11,7 +11,6 @@
"react-katex": "^2.0.2",
"react-markdown": "^4.3.1",
"react-modal": "^3.11.2",
- "react-router-dom": "^5.2.0",
"react-simple-code-editor": "^0.11.0",
"react-syntax-highlighter": "^12.2.1",
"react-syntax-highlighter-virtualized-renderer": "^1.1.0",
@@ -37,5 +36,8 @@
"last 1 firefox version",
"last 1 safari version"
]
+ },
+ "devDependencies": {
+ "babel-plugin-styled-components": "^1.12.0"
}
}
diff --git a/frontend/src/App.js b/frontend/src/App.js
index 0a47baa..f00397f 100644
--- a/frontend/src/App.js
+++ b/frontend/src/App.js
@@ -13,10 +13,7 @@ import ThemeProvider from './theme/ThemeProvider'
import GlobalStyle from './theme/GlobalStyle'
import {Watermark} from "./components/Watermark";
-const Main = styled.div`
- margin-top: 10vh;
- padding: 0 20vw 30px 20vw;
-`
+
const GetPasteWithParam = () => {
let { hash } = useParams();
diff --git a/frontend/src/components/PasteInfo.js b/frontend/src/components/PasteInfo.js
index 6ab5b19..2f912fb 100644
--- a/frontend/src/components/PasteInfo.js
+++ b/frontend/src/components/PasteInfo.js
@@ -1,8 +1,8 @@
import React from 'react';
import styled from 'styled-components'
-import { useHistory } from 'react-router-dom';
import { Theme } from './Inputs'
import {Button} from "./Common/Button";
+import {useRouter} from "next/router";
const Bold = styled.span`
font-weight: 700
@@ -28,10 +28,10 @@ const Flex = styled.div`
`
const PasteInfo = (props) => {
- const history = useHistory();
+ const router = useRouter()
const redirRaw = () => {
const redirUrl = `/raw/${props.hash}`
- history.push(redirUrl);
+ router.push(redirUrl);
}
const renderable = () => {
diff --git a/frontend/src/components/modals/PasteModal.js b/frontend/src/components/modals/PasteModal.js
index e7dbed2..a0fd309 100644
--- a/frontend/src/components/modals/PasteModal.js
+++ b/frontend/src/components/modals/PasteModal.js
@@ -1,21 +1,21 @@
import React from 'react';
import Modal from 'react-modal';
import {Form, ModalHeader, modalStyles} from './shared'
-import { useHistory } from 'react-router-dom';
+import { useRouter } from 'next/router'
import { Text } from '../Inputs'
import { useClipboard } from 'use-clipboard-copy';
import {Button} from "../Common/Button";
const PasteModal = (props) => {
- const history = useHistory();
- const fullURL = `${window.location.origin}/${props.hash}`;
+ const fullURL = `https://ctrl-v.app/${props.hash}`;
const clipboard = useClipboard({ copiedTimeout: 3000 });
Modal.setAppElement('body');
+ const router = useRouter()
const redir = (e) => {
e.preventDefault();
const redirUrl = `/${props.hash}`
- history.push(redirUrl);
+ router.push(redirUrl);
}
return (
diff --git a/frontend/src/components/pages/NewPaste.js b/frontend/src/components/pages/NewPaste.js
deleted file mode 100644
index 19161da..0000000
--- a/frontend/src/components/pages/NewPaste.js
+++ /dev/null
@@ -1,150 +0,0 @@
-import React, { useEffect, useState, useRef } from 'react';
-import { Text, Code } from '../Inputs'
-import OptionsContainer from '../Options'
-import Error from '../Err'
-import PasteModal from '../modals/PasteModal'
-import styled from 'styled-components'
-import CodeRenderer from '../renderers/Code'
-import Latex from '../renderers/Latex'
-import Markdown from '../renderers/Markdown'
-import {Button, SubmitButton} from "../Common/Button";
-import {newPaste} from "../hooks/shared";
-
-const Flex = styled.div`
- display: flex;
- flex-direction: row;
-`
-
-const FlexLeft = styled.div`
- flex: 0 0 calc(50% - 1em - 2px);
-`
-
-const FlexRight = styled.div`
- flex: 0 0 50%;
- max-width: calc(50% - 1em + 2px);
- margin-left: 2em;
-`
-
-const PreviewWrapper = styled.div`
- margin: 2em;
-`
-
-const NewPaste = () => {
- const [title, setTitle] = useState('');
- const [content, setContent] = useState('');
- const [pass, setPass] = useState('');
- const [language, setLanguage] = useState('detect');
- const [expiry, setExpiry] = useState('');
- const [hash, setHash] = useState('');
- const [isPreview, setIsPreview] = useState(false);
- const ErrorLabel = useRef(null);
-
- useEffect(() => {
- document.title = title === "" ? `ctrl-v` : `ctrl-v | ${title}`;
- }, [title])
-
- function handleSubmit(e) {
- e.preventDefault();
-
- // prevent resubmission
- if (!hash) {
- newPaste({title, content, language, pass, expiry})
- .then(resp => {setHash(resp.data.hash)})
- .catch((error) => {
- const resp = error.response
-
- // some weird err (e.g. network)
- if (!resp) {
- ErrorLabel.current.showMessage(error)
- return
- }
-
- // some weird err
- const errTxt = `${resp.status}: ${resp.data}`
- ErrorLabel.current.showMessage(errTxt)
- });
- }
- }
-
- function renderPreview() {
- const pasteInput = <Code
- setContentCallback={setContent}
- content={content}
- maxLength="100000" />
-
- if (isPreview) {
- var preview
- switch (language) {
- case 'latex':
- preview =
- <PreviewWrapper>
- <Latex
- content={content} />
- </PreviewWrapper>
- break
- case 'markdown':
- preview =
- <PreviewWrapper className='md' >
- <Markdown
- content={content} />
- </PreviewWrapper>
- break
- default:
- preview =
- <CodeRenderer
- lang={language}
- theme='atom'
- content={content} />
- }
-
- return (
- <Flex>
- <FlexLeft>
- {pasteInput}
- </FlexLeft>
- <FlexRight className='preview' >
- {preview}
- </FlexRight>
- </Flex>
- );
- } else {
- return (
- pasteInput
- );
- }
- }
-
- return (
- <form onSubmit={handleSubmit}>
- <PasteModal hash={hash} />
- <Text
- label="title"
- onChange={(e) => {setTitle(e.target.value)}}
- value={title}
- autoFocus
- 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) }} />
- <div>
- <SubmitButton type="submit" value="new paste" />
- {language !== 'detect' && <Button
- secondary
- type="button"
- onClick={() => setIsPreview(!isPreview)}>
- preview
- </Button>}
- </div>
- <br />
- <Error ref={ErrorLabel} />
- </form>
- );
-}
-
-export default NewPaste \ No newline at end of file
diff --git a/frontend/src/components/pages/Raw.js b/frontend/src/components/pages/Raw.js
deleted file mode 100644
index 23ef6bf..0000000
--- a/frontend/src/components/pages/Raw.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import React from 'react';
-import styled from 'styled-components'
-import {CodeLike} from "../Common/mixins";
-import useFetchPaste from "../hooks/useFetchPaste";
-
-const RawText = styled.pre`
- ${CodeLike}
- padding: 0 1em;
-`
-
-const Raw = ({hash}) => {
- const { err, result } = useFetchPaste(hash)
- return <RawText>{result?.content || err}</RawText>
-}
-
-export default Raw \ No newline at end of file
diff --git a/frontend/src/components/pages/ViewPaste.js b/frontend/src/components/pages/ViewPaste.js
deleted file mode 100644
index bc61314..0000000
--- a/frontend/src/components/pages/ViewPaste.js
+++ /dev/null
@@ -1,65 +0,0 @@
-import React, { useEffect, useState, useRef } from 'react';
-import Error from '../Err';
-import { Text } from '../Inputs';
-import CodeRenderer from '../renderers/Code'
-import PasteInfo from '../PasteInfo';
-import PasswordModal from '../modals/PasswordModal'
-import RenderDispatch from '../renderers/RenderDispatch'
-import useFetchPaste from "../hooks/useFetchPaste";
-
-const ViewPaste = (props) => {
- const { err, requiresAuth, validPass, getWithPassword, result } = useFetchPaste(props.hash)
- const {content, language, expiry, title} = result ?? {}
- const [theme, setTheme] = useState('atom');
- const [isRenderMode, setIsRenderMode] = useState(false);
- const [enteredPass, setEnteredPass] = useState('');
- const ErrorLabelRef = useRef(null);
-
- if (err) {
- ErrorLabelRef.current.showMessage(err, -1)
- }
-
- useEffect(() => {
- setIsRenderMode(language === 'latex' || language === 'markdown')
- }, [language])
-
- function getDisplay() {
- return isRenderMode ? <RenderDispatch
- language={language}
- content={content}
- /> : <CodeRenderer
- content={content}
- lang={language}
- theme={theme}
- id="pasteInput" />
- }
-
- return (
- <div>
- <PasswordModal
- hasPass={requiresAuth}
- validPass={validPass}
- value={enteredPass}
- onChange={(e) => setEnteredPass(e.target.value)}
- validateCallback={getWithPassword} />
- <Text
- label="title"
- 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)}
- err={<Error ref={ErrorLabelRef} />}
- />
- </div>
- );
-}
-
-export default ViewPaste \ No newline at end of file
diff --git a/frontend/src/components/renderers/Code.js b/frontend/src/components/renderers/Code.js
index 29531fc..85fd5ab 100644
--- a/frontend/src/components/renderers/Code.js
+++ b/frontend/src/components/renderers/Code.js
@@ -1,7 +1,7 @@
import React from 'react';
import SyntaxHighlighter from 'react-syntax-highlighter';
import virtualizedRenderer from 'react-syntax-highlighter-virtualized-renderer';
-import { atomOneLight, ascetic, atomOneDark, dracula, ocean } from 'react-syntax-highlighter/dist/esm/styles/hljs';
+import { atomOneLight, ascetic, atomOneDark, dracula, ocean } from 'react-syntax-highlighter/dist/cjs/styles/hljs';
import styled from 'styled-components'
import {Border, CodeLike, DropShadow, Rounded} from "../Common/mixins";
diff --git a/frontend/src/components/renderers/InlineCode.js b/frontend/src/components/renderers/InlineCode.js
index 44a3f58..2156503 100644
--- a/frontend/src/components/renderers/InlineCode.js
+++ b/frontend/src/components/renderers/InlineCode.js
@@ -1,6 +1,6 @@
import React from 'react';
import SyntaxHighlighter from 'react-syntax-highlighter';
-import { atomOneDark } from 'react-syntax-highlighter/dist/esm/styles/hljs';
+import { atomOneDark } from 'react-syntax-highlighter/dist/cjs/styles/hljs';
import { LANGS } from './Code'
const MarkdownCodeRenderer = ({language, value}) => {
diff --git a/frontend/src/css/index.css b/frontend/src/css/index.css
deleted file mode 100644
index 0340813..0000000
--- a/frontend/src/css/index.css
+++ /dev/null
@@ -1,150 +0,0 @@
-@media all and (max-width: 1000px) {
- .lt-content-column {
- padding: 0 calc(5vw + 1em) 0 5vw !important;
- }
-}
-
-form {
- width: 100%;
-}
-
-textarea, input[type=text], input[type=password], .Dropdown-root {
- width: 100%;
- font-family: 'JetBrains Mono', monospace;
- font-size: 0.8em;
- padding: calc(0.8em - 1px);
- border-radius: 3px;
- border: 1px solid #565656;
- outline: none;
- margin: 1.7em 0;
-}
-
-/* fix weird symbol height in render mode */
-.large-op {
- transform: translateY(-0.55em);
-}
-
-.small-op {
- transform: translateY(-0.1em);
-}
-
-code, pre {
- background: #00000000;
- font-family: 'JetBrains Mono', monospace;
- padding: initial;
- border-radius: 3px;
- outline: none;
-}
-
-.Dropdown-root {
- cursor: pointer;
-}
-
-.Dropdown-root:hover, .Dropdown-root.is-open {
- opacity: 1;
-}
-
-.Dropdown-root + label {
- top: 0.5em;
- opacity: 0;
-}
-
-.Dropdown-root.is-open + label, .Dropdown-root:hover + label {
- opacity: 1;
- top: -0.1em;
-}
-
-.Dropdown-placeholder {
- width: 5.5em;
-}
-
-.Dropdown-menu {
- border-top: 1px solid #111111;
- margin-top: 0.5em;
- bottom: auto;
-}
-
-.Dropdown-option {
- margin-top: 0.5em;
- transition: all 0.5s cubic-bezier(.25,.8,.25,1);
-}
-
-.Dropdown-option:hover {
- font-weight: 700;
- opacity: 0.4;
-}
-
-textarea, input[type=text], input[type=password], .Dropdown-root {
- opacity: 0.5;
- transition: opacity 0.5s cubic-bezier(.25,.8,.25,1);
-}
-
-textarea:focus, input[type=text]:focus, input[type=password]:focus {
- opacity: 1;
-}
-
-input[type=password] {
- font-weight: 700;
-}
-
-textarea {
- height: max(40vh, 100%);
- resize: vertical;
- min-height: 40vh;
-}
-
-a {
- color: #111111;
-}
-
-input[type=submit], button[type=submit] {
- font-family: 'JetBrains Mono', serif;
- font-weight: 700;
- color: #faf9f5;
- background-color: #111111;
- padding: 0.8em 2em;
- margin: 2em 0;
- outline: 0;
-}
-
-button[type=button] {
- font-family: 'JetBrains Mono', serif;
- font-weight: 700;
- width: 8em;
- padding: calc(0.8em - 1px) 1.5em;
- border-radius: 3px;
- color: #111111;
- background-color: #faf9f5;
- border: 1px solid #565656;
- outline: none;
- margin: 2em 2em;
-}
-
-
-/* fixing markdown renderer */
-.md h3 {
- font-weight: bold;
-}
-
-.md hr {
- border-top: 1px solid #000;
- border-style: solid;
-}
-
-.md code {
- background: #00000008;
- font-size: 0.8em;
-}
-
-.md pre {
- padding: 0.7em;
- background: #00000008;
-}
-
-.md pre > code {
- background: none;
-}
-
-.md table {
- width: 100%;
-} \ No newline at end of file
diff --git a/frontend/src/components/hooks/shared.js b/frontend/src/http/shared.js
index 00d41e9..00d41e9 100644
--- a/frontend/src/components/hooks/shared.js
+++ b/frontend/src/http/shared.js
diff --git a/frontend/src/components/hooks/useFetchPaste.js b/frontend/src/http/useFetchPaste.js
index c394f5b..ba482f9 100644
--- a/frontend/src/components/hooks/useFetchPaste.js
+++ b/frontend/src/http/useFetchPaste.js
@@ -1,8 +1,8 @@
import {useEffect, useState} from 'react'
import {fetchPaste, fmtDateStr} from './shared'
-import {LANGS} from "../renderers/Code";
+import {LANGS} from "../components/renderers/Code";
-export default (id) => {
+const useFetchPaste = (id) => {
const [loading, setLoading] = useState(true)
const [err, setErr] = useState()
const [requiresAuth, setRequiresAuth] = useState(false)
@@ -64,4 +64,6 @@ export default (id) => {
}, [id])
return { loading, err, requiresAuth, validPass, getWithPassword, result }
-} \ No newline at end of file
+}
+
+export default useFetchPaste \ No newline at end of file
diff --git a/frontend/src/pages/[hash].js b/frontend/src/pages/[hash].js
new file mode 100644
index 0000000..27f808b
--- /dev/null
+++ b/frontend/src/pages/[hash].js
@@ -0,0 +1,65 @@
+import React, { useEffect, useState, useRef } from 'react';
+import Error from '../components/Err';
+import { Text } from '../components/Inputs';
+import CodeRenderer from '../components/renderers/Code'
+import PasteInfo from '../components/PasteInfo';
+import PasswordModal from '../components/modals/PasswordModal'
+import RenderDispatch from '../components/renderers/RenderDispatch'
+import useFetchPaste from "../http/useFetchPaste";
+
+const ViewPaste = (props) => {
+ const { err, requiresAuth, validPass, getWithPassword, result } = useFetchPaste(props.hash)
+ const {content, language, expiry, title} = result ?? {}
+ const [theme, setTheme] = useState('atom');
+ const [isRenderMode, setIsRenderMode] = useState(false);
+ const [enteredPass, setEnteredPass] = useState('');
+ const ErrorLabelRef = useRef(null);
+
+ if (err) {
+ ErrorLabelRef.current.showMessage(err, -1)
+ }
+
+ useEffect(() => {
+ setIsRenderMode(language === 'latex' || language === 'markdown')
+ }, [language])
+
+ function getDisplay() {
+ return isRenderMode ? <RenderDispatch
+ language={language}
+ content={content}
+ /> : <CodeRenderer
+ content={content}
+ lang={language}
+ theme={theme}
+ id="pasteInput" />
+ }
+
+ return (
+ <div>
+ <PasswordModal
+ hasPass={requiresAuth}
+ validPass={validPass}
+ value={enteredPass}
+ onChange={(e) => setEnteredPass(e.target.value)}
+ validateCallback={getWithPassword} />
+ <Text
+ label="title"
+ 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)}
+ err={<Error ref={ErrorLabelRef} />}
+ />
+ </div>
+ );
+}
+
+export default ViewPaste \ No newline at end of file
diff --git a/frontend/src/pages/_app.js b/frontend/src/pages/_app.js
new file mode 100644
index 0000000..7e99f01
--- /dev/null
+++ b/frontend/src/pages/_app.js
@@ -0,0 +1,23 @@
+import React from 'react'
+import ThemeProvider from "../theme/ThemeProvider";
+import GlobalStyle from "../theme/GlobalStyle";
+import '../theme/style.css';
+import {Watermark} from "../components/Watermark";
+import styled from "styled-components";
+
+const Main = styled.div`
+ margin-top: 10vh;
+ padding: 0 20vw 30px 20vw;
+`
+
+const App = ({ Component, pageProps }) => (
+ <ThemeProvider>
+ <GlobalStyle />
+ <Watermark/>
+ <Main id="appElement">
+ <Component {...pageProps} />
+ </Main>
+ </ThemeProvider>
+)
+
+export default App \ No newline at end of file
diff --git a/frontend/src/pages/_document.js b/frontend/src/pages/_document.js
new file mode 100644
index 0000000..0cbd6a3
--- /dev/null
+++ b/frontend/src/pages/_document.js
@@ -0,0 +1,30 @@
+import Document from 'next/document'
+import { ServerStyleSheet } from 'styled-components'
+
+export default class StyledDocument extends Document {
+ static async getInitialProps(ctx) {
+ const sheet = new ServerStyleSheet()
+ const originalRenderPage = ctx.renderPage
+
+ try {
+ ctx.renderPage = () =>
+ originalRenderPage({
+ enhanceApp: (App) => (props) =>
+ sheet.collectStyles(<App {...props} />),
+ })
+
+ const initialProps = await Document.getInitialProps(ctx)
+ return {
+ ...initialProps,
+ styles: (
+ <>
+ {initialProps.styles}
+ {sheet.getStyleElement()}
+ </>
+ ),
+ }
+ } finally {
+ sheet.seal()
+ }
+ }
+} \ No newline at end of file
diff --git a/frontend/src/pages/index.js b/frontend/src/pages/index.js
new file mode 100644
index 0000000..141ebac
--- /dev/null
+++ b/frontend/src/pages/index.js
@@ -0,0 +1,150 @@
+import React, { useEffect, useState, useRef } from 'react';
+import { Text, Code } from '../components/Inputs'
+import OptionsContainer from '../components/Options'
+import Error from '../components/Err'
+import PasteModal from '../components/modals/PasteModal'
+import styled from 'styled-components'
+import CodeRenderer from '../components/renderers/Code'
+import Latex from '../components/renderers/Latex'
+import Markdown from '../components/renderers/Markdown'
+import {Button, SubmitButton} from "../components/Common/Button";
+import {newPaste} from "../http/shared";
+
+const Flex = styled.div`
+ display: flex;
+ flex-direction: row;
+`
+
+const FlexLeft = styled.div`
+ flex: 0 0 calc(50% - 1em - 2px);
+`
+
+const FlexRight = styled.div`
+ flex: 0 0 50%;
+ max-width: calc(50% - 1em + 2px);
+ margin-left: 2em;
+`
+
+const PreviewWrapper = styled.div`
+ margin: 2em;
+`
+
+const NewPaste = () => {
+ const [title, setTitle] = useState('');
+ const [content, setContent] = useState('');
+ const [pass, setPass] = useState('');
+ const [language, setLanguage] = useState('detect');
+ const [expiry, setExpiry] = useState('');
+ const [hash, setHash] = useState('');
+ const [isPreview, setIsPreview] = useState(false);
+ const ErrorLabel = useRef(null);
+
+ useEffect(() => {
+ document.title = title === "" ? `ctrl-v` : `ctrl-v | ${title}`;
+ }, [title])
+
+ function handleSubmit(e) {
+ e.preventDefault();
+
+ // prevent resubmission
+ if (!hash) {
+ newPaste({title, content, language, pass, expiry})
+ .then(resp => {setHash(resp.data.hash)})
+ .catch((error) => {
+ const resp = error.response
+
+ // some weird err (e.g. network)
+ if (!resp) {
+ ErrorLabel.current.showMessage(error)
+ return
+ }
+
+ // some weird err
+ const errTxt = `${resp.status}: ${resp.data}`
+ ErrorLabel.current.showMessage(errTxt)
+ });
+ }
+ }
+
+ function renderPreview() {
+ const pasteInput = <Code
+ setContentCallback={setContent}
+ content={content}
+ maxLength="100000" />
+
+ if (isPreview) {
+ var preview
+ switch (language) {
+ case 'latex':
+ preview =
+ <PreviewWrapper>
+ <Latex
+ content={content} />
+ </PreviewWrapper>
+ break
+ case 'markdown':
+ preview =
+ <PreviewWrapper className='md' >
+ <Markdown
+ content={content} />
+ </PreviewWrapper>
+ break
+ default:
+ preview =
+ <CodeRenderer
+ lang={language}
+ theme='atom'
+ content={content} />
+ }
+
+ return (
+ <Flex>
+ <FlexLeft>
+ {pasteInput}
+ </FlexLeft>
+ <FlexRight className='preview' >
+ {preview}
+ </FlexRight>
+ </Flex>
+ );
+ } else {
+ return (
+ pasteInput
+ );
+ }
+ }
+
+ return (
+ <form onSubmit={handleSubmit}>
+ <PasteModal hash={hash} />
+ <Text
+ label="title"
+ onChange={(e) => {setTitle(e.target.value)}}
+ value={title}
+ autoFocus
+ 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) }} />
+ <div>
+ <SubmitButton type="submit" value="new paste" />
+ {language !== 'detect' && <Button
+ secondary
+ type="button"
+ onClick={() => setIsPreview(!isPreview)}>
+ preview
+ </Button>}
+ </div>
+ <br />
+ <Error ref={ErrorLabel} />
+ </form>
+ );
+}
+
+export default NewPaste \ No newline at end of file
diff --git a/frontend/src/pages/raw/index.js b/frontend/src/pages/raw/index.js
new file mode 100644
index 0000000..86db3d4
--- /dev/null
+++ b/frontend/src/pages/raw/index.js
@@ -0,0 +1,14 @@
+import React from 'react';
+import useFetchPaste from "../../http/useFetchPaste";
+import { useRouter } from 'next/router'
+
+
+export default (req, res) => {
+ const router = useRouter()
+ const { hash } = router.query
+ const { err, result } = useFetchPaste(hash)
+ res.statusCode = 200
+ res.json({
+ text: 'Hello World! This is the Next.js starter kit :D',
+ })
+} \ No newline at end of file
diff --git a/frontend/src/theme/GlobalStyle.js b/frontend/src/theme/GlobalStyle.js
index 9fe80a5..94e4b57 100644
--- a/frontend/src/theme/GlobalStyle.js
+++ b/frontend/src/theme/GlobalStyle.js
@@ -2,10 +2,10 @@ import { createGlobalStyle } from 'styled-components'
export default createGlobalStyle`
body {
- margin: 0;
- padding: 0;
- background: ${(p) => p.theme.colors.background};
- font-family: 'JetBrains Mono', monospace;
- color: ${(p) => p.theme.colors.text};
+ margin: 0;
+ padding: 0;
+ background: ${(p) => p.theme.colors.background};
+ font-family: 'JetBrains Mono', monospace;
+ color: ${(p) => p.theme.colors.text};
}
` \ No newline at end of file
diff --git a/frontend/src/theme/ThemeProvider.js b/frontend/src/theme/ThemeProvider.js
index d9edcb0..942bc40 100644
--- a/frontend/src/theme/ThemeProvider.js
+++ b/frontend/src/theme/ThemeProvider.js
@@ -10,4 +10,6 @@ const theme = {
},
}
-export default ({ children }) => <ThemeProvider theme={theme}>{children}</ThemeProvider> \ No newline at end of file
+const Provider = ({ children }) => <ThemeProvider theme={theme}>{children}</ThemeProvider>
+
+export default Provider \ No newline at end of file
diff --git a/frontend/src/theme/style.css b/frontend/src/theme/style.css
new file mode 100644
index 0000000..10b955b
--- /dev/null
+++ b/frontend/src/theme/style.css
@@ -0,0 +1,150 @@
+@media all and (max-width: 1000px) {
+ .lt-content-column {
+ padding: 0 calc(5vw + 1em) 0 5vw !important;
+ }
+}
+
+form {
+ width: 100%;
+}
+
+textarea, input[type=text], input[type=password], .Dropdown-root {
+ width: 100%;
+ font-family: 'JetBrains Mono', monospace;
+ font-size: 0.8em;
+ padding: calc(0.8em - 1px);
+ border-radius: 3px;
+ border: 1px solid #565656;
+ outline: none;
+ margin: 1.7em 0;
+}
+
+/* fix weird symbol height in render mode */
+.large-op {
+ transform: translateY(-0.55em);
+}
+
+.small-op {
+ transform: translateY(-0.1em);
+}
+
+code, pre {
+ background: #00000000;
+ font-family: 'JetBrains Mono', monospace;
+ padding: initial;
+ border-radius: 3px;
+ outline: none;
+}
+
+.Dropdown-root {
+ cursor: pointer;
+}
+
+.Dropdown-root:hover, .Dropdown-root.is-open {
+ opacity: 1;
+}
+
+.Dropdown-root + label {
+ top: 0.5em;
+ opacity: 0;
+}
+
+.Dropdown-root.is-open + label, .Dropdown-root:hover + label {
+ opacity: 1;
+ top: -0.1em;
+}
+
+.Dropdown-placeholder {
+ width: 5.5em;
+}
+
+.Dropdown-menu {
+ border-top: 1px solid #111111;
+ margin-top: 0.5em;
+ bottom: auto;
+}
+
+.Dropdown-option {
+ margin-top: 0.5em;
+ transition: all 0.5s cubic-bezier(.25,.8,.25,1);
+}
+
+.Dropdown-option:hover {
+ font-weight: 700;
+ opacity: 0.4;
+}
+
+textarea, input[type=text], input[type=password], .Dropdown-root {
+ opacity: 0.5;
+ transition: opacity 0.5s cubic-bezier(.25,.8,.25,1);
+}
+
+textarea:focus, input[type=text]:focus, input[type=password]:focus {
+ opacity: 1;
+}
+
+input[type=password] {
+ font-weight: 700;
+}
+
+textarea {
+ height: max(40vh, 100%);
+ resize: vertical;
+ min-height: 40vh;
+}
+
+a {
+ color: #111111;
+}
+
+input[type=submit], button[type=submit] {
+ font-family: 'JetBrains Mono', serif;
+ font-weight: 700;
+ color: #faf9f5;
+ background-color: #111111;
+ padding: 0.8em 2em;
+ margin: 2em 0;
+ outline: 0;
+}
+
+button[type=button] {
+ font-family: 'JetBrains Mono', serif;
+ font-weight: 700;
+ width: 8em;
+ padding: calc(0.8em - 1px) 1.5em;
+ border-radius: 3px;
+ color: #111111;
+ background-color: #faf9f5;
+ border: 1px solid #565656;
+ outline: none;
+ margin: 2em 2em;
+}
+
+
+/* fixing markdown renderer */
+.md h3 {
+ font-weight: bold;
+}
+
+.md hr {
+ border-top: 1px solid #000;
+ border-style: solid;
+}
+
+.md code {
+ background: #00000008;
+ font-size: 0.8em;
+}
+
+.md pre {
+ padding: 0.7em;
+ background: #00000008;
+}
+
+.md pre > code {
+ background: none;
+}
+
+.md table {
+ width: 100%;
+} \ No newline at end of file
diff --git a/frontend/yarn.lock b/frontend/yarn.lock
index a38a3c1..f1be868 100644
--- a/frontend/yarn.lock
+++ b/frontend/yarn.lock
@@ -103,7 +103,7 @@
dependencies:
regenerator-runtime "^0.13.4"
-"@babel/runtime@^7.1.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2":
+"@babel/runtime@^7.3.1", "@babel/runtime@^7.7.2":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.10.1.tgz#b6eb75cac279588d3100baecd1b9894ea2840822"
integrity sha512-nQbbCbQc9u/rpg1XCxoMYQTbSMVZjCDxErQ1ClCn9Pvcmv1lGads19ep0a2VsEiIJeHqjZley6EQGEC3Yo1xMA==
@@ -344,6 +344,16 @@ axios@^0.21.1:
babel-plugin-syntax-jsx "^6.18.0"
lodash "^4.17.11"
+babel-plugin-styled-components@^1.12.0:
+ version "1.12.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-styled-components/-/babel-plugin-styled-components-1.12.0.tgz#1dec1676512177de6b827211e9eda5a30db4f9b9"
+ integrity sha512-FEiD7l5ZABdJPpLssKXjBUJMYqzbcNzBowfXDCdJhOpbhWiewapUaY+LZGT8R4Jg2TwOjGjG4RKeyrO5p9sBkA==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.0.0"
+ "@babel/helper-module-imports" "^7.0.0"
+ babel-plugin-syntax-jsx "^6.18.0"
+ lodash "^4.17.11"
+
[email protected], babel-plugin-syntax-jsx@^6.18.0:
version "6.18.0"
resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946"
@@ -1149,18 +1159,6 @@ highlight.js@~9.15.0, highlight.js@~9.15.1:
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.15.10.tgz#7b18ed75c90348c045eef9ed08ca1319a2219ad2"
integrity sha512-RoV7OkQm0T3os3Dd2VHLNMoaoDVx77Wygln3n9l5YV172XonWG6rgQD3XnF/BuFFZw9A0TJgmMSO8FEWQgvcXw==
-history@^4.9.0:
- version "4.10.1"
- resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3"
- integrity sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==
- dependencies:
- "@babel/runtime" "^7.1.2"
- loose-envify "^1.2.0"
- resolve-pathname "^3.0.0"
- tiny-invariant "^1.0.2"
- tiny-warning "^1.0.0"
- value-equal "^1.0.1"
-
hmac-drbg@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
@@ -1170,7 +1168,7 @@ hmac-drbg@^1.0.1:
minimalistic-assert "^1.0.0"
minimalistic-crypto-utils "^1.0.1"
-hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.1.0:
+hoist-non-react-statics@^3.0.0:
version "3.3.2"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
@@ -1397,11 +1395,6 @@ is-word-character@^1.0.0:
resolved "https://registry.yarnpkg.com/is-word-character/-/is-word-character-1.0.4.tgz#ce0e73216f98599060592f62ff31354ddbeb0230"
integrity sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA==
- version "0.0.1"
- resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
- integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=
-
[email protected], isarray@^1.0.0, isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
@@ -1486,7 +1479,7 @@ lodash@^4.17.11, lodash@^4.17.13:
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
-loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0:
+loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
@@ -1555,14 +1548,6 @@ miller-rabin@^4.0.0:
bn.js "^4.0.0"
brorand "^1.0.1"
-mini-create-react-context@^0.4.0:
- version "0.4.0"
- resolved "https://registry.yarnpkg.com/mini-create-react-context/-/mini-create-react-context-0.4.0.tgz#df60501c83151db69e28eac0ef08b4002efab040"
- integrity sha512-b0TytUgFSbgFJGzJqXPKCFCBWigAjpjo+Fl7Vf7ZbKRDptszpppKxXH6DRXEABZ/gcEQczeb0iZ7JvL8e8jjCA==
- dependencies:
- "@babel/runtime" "^7.5.5"
- tiny-warning "^1.0.3"
-
minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
@@ -1814,13 +1799,6 @@ path-exists@^4.0.0:
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
-path-to-regexp@^1.7.0:
- version "1.8.0"
- resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a"
- integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==
- dependencies:
- isarray "0.0.1"
-
pbkdf2@^3.0.3:
version "3.1.2"
resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075"
@@ -2009,7 +1987,7 @@ react-dropdown@^1.7.0:
dependencies:
classnames "^2.2.3"
[email protected], react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.6:
[email protected], react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.6:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
@@ -2055,35 +2033,6 @@ [email protected]:
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.8.3.tgz#721d4657672d400c5e3c75d063c4a85fb2d5d68f"
integrity sha512-X8jZHc7nCMjaCqoU+V2I0cOhNW+QMBwSUkeXnTi8IPe6zaRWfn60ZzvFDZqWPfmSJfjub7dDW1SP0jaHWLu/hg==
-react-router-dom@^5.2.0:
- version "5.2.0"
- resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.2.0.tgz#9e65a4d0c45e13289e66c7b17c7e175d0ea15662"
- integrity sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==
- dependencies:
- "@babel/runtime" "^7.1.2"
- history "^4.9.0"
- loose-envify "^1.3.1"
- prop-types "^15.6.2"
- react-router "5.2.0"
- tiny-invariant "^1.0.2"
- tiny-warning "^1.0.0"
-
- version "5.2.0"
- resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.2.0.tgz#424e75641ca8747fbf76e5ecca69781aa37ea293"
- integrity sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==
- dependencies:
- "@babel/runtime" "^7.1.2"
- history "^4.9.0"
- hoist-non-react-statics "^3.1.0"
- loose-envify "^1.3.1"
- mini-create-react-context "^0.4.0"
- path-to-regexp "^1.7.0"
- prop-types "^15.6.2"
- react-is "^16.6.0"
- tiny-invariant "^1.0.2"
- tiny-warning "^1.0.0"
-
react-simple-code-editor@^0.11.0:
version "0.11.0"
resolved "https://registry.yarnpkg.com/react-simple-code-editor/-/react-simple-code-editor-0.11.0.tgz#bb57c7c29b570f2ab229872599eac184f5bc673c"
@@ -2217,11 +2166,6 @@ [email protected]:
resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb"
integrity sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=
-resolve-pathname@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd"
- integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==
-
ripemd160@^2.0.0, ripemd160@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c"
@@ -2501,16 +2445,6 @@ tiny-emitter@^2.0.0:
resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423"
integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==
-tiny-invariant@^1.0.2:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875"
- integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==
-
-tiny-warning@^1.0.0, tiny-warning@^1.0.3:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
- integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
-
to-arraybuffer@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43"
@@ -2699,11 +2633,6 @@ util@^0.11.0:
dependencies:
inherits "2.0.3"
-value-equal@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c"
- integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==
-
vfile-location@^2.0.0:
version "2.0.6"
resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-2.0.6.tgz#8a274f39411b8719ea5728802e10d9e0dff1519e"