adding and fetching docs

This commit is contained in:
Alexander Bell 2018-09-19 20:29:48 +02:00
parent d9aa8deb16
commit 8d93af6552
11 changed files with 416 additions and 270 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -10,9 +10,12 @@
<title>Ninja</title> <title>Ninja</title>
</head> </head>
<body> <body>
<!-- Alexander Bell, all rights reserved -->
<noscript> <noscript>
You need to enable JavaScript to run this app. You need to enable JavaScript to run this app.
</noscript> </noscript>
<!-- Alexander Bell, all rights reserved -->
<div id="root"></div> <div id="root"></div>
<!-- Alexander Bell, all rights reserved -->
</body> </body>
</html> </html>

View File

@ -42,7 +42,7 @@ const App = inject("rootStore") ( observer(
const body = document.getElementsByTagName('body')[0]; const body = document.getElementsByTagName('body')[0];
body.style.margin = '0'; body.style.margin = '0';
body.style.padding = '0'; body.style.padding = '0';
body.style.overflow = 'hidden'; body.style.overflow = 'auto';
//Load app //Load app
this.stores.rootStore.loadApp(); this.stores.rootStore.loadApp();

View File

@ -0,0 +1,17 @@
import React from 'react';
export default function(props) {
/*
* Expected props
* - black: String
* the text in black
* - red: String
* the text in red
*/
return(
<h2 style={{padding: '20px'}}>
{props.black}<span style={{color: '#DA3821'}}>{props.red}</span>
</h2>
)
}

View File

@ -113,17 +113,14 @@ const Main = inject("rootStore") ( observer(
</Menu.Item> </Menu.Item>
</Menu.Menu> </Menu.Menu>
</Menu> </Menu>
<div style={{minHeight: '100vh'}}>
{this.props.children} {this.props.children}
</div> </div>
</div>
); );
} else { } else {
//Mobile sidebar //Mobile sidebar
return( return(
<div>
<Sidebar.Pushable> <Sidebar.Pushable>
<Sidebar <Sidebar
as={Menu} as={Menu}
@ -137,11 +134,11 @@ const Main = inject("rootStore") ( observer(
<img alt="" src={require('../../files/images/logo.svg')} style={{display: 'block', width: '60px', height: '45px'}} /> <img alt="" src={require('../../files/images/logo.svg')} style={{display: 'block', width: '60px', height: '45px'}} />
</Menu.Item> </Menu.Item>
<Menu.Item name="Home" active={activeItem === 'Home'} onClick={this.handleItemClick} as='a'> <Menu.Item name="/" active={activeItem === '/'} onClick={this.handleItemClick} as='a'>
Home Home
</Menu.Item> </Menu.Item>
<Menu.Item name="Password manager" active={activeItem === 'Password manager'} onClick={this.handleItemClick} as='a'> <Menu.Item name="/passwords" active={activeItem === '/passwords'} onClick={this.handleItemClick} as='a'>
Password manager Password manager
</Menu.Item> </Menu.Item>
</Sidebar> </Sidebar>
@ -151,7 +148,6 @@ const Main = inject("rootStore") ( observer(
{this.props.children} {this.props.children}
</Sidebar.Pusher> </Sidebar.Pusher>
</Sidebar.Pushable> </Sidebar.Pushable>
</div>
) )
} }

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -78,6 +78,7 @@ class Main extends Component {
<Route exact path="/" component={Home} /> <Route exact path="/" component={Home} />
<Route path="/passwords" component={PasswordManager} /> <Route path="/passwords" component={PasswordManager} />
</Switch> </Switch>
<img alt="" style={{width: '100px', marginBottom: '-20px', marginTop: '30px', marginLeft: '20px'}} src={require('../files/images/ninja.svg')} />
</Segment> </Segment>
</Container> </Container>
</Menu> </Menu>

View File

@ -1,6 +1,7 @@
import React, {Component} from 'react'; import React, {Component} from 'react';
import jss from 'jss'; import jss from 'jss';
import preset from 'jss-preset-default'; import preset from 'jss-preset-default';
import { Container } from 'semantic-ui-react';
/* /*
* Functions import * Functions import
@ -9,6 +10,7 @@ import preset from 'jss-preset-default';
/* /*
* Component imports * Component imports
*/ */
import Headline from '../../components/Headline';
jss.setup(preset()); jss.setup(preset());
@ -53,9 +55,16 @@ class Home extends Component {
render() { render() {
return( return(
<div> <Container text>
Home <Headline black="alexbell." red="ninja" />
</div>
<p>
You are signed in on alexbell.ninja. <b>What can you do here?</b><br />
Well, I like developing software in my freetime, mainly applications
for my personal use. They might also be of advantage for you, so feel free
to browse around a little and try some of what you can find here :)
</p>
</Container>
); );
} }

View File

@ -1,11 +1,14 @@
import React, {Component} from 'react'; import React, {Component} from 'react';
import { observer, inject } from 'mobx-react';
import jss from 'jss'; import jss from 'jss';
import preset from 'jss-preset-default'; import preset from 'jss-preset-default';
import { Segment, Container, Header, Form, Input, Button, Icon } from 'semantic-ui-react'; import { Segment, Container, Header, Form, Input, Button, Icon } from 'semantic-ui-react';
import alertify from 'alertify.js';
/* /*
* Functions import * Functions import
*/ */
import { encrypt } from '../../stores/functions/encryption';
/* /*
* Component imports * Component imports
@ -13,6 +16,11 @@ import { Segment, Container, Header, Form, Input, Button, Icon } from 'semantic-
jss.setup(preset()); jss.setup(preset());
//Firebase
var firebase = require('../../stores/fb').fb;
// Initialize Cloud Firestore through Firebase
var db = firebase.firestore();
/* /*
############################### ###############################
@ -28,6 +36,7 @@ Components -- END
const New = inject("rootStore") ( observer(
class New extends Component { class New extends Component {
constructor(props){ constructor(props){
super(props); super(props);
@ -43,6 +52,12 @@ class New extends Component {
* encryption key of the password * encryption key of the password
*/ */
//Stored information
this.stores = this.props.rootStore.stores;
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.state = { this.state = {
url: '', url: '',
login: '', login: '',
@ -92,9 +107,41 @@ class New extends Component {
} }
handleChange(e, {name}) { handleChange(e) {
this.setState({ this.setState({
[name]: e.target.value [e.target.name]: e.target.value
});
}
handleSubmit() {
const url = this.state.url;
const login = encrypt(this.state.login, this.props.encryptionkey);
const password = encrypt(this.state.password, this.props.encryptionkey);
const uid = this.stores.authStore.userData.uid;
db.collection("passwords").add({
uid: uid,
url: url,
login: login,
password: password,
time: new Date()
})
.then((docRef) => {
console.log("Document written with ID: ", docRef.id);
alertify.success("Document successfully added!");
this.props.toggleNewWindow();
})
.catch(function(error) {
console.error("Error adding document: ", error);
alertify.error("Something went wrong! Please check your internet connection");
this.setState({
loading: false
});
});
this.setState({
loading: true
}); });
} }
@ -162,5 +209,6 @@ class New extends Component {
} }
} }
} }
));
export default New; export default New;

