csgo skin manager added. for now only add function available

This commit is contained in:
2XJJ2BJZ7z 2020-02-22 22:24:34 +01:00
parent 0a423c8e90
commit 9ae3ddbdcf
10 changed files with 852 additions and 34 deletions

4
.gitignore vendored
View File

@ -19,4 +19,6 @@
npm-debug.log*
yarn-debug.log*
yarn-error.log*
yarn.lock*
yarn.lock*
/.firebase

View File

@ -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(
<div className={this.classes.box}>
<Container text>
<Segment padded="very">
<Form onSubmit={this.handleSubmit}>
<Form.Group>
<Form.Input
label="Item name"
name="item"
id="item"
onChange={this.handleChange}
type="text"
placeholder="AWP | Containment Breach"
required
width={10}
defaultValue={this.props.data[this.props.editIndex].item}
autoComplete="off"
/>
<Form.Select
label="Status"
placeholder='Select status'
name="status"
id="status"
onChange={this.handleChange}
fluid
value={this.state.status}
options={this.options}
required
width={6}
defaultValue={this.props.data[this.props.editIndex].status}
/>
</Form.Group>
<Form.Group>
<Form.Input
pattern="[1-9][0-9]*(\.[0-9]{1,2}){0,1}"
label="Bought for"
icon="euro"
name="boughtfor"
id="boughtfor"
onChange={this.handleChange}
type="text"
placeholder="3.87"
required
width={8}
defaultValue={this.props.data[this.props.editIndex].boughtfor}
autoComplete="off"
/>
<Form.Input
pattern="[1-9][0-9]*(\.[0-9]{1,2}){0,1}"
label="Sold for"
icon="euro"
name="soldfor"
id="soldfor"
onChange={this.handleChange}
type="text"
placeholder="4.22"
required={!this.state.status==0}
disabled={this.state.status==0}
width={8}
defaultValue={this.props.data[this.props.editIndex].soldfor}
autoComplete="off"
/>
</Form.Group>
<Button loading={this.state.loading} type='submit' primary>Update</Button>
<Button loading={this.state.loading} type="button" onClick={this.props.toggleEditWindow}><Icon name="x" />Cancel</Button>
<br />
<br />
<Button loading={this.state.loading} type="button" onClick={this.delete} icon color="red"><Icon name="trash alternate" /></Button>
</Form>
</Segment>
</Container>
</div>
);
}
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;

View File

@ -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 (
<div className={this.classes.box}>
<Container text>
<Segment padded="very">
<Form onSubmit={this.handleSubmit}>
<Header as="h2">
Add new item
</Header>
<Form.Group>
<Form.Input
label="Item name"
name="item"
id="item"
onChange={this.handleChange}
type="text"
placeholder="AWP | Containment Breach"
required
width={10}
autoComplete="off"
/>
<Form.Select
label="Status"
placeholder='Select status'
name="status"
id="status"
onChange={this.handleChange}
fluid
value={this.state.status}
options={this.options}
required
width={6}
/>
</Form.Group>
<Form.Group>
<Form.Input
pattern="[1-9][0-9]*(\.[0-9]{1,2}){0,1}"
label="Bought for"
icon="euro"
name="boughtfor"
id="boughtfor"
onChange={this.handleChange}
type="text"
placeholder="3.87"
required
width={8}
autoComplete="off"
/>
<Form.Input
pattern="[1-9][0-9]*(\.[0-9]{1,2}){0,1}"
label="Sold for"
icon="euro"
name="soldfor"
id="soldfor"
onChange={this.handleChange}
type="text"
placeholder="4.22"
required={!this.state.status == 0}
disabled={this.state.status == 0}
width={8}
autoComplete="off"
/>
</Form.Group>
<Button loading={this.state.loading} type='submit' primary>Add</Button>
<Button loading={this.state.loading} onClick={this.props.toggleNewWindow}><Icon name="x" />Cancel</Button>
</Form>
</Segment>
</Container>
</div>
);
}
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;

View File

