adding and fetching docs
This commit is contained in:
parent
d9aa8deb16
commit
8d93af6552
Binary file not shown.
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 1.1 KiB |
@ -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>
|
||||||
|
@ -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();
|
||||||
|
17
src/components/Headline.js
Normal file
17
src/components/Headline.js
Normal 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>
|
||||||
|
)
|
||||||
|
}
|
@ -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>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1
src/files/images/ninja.svg
Normal file
1
src/files/images/ninja.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 13 KiB |
@ -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>
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
13
src/stores/functions/encryption.js
Normal file
13
src/stores/functions/encryption.js
Normal 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);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user