View File

@ -1,20 +1,28 @@
import React, {Component} from 'react'; import React, {Component} from 'react';
import { observer, inject } from 'mobx-react';
import jss from 'jss'; import jss from 'jss';
import preset from 'jss-preset-default'; import preset from 'jss-preset-default';
import { Menu, Segment, Dropdown, Table, Button, Icon, Header } from 'semantic-ui-react'; import { Menu, Segment, Dropdown, Table, Button, Loader, Icon, Header, Input } from 'semantic-ui-react';
import alertify from 'alertify.js'; import alertify from 'alertify.js';
/* /*
* Functions import * Functions import
*/ */
import { decrypt } from '../../stores/functions/encryption';
/* /*
* Component imports * Component imports
*/ */
import New from './New'; import New from './New';
import Headline from '../../components/Headline';
jss.setup(preset()); jss.setup(preset());
//Firebase
var firebase = require('../../stores/fb').fb;
// Initialize Cloud Firestore through Firebase
var db = firebase.firestore();
/* /*
############################### ###############################
@ -30,6 +38,7 @@ Components -- END
const PasswordManager = inject("rootStore") ( observer(
class PasswordManager extends Component { class PasswordManager extends Component {
constructor(props){ constructor(props){
super(props); super(props);
@ -40,13 +49,18 @@ class PasswordManager extends Component {
* - / * - /
*/ */
//Stored information
this.stores = this.props.rootStore.stores;
this.toggleNewWindow = this.toggleNewWindow.bind(this); this.toggleNewWindow = this.toggleNewWindow.bind(this);
this.onChangeInput = this.onChangeInput.bind(this); this.onChangeInput = this.onChangeInput.bind(this);
this.state = { this.state = {
newWindowOpen: false, newWindowOpen: false,
key: '', key: '',
search: '' search: '',
data: [],
loading: true
} }
//Styles //Styles
@ -58,6 +72,11 @@ class PasswordManager extends Component {
} }
componentDidMount() {
this.fetchData();
}
componentWillUnmount() { componentWillUnmount() {
this.sheet.detach() this.sheet.detach()
} }
@ -73,11 +92,10 @@ class PasswordManager extends Component {
toggleNewWindow() { toggleNewWindow() {
//Add new password window //Open window to add a new password doc
if(this.state.key !== '') if(this.state.key !== '')
{ {
alertify.log("Encryption key: '" + this.state.key + "'");
this.setState({ this.setState({
newWindowOpen: !this.state.newWindowOpen newWindowOpen: !this.state.newWindowOpen
}); });
@ -87,10 +105,68 @@ class PasswordManager extends Component {
} }
fetchData() {
//Gets password list from firestore and
//Writes in this.state
const uid = this.stores.authStore.userData.uid;
db.collection("passwords").where("uid", "==", uid).orderBy("time", "desc").get().then((querySnapshot) => {
var arr = [];
querySnapshot.forEach((doc) => {
const data = doc.data();
arr.push(data);
});
this.setState({
data: arr,
loading: false
});
});
}
displayData(dataList) {
//Returns array with all table row components
var components = [];
dataList.forEach((element, index) => {
components.push(
<Table.Row key={index}>
<Table.Cell>{element.url}</Table.Cell>
<Table.Cell>{decrypt(element.login, this.state.key)}</Table.Cell>
<Table.Cell>
<Button icon>
<Icon name='copy' />
</Button>
</Table.Cell>
<Table.Cell>{decrypt(element.password, this.state.key)}</Table.Cell>
<Table.Cell>
<Button icon>
<Icon name='copy' />
</Button>
</Table.Cell>
</Table.Row>
);
});
return components;
}
render() { render() {
const tableRows = this.displayData(this.state.data)
return( return(
<div> <div>
<New encryptionkey={this.state.key} open={this.state.newWindowOpen} toggleNewWindow={this.toggleNewWindow} /> <New encryptionkey={this.state.key} open={this.state.newWindowOpen} toggleNewWindow={this.toggleNewWindow} />
<Headline black="Password " red="manager" />
<Input icon placeholder='Search...'>
<input type='text' placeholder='Search...' name="search" onChange={this.onChangeInput} autoComplete="off" />
<Icon name='search' />
</Input>
<Menu attached='top'> <Menu attached='top'>
<Dropdown item icon='wrench' simple> <Dropdown item icon='wrench' simple>
<Dropdown.Menu> <Dropdown.Menu>
@ -107,16 +183,10 @@ class PasswordManager extends Component {
<i className='key icon' /> <i className='key icon' />
</div> </div>
</div> </div>
<div className='ui right aligned category search item'>
<div className='ui transparent icon input'>
<input className='prompt' type='text' placeholder='Search...' name="search" onChange={this.onChangeInput} autoComplete="off" />
<i className='search icon' />
</div>
</div>
</Menu.Menu> </Menu.Menu>
</Menu> </Menu>
<Segment attached='bottom'> <Segment attached='bottom'>
<Table celled striped> <Table celled striped>
<Table.Header> <Table.Header>
@ -130,27 +200,14 @@ class PasswordManager extends Component {
</Table.Header> </Table.Header>
<Table.Body> <Table.Body>
<Table.Row> {tableRows}
<Table.Cell>cell</Table.Cell>
<Table.Cell>cell</Table.Cell>
<Table.Cell>
<Button icon>
<Icon name='copy' />
</Button>
</Table.Cell>
<Table.Cell>cell</Table.Cell>
<Table.Cell>
<Button icon>
<Icon name='copy' />
</Button>
</Table.Cell>
</Table.Row>
</Table.Body> </Table.Body>
</Table> </Table>
<Loader active={this.state.loading} />
</Segment> </Segment>
<Header as="h3"> <Header as="h3">
How does it work? How does it work? Is it safe?
</Header> </Header>
<p> <p>
I ask you to give an encryption key. Once you set that key you can create a new password which is then encrypted with that key. I ask you to give an encryption key. Once you set that key you can create a new password which is then encrypted with that key.
@ -163,7 +220,7 @@ class PasswordManager extends Component {
</p> </p>
<hr /> <hr />
<p> <p>
I advise you to choose one encryption key and use it for all your passwords in your list. Why? Using the same key makes decrypting easier for you because <b>all</b> entries are being decrypted correctly at the same time by using one single key. <b>I advise you to choose one encryption key and use it for all your passwords in your list.</b> Why? Using the same key makes decrypting easier for you because <b>all</b> entries are being decrypted correctly at the same time by using one single key.
</p> </p>
</div> </div>
); );
@ -176,5 +233,6 @@ class PasswordManager extends Component {
} }
} }
} }
));
export default PasswordManager; export default PasswordManager;

View File

@ -0,0 +1,13 @@
function norm_to_ascii(string){return unescape(encodeURIComponent(string))};
function norm_to_unicode(string){return decodeURIComponent(escape(string))};
function crypt_sym(string,k){return String.fromCharCode.apply(undefined,string.split("").map(function(c){return c.charCodeAt(0)^(k||13)}))};
export function encrypt(string, key) {
//Encrypt a string
return btoa(crypt_sym(norm_to_ascii(string), key));
}
export function decrypt(string, key) {
//Decrypt a string
return crypt_sym(norm_to_unicode(atob(string)), key);
}