mirror of
https://github.com/morten-olsen/parcel.git
synced 2026-02-08 01:36:24 +01:00
cleanup
This commit is contained in:
@@ -12,6 +12,7 @@ const config = (api) => {
|
|||||||
'GITHUB_REPOSITORY',
|
'GITHUB_REPOSITORY',
|
||||||
],
|
],
|
||||||
}],
|
}],
|
||||||
|
[require.resolve('react-hot-loader/babel')],
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
10
package.json
10
package.json
@@ -7,19 +7,27 @@
|
|||||||
"build": "webpack"
|
"build": "webpack"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@ant-design/icons": "^4.2.2",
|
||||||
"@babel/core": "^7.11.1",
|
"@babel/core": "^7.11.1",
|
||||||
"@babel/preset-env": "^7.11.0",
|
"@babel/preset-env": "^7.11.0",
|
||||||
"@babel/preset-react": "^7.10.4",
|
"@babel/preset-react": "^7.10.4",
|
||||||
"@babel/preset-typescript": "^7.10.4",
|
"@babel/preset-typescript": "^7.10.4",
|
||||||
|
"@hot-loader/react-dom": "^16.13.0",
|
||||||
"@types/html-webpack-plugin": "^3.2.3",
|
"@types/html-webpack-plugin": "^3.2.3",
|
||||||
"@types/openpgp": "^4.4.12",
|
"@types/openpgp": "^4.4.12",
|
||||||
"@types/react": "^16.9.46",
|
"@types/react": "^16.9.46",
|
||||||
"@types/react-dom": "^16.9.8",
|
"@types/react-dom": "^16.9.8",
|
||||||
|
"@types/react-router": "^5.1.8",
|
||||||
|
"@types/react-router-dom": "^5.1.5",
|
||||||
"@types/styled-components": "^5.1.2",
|
"@types/styled-components": "^5.1.2",
|
||||||
|
"antd": "^4.5.4",
|
||||||
"babel-loader": "^8.1.0",
|
"babel-loader": "^8.1.0",
|
||||||
"babel-plugin-transform-inline-environment-variables": "^0.4.3",
|
"babel-plugin-transform-inline-environment-variables": "^0.4.3",
|
||||||
|
"css-loader": "^4.2.1",
|
||||||
"html-webpack-plugin": "^4.3.0",
|
"html-webpack-plugin": "^4.3.0",
|
||||||
"parcel-bundler": "^1.12.4",
|
"parcel-bundler": "^1.12.4",
|
||||||
|
"react-hot-loader": "^4.12.21",
|
||||||
|
"style-loader": "^1.2.1",
|
||||||
"ts-node": "^8.10.2",
|
"ts-node": "^8.10.2",
|
||||||
"typescript": "^3.9.7",
|
"typescript": "^3.9.7",
|
||||||
"webpack": "^4.44.1",
|
"webpack": "^4.44.1",
|
||||||
@@ -33,6 +41,8 @@
|
|||||||
"react-dom": "^16.13.1",
|
"react-dom": "^16.13.1",
|
||||||
"react-dropzone": "^11.0.3",
|
"react-dropzone": "^11.0.3",
|
||||||
"react-feather": "^2.0.8",
|
"react-feather": "^2.0.8",
|
||||||
|
"react-router": "^5.2.0",
|
||||||
|
"react-router-dom": "^5.2.0",
|
||||||
"styled-components": "^5.1.1"
|
"styled-components": "^5.1.1"
|
||||||
},
|
},
|
||||||
"browserslist": [
|
"browserslist": [
|
||||||
|
|||||||
17
src/App.tsx
17
src/App.tsx
@@ -1,19 +1,20 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ThemeProvider } from 'styled-components';
|
import { hot } from 'react-hot-loader/root';
|
||||||
|
import { Layout } from 'antd';
|
||||||
import { GithubProvider } from './contexts/Github';
|
import { GithubProvider } from './contexts/Github';
|
||||||
import { EncryptionProvider } from './contexts/Encryption';
|
import { EncryptionProvider } from './contexts/Encryption';
|
||||||
import Encrypt from './screens/Encrypt';
|
import AppRouter from './Router';
|
||||||
import theme from './theme';
|
|
||||||
|
|
||||||
const App: React.FC = () => (
|
const App: React.FC = () => (
|
||||||
<ThemeProvider theme={theme}>
|
|
||||||
<GithubProvider username="morten-olsen">
|
<GithubProvider username="morten-olsen">
|
||||||
<div>Test</div>
|
|
||||||
<EncryptionProvider>
|
<EncryptionProvider>
|
||||||
<Encrypt />
|
<Layout>
|
||||||
|
<Layout.Content style={{ padding: '25px' }}>
|
||||||
|
<AppRouter/>
|
||||||
|
</Layout.Content>
|
||||||
|
</Layout>
|
||||||
</EncryptionProvider>
|
</EncryptionProvider>
|
||||||
</GithubProvider>
|
</GithubProvider>
|
||||||
</ThemeProvider>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
export default App;
|
export default hot(App);
|
||||||
|
|||||||
24
src/Router.tsx
Normal file
24
src/Router.tsx
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
HashRouter as Router,
|
||||||
|
Switch,
|
||||||
|
Route,
|
||||||
|
} from 'react-router-dom';
|
||||||
|
|
||||||
|
import Encrypt from './screens/Encrypt';
|
||||||
|
import Debug from './screens/Debug';
|
||||||
|
|
||||||
|
const AppRouter: React.FC = () => (
|
||||||
|
<Router>
|
||||||
|
<Switch>
|
||||||
|
<Route path="/debug">
|
||||||
|
<Debug />
|
||||||
|
</Route>
|
||||||
|
<Route path="/">
|
||||||
|
<Encrypt />
|
||||||
|
</Route>
|
||||||
|
</Switch>
|
||||||
|
</Router>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default AppRouter;
|
||||||
@@ -1,49 +1,49 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { Fragment, useState } from 'react';
|
||||||
import styled from 'styled-components';
|
import { Menu, Dropdown, Form } from 'antd';
|
||||||
import { Theme } from '../theme';
|
import { DownOutlined, FileOutlined, FileTextOutlined } from '@ant-design/icons';
|
||||||
import AddText from './AddText';
|
import AddText from './AddText';
|
||||||
import AddFile from './AddFile';
|
import AddFile from './AddFile';
|
||||||
|
|
||||||
const Wrapper = styled.div`
|
const layout = {
|
||||||
`;
|
labelCol: { span: 2 },
|
||||||
|
};
|
||||||
const Top = styled.div`
|
|
||||||
display: flex;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const Button = styled.button<{
|
|
||||||
active: boolean;
|
|
||||||
theme: Theme;
|
|
||||||
}>`
|
|
||||||
background: ${({ active }) => active ? '#2c3e50' : 'transparent'};
|
|
||||||
padding: ${({ theme }) => theme.margin.medium}px;
|
|
||||||
border: none;
|
|
||||||
font: inherit;
|
|
||||||
color: inherit;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const Panel = styled.div<{
|
|
||||||
theme: Theme;
|
|
||||||
}>`
|
|
||||||
background: #2c3e50;
|
|
||||||
color: #fff;
|
|
||||||
padding: ${({ theme }) => theme.margin.medium}px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const Add: React.FC = () => {
|
const Add: React.FC = () => {
|
||||||
const [type, setType] = useState<'file' | 'text'>('text');
|
const [type, setType] = useState<'file' | 'text'>('text');
|
||||||
|
|
||||||
|
const menu = (
|
||||||
|
<Menu>
|
||||||
|
<Menu.Item
|
||||||
|
onClick={() => setType('file')}
|
||||||
|
active={type === 'file'}
|
||||||
|
icon={<FileOutlined />}
|
||||||
|
>
|
||||||
|
File
|
||||||
|
</Menu.Item>
|
||||||
|
<Menu.Item
|
||||||
|
onClick={() => setType('text')}
|
||||||
|
active={type === 'text'}
|
||||||
|
icon={<FileTextOutlined />}
|
||||||
|
>
|
||||||
|
Text
|
||||||
|
</Menu.Item>
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrapper>
|
<>
|
||||||
<Top>
|
<Form {...layout}>
|
||||||
<Button active={type==='text'} onClick={() => setType('text')}>Text</Button>
|
<Form.Item
|
||||||
<Button active={type==='file'} onClick={() => setType('file')}>File</Button>
|
label="I want to encrypt a"
|
||||||
</Top>
|
>
|
||||||
<Panel>
|
<Dropdown overlay={menu}>
|
||||||
{type === 'file' && <AddFile />}
|
<a>{type} <DownOutlined /></a>
|
||||||
|
</Dropdown>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
{type === 'text' && <AddText />}
|
{type === 'text' && <AddText />}
|
||||||
</Panel>
|
{type === 'file' && <AddFile />}
|
||||||
</Wrapper>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
import React, { useContext, useCallback } from 'react';
|
import React, { useContext, useCallback } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
import { Layout } from 'antd';
|
||||||
|
import { UploadOutlined } from '@ant-design/icons';
|
||||||
import { useDropzone } from 'react-dropzone';
|
import { useDropzone } from 'react-dropzone';
|
||||||
import EncryptionContext from '../contexts/Encryption';
|
import EncryptionContext from '../contexts/Encryption';
|
||||||
import { Upload } from 'react-feather';
|
|
||||||
|
|
||||||
const DropWrapper = styled.div`
|
const Icon = styled(UploadOutlined)`
|
||||||
|
font-size: 100px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const DropWrapper = styled(Layout)`
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -19,13 +24,11 @@ const AddFile: React.FC = () => {
|
|||||||
}, [])
|
}, [])
|
||||||
const {getRootProps, getInputProps} = useDropzone({ onDrop });
|
const {getRootProps, getInputProps} = useDropzone({ onDrop });
|
||||||
return (
|
return (
|
||||||
<div>
|
|
||||||
<DropWrapper {...getRootProps()}>
|
<DropWrapper {...getRootProps()}>
|
||||||
<input {...getInputProps()} />
|
<input {...getInputProps()} />
|
||||||
<Upload size={200} />
|
<Icon />
|
||||||
<p>Drag 'n' drop some files here, or click to select files</p>
|
<p>Drag 'n' drop some files here, or click to select files</p>
|
||||||
</DropWrapper>
|
</DropWrapper>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,39 +1,57 @@
|
|||||||
import React, { useState, useCallback, useContext } from 'react';
|
import React, { useState, useCallback, useContext } from 'react';
|
||||||
import styled from 'styled-components';
|
import { Input, Form, Button } from 'antd';
|
||||||
|
import { PlusOutlined } from '@ant-design/icons';
|
||||||
import EncryptionContext from '../contexts/Encryption';
|
import EncryptionContext from '../contexts/Encryption';
|
||||||
import { Theme } from '../theme';
|
|
||||||
|
|
||||||
const Wrapper = styled.div`
|
const layout = {
|
||||||
display: flex;
|
labelCol: { span: 2 },
|
||||||
flex-direction: column;
|
};
|
||||||
flex: 1;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const Text = styled.textarea<{
|
const tailLayout = {
|
||||||
theme: Theme;
|
wrapperCol: { offset: 2 },
|
||||||
}>`
|
};
|
||||||
border: none;
|
|
||||||
height: 200px;
|
|
||||||
background: transparent;
|
|
||||||
color: inherit;
|
|
||||||
padding: ${({ theme }) => theme.margin.medium}px;
|
|
||||||
font: inherit;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const AddText : React.FC = () => {
|
const AddText : React.FC = () => {
|
||||||
const { addText } = useContext(EncryptionContext);
|
const { addText } = useContext(EncryptionContext);
|
||||||
|
const [name, setName] = useState('');
|
||||||
const [text, setText] = useState('');
|
const [text, setText] = useState('');
|
||||||
|
|
||||||
const add = useCallback(() => {
|
const add = useCallback(() => {
|
||||||
addText(text);
|
addText(text, name || 'untitled');
|
||||||
setText('');
|
setText('');
|
||||||
|
setName('');
|
||||||
}, [text, addText]);
|
}, [text, addText]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Form {...layout}>
|
||||||
<Text placeholder="Enter you message..." value={text} onChange={evt => setText(evt.target.value)} />
|
<Form.Item
|
||||||
<button onClick={add}>Save</button>
|
label="Name"
|
||||||
</Wrapper>
|
>
|
||||||
|
<Input
|
||||||
|
value={name}
|
||||||
|
onChange={evt => setName(evt.target.value)}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label="Message"
|
||||||
|
>
|
||||||
|
<Input.TextArea
|
||||||
|
value={text}
|
||||||
|
rows={10}
|
||||||
|
onChange={evt => setText(evt.target.value)}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item {...tailLayout}>
|
||||||
|
<Button
|
||||||
|
onClick={add}
|
||||||
|
type="primary"
|
||||||
|
icon={<PlusOutlined />}
|
||||||
|
disabled={!text}
|
||||||
|
>
|
||||||
|
Add
|
||||||
|
</Button>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,20 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styled from 'styled-components';
|
import {
|
||||||
import { File } from '../contexts/Encryption';
|
List,
|
||||||
|
Button,
|
||||||
|
} from 'antd';
|
||||||
|
import {
|
||||||
|
DeleteOutlined,
|
||||||
|
SyncOutlined,
|
||||||
|
IssuesCloseOutlined,
|
||||||
|
LockOutlined,
|
||||||
|
} from '@ant-design/icons';
|
||||||
|
import { FileType } from '../contexts/Encryption';
|
||||||
import { CheckCircle, XCircle, Download, Trash, Loader } from 'react-feather';
|
import { CheckCircle, XCircle, Download, Trash, Loader } from 'react-feather';
|
||||||
import Row, { Cell } from './Row';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
remove: () => void;
|
remove: () => void;
|
||||||
file: File;
|
file: FileType;
|
||||||
}
|
}
|
||||||
|
|
||||||
const downloadLink = (name: string, url: string) => {
|
const downloadLink = (name: string, url: string) => {
|
||||||
@@ -18,42 +26,51 @@ const downloadLink = (name: string, url: string) => {
|
|||||||
document.body.removeChild(downloadLink);
|
document.body.removeChild(downloadLink);
|
||||||
};
|
};
|
||||||
|
|
||||||
const Button = styled.button`
|
const icons: {[name: string]: any} = {
|
||||||
background: none;
|
encrypting: <SyncOutlined spin />,
|
||||||
border: none;
|
failed: <IssuesCloseOutlined />,
|
||||||
color: green;
|
encrypted: <LockOutlined />,
|
||||||
`;
|
|
||||||
|
|
||||||
const icons: {[name: string]: typeof CheckCircle} = {
|
|
||||||
encrypting: Loader,
|
|
||||||
failed: XCircle,
|
|
||||||
encrypted: CheckCircle,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const IconText = ({ icon, text, ...props }) => (
|
||||||
|
<Button
|
||||||
|
{...props}
|
||||||
|
icon={React.createElement(icon)}
|
||||||
|
>
|
||||||
|
{text}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
|
||||||
const FileView: React.FC<Props> = ({
|
const FileView: React.FC<Props> = ({
|
||||||
file,
|
file,
|
||||||
remove,
|
remove,
|
||||||
}) => {
|
}) => {
|
||||||
const Icon = icons[file.status];
|
const icon = icons[file.status];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Row
|
<List.Item
|
||||||
left={(
|
actions={file.link ? [(
|
||||||
<Cell><Icon /></Cell>
|
<IconText
|
||||||
)}
|
icon={DeleteOutlined}
|
||||||
title={file.name}
|
danger
|
||||||
body={`encrypted for ${file.reciever}`}
|
text="Delete"
|
||||||
right={!!file.link && (
|
onClick={remove}
|
||||||
<>
|
|
||||||
<Cell>
|
|
||||||
<Button onClick={() => downloadLink(file.name, file.link)}><Download /></Button>
|
|
||||||
</Cell>
|
|
||||||
<Cell>
|
|
||||||
<Button onClick={remove}><Trash /></Button>
|
|
||||||
</Cell>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
|
), (
|
||||||
|
<IconText
|
||||||
|
icon={DeleteOutlined}
|
||||||
|
type="primary"
|
||||||
|
text="Download"
|
||||||
|
onClick={() => downloadLink(file.name, file.link!)}
|
||||||
|
/>
|
||||||
|
)]: []}
|
||||||
|
>
|
||||||
|
<List.Item.Meta
|
||||||
|
avatar={icon}
|
||||||
|
title={file.name}
|
||||||
|
description={`Encrypted for ${file.reciever}`}
|
||||||
|
/>
|
||||||
|
</List.Item>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,17 @@
|
|||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
|
import { List, Empty } from 'antd';
|
||||||
import EncryptionContext from '../contexts/Encryption';
|
import EncryptionContext from '../contexts/Encryption';
|
||||||
import File from './File';
|
import File from './File';
|
||||||
|
|
||||||
const Encrypt: React.FC = () => {
|
const Encrypt: React.FC = () => {
|
||||||
const { files, deleteFile } = useContext(EncryptionContext);
|
const { files, deleteFile } = useContext(EncryptionContext);
|
||||||
|
|
||||||
|
if (Object.keys(files).length === 0) {
|
||||||
|
return <Empty />
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<List>
|
||||||
{Object.entries(files).map(([id, file]) => (
|
{Object.entries(files).map(([id, file]) => (
|
||||||
<File
|
<File
|
||||||
key={id}
|
key={id}
|
||||||
@@ -14,7 +19,7 @@ const Encrypt: React.FC = () => {
|
|||||||
remove={() => deleteFile(id)}
|
remove={() => deleteFile(id)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</List>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
71
src/components/Profile.tsx
Normal file
71
src/components/Profile.tsx
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import React, { useContext, useState } from 'react';
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
Avatar,
|
||||||
|
Button,
|
||||||
|
Modal,
|
||||||
|
} from 'antd';
|
||||||
|
import { KeyOutlined, GithubOutlined } from '@ant-design/icons'
|
||||||
|
import GithubContext from '../contexts/Github';
|
||||||
|
|
||||||
|
const IconText = ({ icon, text, ...props }) => (
|
||||||
|
<Button
|
||||||
|
{...props}
|
||||||
|
icon={React.createElement(icon)}
|
||||||
|
>
|
||||||
|
{text}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
|
||||||
|
const Profile: React.FC = () => {
|
||||||
|
const { user, keys } = useContext(GithubContext);
|
||||||
|
const [showKeys, setShowKeys] = useState(false);
|
||||||
|
if (!user) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Modal
|
||||||
|
visible={showKeys}
|
||||||
|
onOk={() => setShowKeys(false)}
|
||||||
|
onCancel={() => setShowKeys(false)}
|
||||||
|
title="Keys"
|
||||||
|
>
|
||||||
|
<pre>
|
||||||
|
{keys!.join('\n\n')}
|
||||||
|
</pre>
|
||||||
|
</Modal>
|
||||||
|
<Card
|
||||||
|
style={{ width: 300, marginTop: 16, alignSelf: 'center' }}
|
||||||
|
actions={[(
|
||||||
|
<IconText
|
||||||
|
key="showkeys"
|
||||||
|
text="Show keys"
|
||||||
|
icon={KeyOutlined}
|
||||||
|
onClick={() => setShowKeys(true)}
|
||||||
|
/>
|
||||||
|
), (
|
||||||
|
<a target="_blank" href={`https://github.com/${user.login}`}>
|
||||||
|
<IconText
|
||||||
|
key="gotogithub"
|
||||||
|
text="Go to Github"
|
||||||
|
icon={GithubOutlined}
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
)]}
|
||||||
|
>
|
||||||
|
<Card.Meta
|
||||||
|
title={user.name}
|
||||||
|
avatar={(
|
||||||
|
<Avatar src={user.avatar_url} size={80} />
|
||||||
|
)}
|
||||||
|
description={user.location}
|
||||||
|
/>
|
||||||
|
|
||||||
|
</Card>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Profile;
|
||||||
@@ -10,6 +10,10 @@ class Config {
|
|||||||
const [user] = this.repo.split('/');
|
const [user] = this.repo.split('/');
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isProd() {
|
||||||
|
return process.env.NODE_ENV === 'production';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new Config();
|
export default new Config();
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import React, { useState, useCallback, useContext, createContext } from 'react';
|
import React, { useState, useCallback, useContext, createContext } from 'react';
|
||||||
import * as openpgp from 'openpgp';
|
import * as openpgp from 'openpgp';
|
||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
|
import { message } from 'antd';
|
||||||
import GithubContext from './Github';
|
import GithubContext from './Github';
|
||||||
|
|
||||||
export interface FileType {
|
export interface FileType {
|
||||||
@@ -14,7 +15,7 @@ export interface FileType {
|
|||||||
interface EncryptionContextType {
|
interface EncryptionContextType {
|
||||||
files: {[id: string]: FileType};
|
files: {[id: string]: FileType};
|
||||||
addFile: (file: File) => Promise<void>;
|
addFile: (file: File) => Promise<void>;
|
||||||
addText: (text: string) => Promise<void>;
|
addText: (text: string, name: string) => Promise<void>;
|
||||||
deleteFile: (id: string) => void;
|
deleteFile: (id: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,7 +33,7 @@ const encrypt = async (keys: string[], content: string) => {
|
|||||||
const encrypted = await openpgp.encrypt({
|
const encrypted = await openpgp.encrypt({
|
||||||
message,
|
message,
|
||||||
armor: true,
|
armor: true,
|
||||||
publicKeys: armoredKeys.reduce((output, key: any) => [...output, ...key.keys], []),
|
publicKeys: armoredKeys.reduce<any>((output, key: any) => [...output, ...key.keys], []),
|
||||||
});
|
});
|
||||||
const { data } = encrypted;
|
const { data } = encrypted;
|
||||||
const blob = new Blob([data], {
|
const blob = new Blob([data], {
|
||||||
@@ -55,7 +56,9 @@ const EncryptionProvider: React.FC = ({
|
|||||||
});
|
});
|
||||||
}, [files]);
|
}, [files]);
|
||||||
|
|
||||||
const add = (name: string = nanoid()) => {
|
const add = (name: string) => {
|
||||||
|
const id = nanoid();
|
||||||
|
message.info(`Beginning to encrypt ${name}`);
|
||||||
const file: FileType = {
|
const file: FileType = {
|
||||||
name: `${name}.asc`,
|
name: `${name}.asc`,
|
||||||
reciever: username,
|
reciever: username,
|
||||||
@@ -63,19 +66,20 @@ const EncryptionProvider: React.FC = ({
|
|||||||
};
|
};
|
||||||
setFiles(files => ({
|
setFiles(files => ({
|
||||||
...files,
|
...files,
|
||||||
[name]: file,
|
[id]: file,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const setError = (err: any) => {
|
const setError = (err: any) => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
setFiles(files => ({
|
setFiles(files => ({
|
||||||
...files,
|
...files,
|
||||||
[name]: {
|
[id]: {
|
||||||
...files[name],
|
...files[id],
|
||||||
status: 'failed',
|
status: 'failed',
|
||||||
error: err,
|
error: err,
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
message.error(`Failed to encrypt ${name}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const setContent = (text: string, keys: string[]) => {
|
const setContent = (text: string, keys: string[]) => {
|
||||||
@@ -84,12 +88,13 @@ const EncryptionProvider: React.FC = ({
|
|||||||
const encrypted = await encrypt(keys, text);
|
const encrypted = await encrypt(keys, text);
|
||||||
setFiles(files => ({
|
setFiles(files => ({
|
||||||
...files,
|
...files,
|
||||||
[name]: {
|
[id]: {
|
||||||
...files[name],
|
...files[id],
|
||||||
link: encrypted,
|
link: encrypted,
|
||||||
status: 'encrypted'
|
status: 'encrypted'
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
message.success(`Done encrypting ${name}`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err);
|
setError(err);
|
||||||
}
|
}
|
||||||
@@ -104,7 +109,6 @@ const EncryptionProvider: React.FC = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const addFile = useCallback(async (file: File) => {
|
const addFile = useCallback(async (file: File) => {
|
||||||
console.log('a', keys, file);
|
|
||||||
if (!keys) return;
|
if (!keys) return;
|
||||||
const addedFile = add(file.name);
|
const addedFile = add(file.name);
|
||||||
const reader = new FileReader()
|
const reader = new FileReader()
|
||||||
@@ -112,15 +116,14 @@ const EncryptionProvider: React.FC = ({
|
|||||||
reader.onabort = addedFile.setError,
|
reader.onabort = addedFile.setError,
|
||||||
reader.onerror = addedFile.setError,
|
reader.onerror = addedFile.setError,
|
||||||
reader.onload = () => {
|
reader.onload = () => {
|
||||||
console.log('foo', file);
|
|
||||||
addedFile.setContent(reader.result as string, keys);
|
addedFile.setContent(reader.result as string, keys);
|
||||||
}
|
}
|
||||||
reader.readAsText(file)
|
reader.readAsText(file)
|
||||||
}, [keys, username]);
|
}, [keys, username]);
|
||||||
|
|
||||||
const addText = useCallback(async (text: string) => {
|
const addText = useCallback(async (text: string, name: string) => {
|
||||||
if (!keys) return;
|
if (!keys) return;
|
||||||
const file = add();
|
const file = add(`${name}.txt`);
|
||||||
file.setContent(text, keys);
|
file.setContent(text, keys);
|
||||||
}, [keys, username]);
|
}, [keys, username]);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import React, { useState, useEffect, createContext } from 'react';
|
import React, { useState, useEffect, createContext } from 'react';
|
||||||
|
import { Layout, Spin } from 'antd';
|
||||||
|
|
||||||
interface GithubContextType {
|
interface GithubContextType {
|
||||||
username: string;
|
username: string;
|
||||||
@@ -13,6 +14,20 @@ interface Props {
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Loader = () => (
|
||||||
|
<Layout
|
||||||
|
style={{
|
||||||
|
position: 'fixed',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
height: '100%',
|
||||||
|
width: '100%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Spin size="large" />
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
|
||||||
const GithubContext = createContext<GithubContextType>({
|
const GithubContext = createContext<GithubContextType>({
|
||||||
username: 'unknown',
|
username: 'unknown',
|
||||||
state: 'failed',
|
state: 'failed',
|
||||||
@@ -46,6 +61,10 @@ const GithubProvider: React.FC<Props> = ({
|
|||||||
run();
|
run();
|
||||||
}, [username]);
|
}, [username]);
|
||||||
|
|
||||||
|
if (state === 'loading') {
|
||||||
|
return <Loader />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GithubContext.Provider
|
<GithubContext.Provider
|
||||||
value={{
|
value={{
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import 'antd/dist/antd.css';
|
||||||
import { render } from 'react-dom';
|
import { render } from 'react-dom';
|
||||||
import App from './App';
|
import App from './App';
|
||||||
|
|
||||||
|
|||||||
37
src/screens/Debug.tsx
Normal file
37
src/screens/Debug.tsx
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import React, { useMemo } from 'react';
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
} from 'antd';
|
||||||
|
import config from '../config';
|
||||||
|
|
||||||
|
const columns = [{
|
||||||
|
title: 'Name',
|
||||||
|
dataIndex: 'name',
|
||||||
|
key: 'name',
|
||||||
|
}, {
|
||||||
|
title: 'Value',
|
||||||
|
dataIndex: 'value',
|
||||||
|
key: 'value',
|
||||||
|
}];
|
||||||
|
|
||||||
|
const Debug: React.FC = () => {
|
||||||
|
const data = useMemo(() => {
|
||||||
|
const vals = {
|
||||||
|
'Repository': config.repo,
|
||||||
|
'User': config.user,
|
||||||
|
'Is Production': config.isProd,
|
||||||
|
};
|
||||||
|
|
||||||
|
return Object.entries(vals).map(([name, value]) => ({
|
||||||
|
key: name,
|
||||||
|
name,
|
||||||
|
value: value.toString(),
|
||||||
|
}));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Table dataSource={data} columns={columns} />
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Debug;
|
||||||
@@ -1,16 +1,33 @@
|
|||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
|
import { Collapse, Badge } from 'antd';
|
||||||
|
import Profile from '../components/Profile';
|
||||||
import Add from '../components/Add';
|
import Add from '../components/Add';
|
||||||
import FileList from '../components/FileList';
|
import FileList from '../components/FileList';
|
||||||
import GithubContext from '../contexts/Github';
|
import EncryptionContext from '../contexts/Encryption';
|
||||||
|
|
||||||
const Encrypt: React.FC = () => {
|
const Encrypt: React.FC = () => {
|
||||||
const { username } = useContext(GithubContext);
|
const { files } = useContext(EncryptionContext);
|
||||||
return (
|
return (
|
||||||
<div>
|
<>
|
||||||
<div>To: {username}</div>
|
<Collapse ghost defaultActiveKey={[2, 3]}>
|
||||||
|
<Collapse.Panel key={2} header="Encrypt">
|
||||||
<Add />
|
<Add />
|
||||||
|
</Collapse.Panel>
|
||||||
|
<Collapse.Panel
|
||||||
|
key={3}
|
||||||
|
header={(
|
||||||
|
<Badge count={Object.keys(files).length} offset={[20, 7]}>
|
||||||
|
Files
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
>
|
||||||
<FileList />
|
<FileList />
|
||||||
</div>
|
</Collapse.Panel>
|
||||||
|
<Collapse.Panel key={1} header="Profile">
|
||||||
|
<Profile />
|
||||||
|
</Collapse.Panel>
|
||||||
|
</Collapse>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Configuration } from 'webpack';
|
import webpack, { Configuration } from 'webpack';
|
||||||
import HtmlWebpackPlugin from 'html-webpack-plugin';
|
import HtmlWebpackPlugin from 'html-webpack-plugin';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
@@ -6,6 +6,7 @@ const config: Configuration = {
|
|||||||
mode: 'development',
|
mode: 'development',
|
||||||
entry: {
|
entry: {
|
||||||
app: [
|
app: [
|
||||||
|
'react-hot-loader/patch',
|
||||||
path.join(__dirname, 'src', 'index.tsx'),
|
path.join(__dirname, 'src', 'index.tsx'),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -14,16 +15,27 @@ const config: Configuration = {
|
|||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
extensions: ['.tsx', '.ts', '.js'],
|
extensions: ['.tsx', '.ts', '.js'],
|
||||||
|
alias: {
|
||||||
|
'react-dom': '@hot-loader/react-dom',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
new HtmlWebpackPlugin(),
|
new HtmlWebpackPlugin(),
|
||||||
],
|
],
|
||||||
module: {
|
module: {
|
||||||
rules: [{
|
rules: [{
|
||||||
test: /\.tsx?/,
|
test: /\.tsx?$/,
|
||||||
use: ['babel-loader'],
|
use: ['babel-loader'],
|
||||||
|
}, {
|
||||||
|
test: /\.css$/,
|
||||||
|
use: ['style-loader', 'css-loader'],
|
||||||
}],
|
}],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
(config as any).devServer = {
|
||||||
|
hot: true,
|
||||||
|
contentBase: './dist',
|
||||||
|
};
|
||||||
|
|
||||||
export default config;
|
export default config;
|
||||||
|
|||||||
Reference in New Issue
Block a user