@ -115,6 +115,10 @@ const Main = inject("rootStore") ( observer(
<Menu.Item name='/p' active={activeItem === '/p'} onClick={this.handleItemClick} as='a'>
Password manager
</Menu.Item>
<Menu.Item name='/c' active={activeItem === '/c'} onClick={this.handleItemClick} as='a'>
Csgo skins
</Menu.Item>
<Menu.Menu position='right'>
<Menu.Item>

View File

@ -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"
/>
</Form.Field>
@ -262,7 +263,8 @@ const Edit = inject("rootStore") ( observer(
type="text"
placeholder='********'
label={<Button onClick={this.dice} type="button" icon><Icon name='random' /></Button>}
labelPosition="right"
labelPosition="right"
autoComplete="off"
required
/>
</Form.Field>
@ -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)',

View File

@ -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"
/>
</Form.Field>
@ -214,6 +213,7 @@ const New = inject("rootStore") ( observer(
label={<Button onClick={this.dice} type="button" icon><Icon name='random' /></Button>}
labelPosition="right"
required
autoComplete="off"
/>
</Form.Field>
@ -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)',

View File

@ -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 {
<Switch>
<Route exact path="/" component={Home} />
<Route path="/p" component={PasswordManager} />
<Route path="/c" component={CsgoSkins} />
</Switch>
<img alt="" className={this.classes.ninja} src={require('../files/images/ninja.svg')} />
</Segment>

View File

@ -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: <span style={{color: '#00a2ff'}}>Owned</span>,
1: <span style={{color: '#ff5e00'}}>Pending</span>,
2: <span style={{color: '#9d00ff'}}>Sold</span>
}[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 = <div style={{color: 'green'}}>+ {diff}</div>;
} else if(diff < 0) {
diffComponent = <div style={{color: 'red'}}>- {-diff}</div>;
} else {
diffComponent = <div>+/- 0</div>;
}
}
components.push(
<Table.Row key={index}>
<Table.Cell>
{ /*element.status != 2 ? <Button icon onClick={toggleEditWindow}>
<Icon name='edit' />
</Button> : null */}
</Table.Cell>
<Table.Cell>{element.item}</Table.Cell>
<Table.Cell>{element.boughtfor}</Table.Cell>
<Table.Cell>
{soldfor}
{diffComponent}
</Table.Cell>
<Table.Cell></Table.Cell>
<Table.Cell>{status}</Table.Cell>
</Table.Row>
);
}
});
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 = <div style={{color: 'green'}}>+ {totalDiff}</div>;
} else if(totalDiff < 0) {
totalDiffComponent = <div style={{color: 'red'}}>- {-totalDiff}</div>;
} else {
totalDiffComponent = <div>+/- 0</div>;
}
return(
<div>
{ this.state.newWindowOpen ? <New addDoc={this.addDoc} toggleNewWindow={this.toggleNewWindow} /> : null }
{ this.state.editWindowOpen ? <Edit toggleNewWindow={this.toggleNewWindow} /> : null }
<Headline black="Csgo " red="skins" />
<Input
icon
placeholder='Search...'
type='text'
id="search"
name="search"
onChange={this.onChangeInput}
iconPosition='left'
transparent
autoComplete="off"
>
<Icon name='search' />
<input />
</Input>
<Menu attached='top'>
<Dropdown item icon='wrench' simple>
<Dropdown.Menu>
<Dropdown.Item onClick={this.toggleNewWindow}>
New
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
</Menu>
<Segment attached='bottom'>
<Table celled striped>
<Table.Header>
<Table.Row>
<Table.HeaderCell width={1}></Table.HeaderCell>
<Table.HeaderCell width={3}>Item</Table.HeaderCell>
<Table.HeaderCell width={3}>Bought for</Table.HeaderCell>
<Table.HeaderCell width={3}>
Sold for
{totalDiffComponent}
</Table.HeaderCell>
<Table.HeaderCell width={3}>Market price</Table.HeaderCell>
<Table.HeaderCell width={2}>Status</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{tableRows}
</Table.Body>
</Table>
<Loader active={this.state.loading} />
</Segment>
</div>
);
}
getStyles() {
return {
}
}
}
));
export default CsgoSkins;

View File

@ -59,9 +59,7 @@ class Home extends Component {
<Headline black="alexbell." red="ninja" />
<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
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 :)
</p>
</Container>

View File

@ -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(
<div>
@ -314,6 +314,7 @@ const PasswordManager = inject("rootStore") ( observer(
onChange={this.onChangeInput}
iconPosition='left'
transparent
autoComplete="off"
>
<Icon name='search' />
<input />
@ -358,28 +359,6 @@ const PasswordManager = inject("rootStore") ( observer(
</Table>
<Loader active={this.state.loading} />
</Segment>
<Header as="h3">
How does it work? Is it safe?
</Header>
<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.
</p>
<p>
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: <b>The password is completely safe and solely accessible by you and only you!</b>
</p>
<p>
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.
</p>
<hr />
<p>
<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>
<hr />
<p>
<b>For nerds: </b><a href="https://en.wikipedia.org/wiki/Advanced_Encryption_Standard" target="_blank" rel="noopener noreferrer">AES (Rijndael cipher) </a>
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.
</p>
</div>
);
}