diff --git a/.gitignore b/.gitignore index 4d50b57..a345197 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,6 @@ npm-debug.log* yarn-debug.log* yarn-error.log* -yarn.lock* \ No newline at end of file +yarn.lock* + +/.firebase \ No newline at end of file diff --git a/src/components/CsgoSkins/Edit.js b/src/components/CsgoSkins/Edit.js new file mode 100644 index 0000000..bbf44e0 --- /dev/null +++ b/src/components/CsgoSkins/Edit.js @@ -0,0 +1,262 @@ +import React, {Component} from 'react'; +import { observer, inject } from 'mobx-react'; +import jss from 'jss'; +import preset from 'jss-preset-default'; +import { Segment, Container, Form, Input, Button, Icon } from 'semantic-ui-react'; +import alertify from 'alertify.js'; + +/* +* Functions import +*/ + +/* +* Component imports +*/ + +jss.setup(preset()); + +//Firebase +var firebase = require('../../stores/fb').fb; +// Initialize Cloud Firestore through Firebase +var db = firebase.firestore(); + + +/* +############################### +Components -- START +############################### +*/ + +/* +############################### +Components -- END +############################### +*/ + + + +const Edit = inject("rootStore") ( observer( + class Edit extends Component { + constructor(props){ + super(props); + //Update an existing document + + /* + * Expected props + * - toggleEditWindow: function + * toggles window: open and close + * - updateDoc: function + * updates doc locally + * - deleteDoc: function + * deletes doc locally + * - editIndex: Int + * index of array with all docs. locale + * - data: [] + * all local doc data. data[editIndex] is the doc, that is to be edited + */ + + //Stored information + this.stores = this.props.rootStore.stores; + + this.handleChange = this.handleChange.bind(this); + this.handleSubmit = this.handleSubmit.bind(this); + this.delete = this.delete.bind(this); + + this.state = { + item: this.props.data[this.props.editIndex].item, + status: this.props.data[this.props.editIndex].status, + boughtfor: this.props.data[this.props.editIndex].boughtfor, + soldfor: this.props.data[this.props.editIndex].soldfor + } + + //Styles + this.styles = this.getStyles(); + this.sheet = jss.createStyleSheet(this.styles); + const {classes} = this.sheet.attach(); + this.classes = classes; + //Styles + } + + + componentWillUnmount() { + this.sheet.detach() + } + + + handleChange(e) { + this.setState({ + [e.target.name]: e.target.value + }); + } + + + delete() { + //Delete doc + + alertify.confirm("Are you sure?", () => { + db.collection("csgoskins/" + this.stores.authStore.userData.uid + "/csgoskins").doc(this.props.data[this.props.editIndex].id).delete().then(() => { + //Delete doc locally + this.props.deleteDoc(this.props.editIndex); + + this.props.toggleEditWindow(); + + alertify.success("Document removed!"); + this.setState({ + loading: false + }); + }).catch((error) => { + console.error("Error updating the document: ", error); + alertify.error("Something went wrong! Please check your internet connection"); + this.setState({ + loading: false + }); + }); + + this.setState({ + loading: true + }); + }); + } + + + handleSubmit() { + //Update a doc locally with the new values + const data = { + item: this.state.item, + boughtfor: this.state.boughtfor, + soldfor: this.state.soldfor, + status: this.state.status, + time: new Date() + } + + const id = this.props.data[this.props.editIndex].id; + + db.collection("csgoskins/" + this.stores.authStore.userData.uid + "/csgoskins").doc(id).set(data) + .then(() => { + console.log("Document updated"); + alertify.success("Document updated!"); + + //Update a doc locally with the new values + this.props.updateDoc(Object.assign(data, {id:id}), this.props.editIndex); + + this.props.toggleEditWindow(); + this.setState({ + loading: false + }); + }) + .catch(function(error) { + console.error("Error updating the document: ", error); + alertify.error("Something went wrong! Please check your internet connection"); + this.setState({ + loading: false + }); + }); + + this.setState({ + loading: true + }); + } + + + render() { + return( +
+ + +
+ + + + + + + + + + + + + + + +
+
+ + + +
+
+
+
+ ); + } + + + getStyles() { + return { + box: { + width: '100vw', + height: '100vh', + position: 'fixed', + zIndex: 3, + top: 0, + left: 0, + backgroundColor: 'rgba(255,255,255,0.9)', + paddingTop: '5%' + } + } + } + } +)); + +export default Edit; diff --git a/src/components/CsgoSkins/New.js b/src/components/CsgoSkins/New.js new file mode 100644 index 0000000..16e51ea --- /dev/null +++ b/src/components/CsgoSkins/New.js @@ -0,0 +1,246 @@ +import React, { Component } from 'react'; +import { observer, inject } from 'mobx-react'; +import jss from 'jss'; +import preset from 'jss-preset-default'; +import { Segment, Container, Header, Form, Button, Icon } from 'semantic-ui-react'; +import alertify from 'alertify.js'; + +/* +* Functions import +*/ + +/* +* Component imports +*/ + +jss.setup(preset()); + +//Firebase +var firebase = require('../../stores/fb').fb; +// Initialize Cloud Firestore through Firebase +var db = firebase.firestore(); + + +/* +############################### +Components -- START +############################### +*/ + +/* +############################### +Components -- END +############################### +*/ + + + +const New = inject("rootStore")(observer( + class New extends Component { + constructor(props) { + super(props); + //Add a new password window + + /* + * Expected props + * - toggleNewWindow: function + * toggles window: open and close + * - addDoc: function + * adds a new doc locally + */ + + //Stored information + this.stores = this.props.rootStore.stores; + + this.handleChange = this.handleChange.bind(this); + this.handleSubmit = this.handleSubmit.bind(this); + + this.state = { + item: '', + baughtfor: 0, + status: 0, + soldfor: 0, + loading: false + } + + //Styles + this.styles = this.getStyles(); + this.sheet = jss.createStyleSheet(this.styles); + const { classes } = this.sheet.attach(); + this.classes = classes; + //Styles + + this.options = [ + { + key: 0, + text: 'Owned', + value: 0 + }, + { + key: 1, + text: 'Pending', + value: 1 + }, + { + key: 2, + text: 'Sold', + value: 2 + } + ] + } + + componentDidMount() { + document.getElementById('item').focus(); + } + + + componentWillUnmount() { + this.sheet.detach(); + } + + + handleChange(e, { value, name }) { + if(name == 'boughtfor' || name == 'soldfor') { + value = parseFloat(value); + } + this.setState({ + [name]: value + }); + } + + + handleSubmit() { + //Update a doc locally with the new values + const data = { + item: this.state.item, + boughtfor: this.state.boughtfor, + soldfor: this.state.soldfor, + status: this.state.status, + time: new Date() + } + + db.collection("csgoskins/" + this.stores.authStore.userData.uid + "/csgoskins").add(data) + .then((docRef) => { + console.log("Document written with ID: ", docRef.id); + alertify.success("Item added!"); + + //Add a new doc locally so no new data transfer from firestore + //is needed + this.props.addDoc(Object.assign(data, { id: docRef.id })); + + this.props.toggleNewWindow(); + this.setState({ + loading: false + }); + }) + .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 + }); + } + + + render() { + return ( +
+ + +
+
+ Add new item +
+ + + + + + + + + + + + + + + + +
+
+
+
+ ); + } + + + getStyles() { + return { + box: { + width: '100vw', + height: '100vh', + position: 'fixed', + zIndex: 3, + top: 0, + left: 0, + backgroundColor: 'rgba(255,255,255,0.9)', + paddingTop: '5%' + } + } + } + } +)); + +export default New; diff --git a/src/components/Menu/Menu.js b/src/components/Menu/Menu.js index dfbf904..0da68c9 100644 --- a/src/components/Menu/Menu.js +++ b/src/components/Menu/Menu.js @@ -115,6 +115,10 @@ const Main = inject("rootStore") ( observer( Password manager + + + Csgo skins + diff --git a/src/components/PasswordManager/Edit.js b/src/components/PasswordManager/Edit.js index 6cfc68e..ba64782 100644 --- a/src/components/PasswordManager/Edit.js +++ b/src/components/PasswordManager/Edit.js @@ -57,8 +57,8 @@ const Edit = inject("rootStore") ( observer( * deletes doc locally * - editIndex: Int * index of array with all docs. locale - * - toggleEditWindow: function - * Open or close edit window + * - data: [] + * all local doc data. data[editIndex] is the doc, that is to be edited */ //Stored information @@ -238,6 +238,7 @@ const Edit = inject("rootStore") ( observer( type="text" placeholder="http://alexbell.ninja" required + autoComplete="off" /> @@ -262,7 +263,8 @@ const Edit = inject("rootStore") ( observer( type="text" placeholder='********' label={} - labelPosition="right" + labelPosition="right" + autoComplete="off" required /> @@ -294,7 +296,7 @@ const Edit = inject("rootStore") ( observer( width: '100vw', height: '100vh', position: 'fixed', - zIndex: 1, + zIndex: 3, top: 0, left: 0, backgroundColor: 'rgba(255,255,255,0.9)', diff --git a/src/components/PasswordManager/New.js b/src/components/PasswordManager/New.js index 35c099f..0ccbfac 100644 --- a/src/components/PasswordManager/New.js +++ b/src/components/PasswordManager/New.js @@ -53,8 +53,6 @@ const New = inject("rootStore") ( observer( * encryption key of the password * - addDoc: function * adds a new doc locally - * - toggleEditWindow: function - * Open or close edit window */ //Stored information @@ -188,6 +186,7 @@ const New = inject("rootStore") ( observer( type="text" placeholder="http://alexbell.ninja" required + autoComplete="off" /> @@ -214,6 +213,7 @@ const New = inject("rootStore") ( observer( label={} labelPosition="right" required + autoComplete="off" /> @@ -239,7 +239,7 @@ const New = inject("rootStore") ( observer( width: '100vw', height: '100vh', position: 'fixed', - zIndex: 1, + zIndex: 3, top: 0, left: 0, backgroundColor: 'rgba(255,255,255,0.9)', diff --git a/src/pages/Main.js b/src/pages/Main.js index 147a25b..52883fa 100644 --- a/src/pages/Main.js +++ b/src/pages/Main.js @@ -15,6 +15,7 @@ import history from '../stores/functions/history'; import Menu from '../components/Menu/Menu'; import Home from './home/Home'; import PasswordManager from './passwordManager/PasswordManager'; +import CsgoSkins from './csgoSkins/CsgoSkins'; jss.setup(preset()); @@ -77,6 +78,7 @@ class Main extends Component { + diff --git a/src/pages/csgoSkins/CsgoSkins.js b/src/pages/csgoSkins/CsgoSkins.js new file mode 100644 index 0000000..fc9ec7e --- /dev/null +++ b/src/pages/csgoSkins/CsgoSkins.js @@ -0,0 +1,323 @@ +import React, {Component} from 'react'; +import { observer, inject } from 'mobx-react'; +import jss from 'jss'; +import preset from 'jss-preset-default'; +import { Menu, Segment, Dropdown, Table, Loader, Icon, Input, Button } from 'semantic-ui-react'; +import alertify from 'alertify.js'; + +/* +* Functions import +*/ + +/* +* Component imports +*/ +import New from '../../components/CsgoSkins/New'; +import Edit from '../../components/CsgoSkins/Edit'; +import Headline from '../../components/Headline'; + +jss.setup(preset()); + +//Firebase +var firebase = require('../../stores/fb').fb; +// Initialize Cloud Firestore through Firebase +var db = firebase.firestore(); + + +/* +############################### +Components -- START +############################### +*/ + +/* +############################### +Components -- END +############################### +*/ + + + +const CsgoSkins = inject("rootStore") ( observer( + class CsgoSkins extends Component { + constructor(props){ + super(props); + //Csgo skins page + + /* + * Expected props + * - / + */ + + //Stored information + this.stores = this.props.rootStore.stores; + + this.toggleNewWindow = this.toggleNewWindow.bind(this); + this.onChangeInput = this.onChangeInput.bind(this); + this.addDoc = this.addDoc.bind(this); + + this.state = { + loading: true, + data: [], + search: '', + newWindowOpen: false, + editWindowOpen: false + } + + //Styles + this.styles = this.getStyles(); + this.sheet = jss.createStyleSheet(this.styles); + const {classes} = this.sheet.attach(); + this.classes = classes; + //Styles + } + + + componentDidMount() { + this.fetchData(); + } + + + componentWillUnmount() { + this.sheet.detach() + } + + + onChangeInput(e) { + //input changes, including search + + this.setState({ + [e.target.name]: e.target.value + }); + } + + + toggleNewWindow() { + //Open window to add a new doc + + this.setState({ + newWindowOpen: !this.state.newWindowOpen + }); + } + + + toggleEditWindow(index) { + //Open window to add a new password doc + + this.setState({ + editWindowOpen: !this.state.editWindowOpen, + editIndex: index + }); + } + + + addDoc(data) { + //Add a new doc locally so no new data transfer from firestore + //is needed + var newArr = []; + newArr[0] = data; + newArr = newArr.concat(this.state.data); + this.setState({ + data: newArr + }); + } + + + updateDoc(data, editIndex) { + //Update a doc locally with the new values + var newArr = this.state.data; + newArr[editIndex] = data; + this.setState({ + data: newArr + }); + } + + + deleteDoc(editIndex) { + //Delete doc locally + + var newArr = []; + this.state.data.forEach(function(element, index) { + if(index !== editIndex) { + newArr.push(element); + } + }); + this.setState({ + data: newArr + }); + } + + + fetchData() { + //Gets item list from firestore and + //Writes in this.state + + const uid = this.stores.authStore.userData.uid; + + db.collection("csgoskins/"+uid+"/csgoskins").orderBy("status", "asc").get().then((querySnapshot) => { + var arr = []; + querySnapshot.forEach((doc) => { + const data = doc.data(); + arr.push(Object.assign(data, {id:doc.id})); + }); + this.setState({ + data: arr, + loading: false + }); + }).catch(function() { + alertify.error("Data could not be loaded. Please check your internet connection"); + }); + } + + + displayData(dataList) { + //Returns array with all table row components + var components = []; + + var toggleEditWindow = () => {}; + var soldfor; + var status; + var diff; + var diffComponent; + + dataList.forEach((element, index) => { + if(element.item.toLowerCase().includes(this.state.search.toLowerCase())) { + toggleEditWindow = () => { + this.toggleEditWindow(index); + } + + //Generate literal status from numerical status + soldfor = element.status != 0 ? element.soldfor : '-'; + status = { + 0: Owned, + 1: Pending, + 2: Sold + }[element.status]; + + //View win under soldfor + diff = Math.round((element.soldfor-element.boughtfor)*100)/100; + diffComponent = null; + if(element.status == 2) { + if(diff > 0) { + diffComponent =
+ {diff}
; + } else if(diff < 0) { + diffComponent =
- {-diff}
; + } else { + diffComponent =
+/- 0
; + } + } + + components.push( + + + { /*element.status != 2 ? : null */} + + {element.item} + {element.boughtfor} + + {soldfor} + {diffComponent} + + + {status} + + ); + } + }); + + return components; + } + + + render() { + const tableRows = this.displayData(this.state.data); + + //Calculate the total win/loss + var totalDiff = 0; + var totalDiffComponent; + this.state.data.forEach((value) => { + if(value.status == 2) { + totalDiff += value.soldfor; + totalDiff -= value.boughtfor; + } + }); + totalDiff = Math.round(totalDiff*100)/100 + if(totalDiff > 0) { + totalDiffComponent =
+ {totalDiff}
; + } else if(totalDiff < 0) { + totalDiffComponent =
- {-totalDiff}
; + } else { + totalDiffComponent =
+/- 0
; + } + + return( +
+ { this.state.newWindowOpen ? : null } + { this.state.editWindowOpen ? : null } + + + + + + + + + + + + + New + + + + + + + + + + + + Item + Bought for + + Sold for + {totalDiffComponent} + + Market price + Status + + + + + {tableRows} + +
+ +
+
+ ); + } + + + getStyles() { + return { + + } + } + } +)); + +export default CsgoSkins; diff --git a/src/pages/home/Home.js b/src/pages/home/Home.js index 1bab6e3..162ff85 100644 --- a/src/pages/home/Home.js +++ b/src/pages/home/Home.js @@ -59,9 +59,7 @@ class Home extends Component {

- You are signed in on alexbell.ninja. What can you do here?
- 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 + Yo, what's up? Ninja here! What can you do here? Feel free to browse around a little and try some of what you can find here :)

diff --git a/src/pages/passwordManager/PasswordManager.js b/src/pages/passwordManager/PasswordManager.js index f4db30e..fdbfa2c 100644 --- a/src/pages/passwordManager/PasswordManager.js +++ b/src/pages/passwordManager/PasswordManager.js @@ -295,7 +295,7 @@ const PasswordManager = inject("rootStore") ( observer( render() { - const tableRows = this.displayData(this.state.data) + const tableRows = this.displayData(this.state.data); return(
@@ -314,6 +314,7 @@ const PasswordManager = inject("rootStore") ( observer( onChange={this.onChangeInput} iconPosition='left' transparent + autoComplete="off" > @@ -358,28 +359,6 @@ const PasswordManager = inject("rootStore") ( observer( - -
- How does it work? Is it safe? -
-

- 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. -

-

- The password is then saved in the encrypted form on the database in the internet, which means that nobody (not even me) can read your password unless that person knows the key. In other words: The password is completely safe and solely accessible by you and only you! -

-

- To decrypt your passwords simply type in the encryption key and all your passwords in your list are displayed correctly and readable in the form you had set them. -

-
-

- 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 all entries are being decrypted correctly at the same time by using one single key. -

-
-

- For nerds: AES (Rijndael cipher) - encrypted with a 128 bits key, 10 rounds and a blocksize of 128 bits. Established by the U.S. NIST in 2001 and approved by the NSA for "top secret" information. -

); }