Migrate AppStore to mobx
This commit is contained in:
parent
667648b0c9
commit
2871e22a61
|
|
@ -1,53 +0,0 @@
|
||||||
import axios, {AxiosResponse} from 'axios';
|
|
||||||
import * as config from '../config';
|
|
||||||
import dispatcher from '../stores/dispatcher';
|
|
||||||
import {snack} from './GlobalAction';
|
|
||||||
|
|
||||||
/** Fetches all applications. */
|
|
||||||
export function fetchApps() {
|
|
||||||
axios.get(config.get('url') + 'application').then((resp: AxiosResponse<IMessage[]>) => {
|
|
||||||
dispatcher.dispatch({type: 'UPDATE_APPS', payload: resp.data});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete an application by id.
|
|
||||||
* @param {int} id the application id
|
|
||||||
*/
|
|
||||||
export function deleteApp(id: number) {
|
|
||||||
axios
|
|
||||||
.delete(config.get('url') + 'application/' + id)
|
|
||||||
.then(() => {
|
|
||||||
fetchApps();
|
|
||||||
dispatcher.dispatch({type: 'DELETE_MESSAGES', payload: id});
|
|
||||||
})
|
|
||||||
.then(() => snack('Application deleted'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an application.
|
|
||||||
* @param {string} name the application name
|
|
||||||
* @param {string} description the description of the application.
|
|
||||||
*/
|
|
||||||
export function createApp(name: string, description: string) {
|
|
||||||
axios
|
|
||||||
.post(config.get('url') + 'application', {name, description})
|
|
||||||
.then(fetchApps)
|
|
||||||
.then(() => snack('Application created'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Upload an image for an application.
|
|
||||||
* @param {int} id the application id
|
|
||||||
* @param {Blob} file the description of the application.
|
|
||||||
*/
|
|
||||||
export function uploadImage(id: number, file: Blob) {
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append('file', file);
|
|
||||||
axios
|
|
||||||
.post(config.get('url') + 'application/' + id + '/image', formData, {
|
|
||||||
headers: {'content-type': 'multipart/form-data'},
|
|
||||||
})
|
|
||||||
.then(fetchApps)
|
|
||||||
.then(() => snack('Application image updated'));
|
|
||||||
}
|
|
||||||
|
|
@ -6,6 +6,7 @@ import {StyleRules, Theme, WithStyles, withStyles} from '@material-ui/core/style
|
||||||
import React, {Component} from 'react';
|
import React, {Component} from 'react';
|
||||||
import {Link} from 'react-router-dom';
|
import {Link} from 'react-router-dom';
|
||||||
import AppStore from '../stores/AppStore';
|
import AppStore from '../stores/AppStore';
|
||||||
|
import {observer} from 'mobx-react';
|
||||||
|
|
||||||
const styles = (theme: Theme): StyleRules<'drawerPaper' | 'toolbar' | 'link'> => ({
|
const styles = (theme: Theme): StyleRules<'drawerPaper' | 'toolbar' | 'link'> => ({
|
||||||
drawerPaper: {
|
drawerPaper: {
|
||||||
|
|
@ -27,24 +28,11 @@ interface IProps {
|
||||||
loggedIn: boolean;
|
loggedIn: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
@observer
|
||||||
apps: IApplication[];
|
class Navigation extends Component<IProps & Styles> {
|
||||||
}
|
|
||||||
|
|
||||||
class Navigation extends Component<IProps & Styles, IState> {
|
|
||||||
public state: IState = {apps: []};
|
|
||||||
|
|
||||||
public componentWillMount() {
|
|
||||||
AppStore.on('change', this.updateApps);
|
|
||||||
}
|
|
||||||
|
|
||||||
public componentWillUnmount() {
|
|
||||||
AppStore.removeListener('change', this.updateApps);
|
|
||||||
}
|
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const {classes, loggedIn} = this.props;
|
const {classes, loggedIn} = this.props;
|
||||||
const {apps} = this.state;
|
const apps = AppStore.getItems();
|
||||||
|
|
||||||
const userApps =
|
const userApps =
|
||||||
apps.length === 0
|
apps.length === 0
|
||||||
|
|
@ -88,8 +76,6 @@ class Navigation extends Component<IProps & Styles, IState> {
|
||||||
</Drawer>
|
</Drawer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateApps = () => this.setState({apps: AppStore.get()});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withStyles(styles, {withTheme: true})<IProps>(Navigation);
|
export default withStyles(styles, {withTheme: true})<IProps>(Navigation);
|
||||||
|
|
|
||||||
|
|
@ -10,42 +10,36 @@ import TableRow from '@material-ui/core/TableRow';
|
||||||
import Delete from '@material-ui/icons/Delete';
|
import Delete from '@material-ui/icons/Delete';
|
||||||
import Edit from '@material-ui/icons/Edit';
|
import Edit from '@material-ui/icons/Edit';
|
||||||
import React, {ChangeEvent, Component, SFC} from 'react';
|
import React, {ChangeEvent, Component, SFC} from 'react';
|
||||||
import * as AppAction from '../actions/AppAction';
|
|
||||||
import ConfirmDialog from '../component/ConfirmDialog';
|
import ConfirmDialog from '../component/ConfirmDialog';
|
||||||
import DefaultPage from '../component/DefaultPage';
|
import DefaultPage from '../component/DefaultPage';
|
||||||
import ToggleVisibility from '../component/ToggleVisibility';
|
import ToggleVisibility from '../component/ToggleVisibility';
|
||||||
import AppStore from '../stores/AppStore';
|
|
||||||
import AddApplicationDialog from './dialog/AddApplicationDialog';
|
import AddApplicationDialog from './dialog/AddApplicationDialog';
|
||||||
|
import AppStore from '../stores/AppStore';
|
||||||
|
import {observer} from 'mobx-react';
|
||||||
|
import {observable} from 'mobx';
|
||||||
|
|
||||||
interface IState {
|
@observer
|
||||||
apps: IApplication[];
|
class Applications extends Component {
|
||||||
createDialog: boolean;
|
@observable
|
||||||
deleteId: number;
|
private deleteId: number | false = false;
|
||||||
}
|
@observable
|
||||||
|
private createDialog = false;
|
||||||
|
|
||||||
class Applications extends Component<{}, IState> {
|
|
||||||
public state = {apps: [], createDialog: false, deleteId: -1};
|
|
||||||
private uploadId = -1;
|
private uploadId = -1;
|
||||||
private upload: HTMLInputElement | null = null;
|
private upload: HTMLInputElement | null = null;
|
||||||
|
|
||||||
public componentWillMount() {
|
public componentDidMount = AppStore.refresh;
|
||||||
AppStore.on('change', this.updateApps);
|
|
||||||
this.updateApps();
|
|
||||||
}
|
|
||||||
|
|
||||||
public componentWillUnmount() {
|
|
||||||
AppStore.removeListener('change', this.updateApps);
|
|
||||||
}
|
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const {apps, createDialog, deleteId} = this.state;
|
const {createDialog, deleteId} = this;
|
||||||
|
const apps = AppStore.getItems();
|
||||||
return (
|
return (
|
||||||
<DefaultPage
|
<DefaultPage
|
||||||
title="Applications"
|
title="Applications"
|
||||||
buttonTitle="Create Application"
|
buttonTitle="Create Application"
|
||||||
buttonId="create-app"
|
buttonId="create-app"
|
||||||
maxWidth={1000}
|
maxWidth={1000}
|
||||||
fButton={this.showCreateDialog}>
|
fButton={() => (this.createDialog = true)}>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<Paper elevation={6}>
|
<Paper elevation={6}>
|
||||||
<Table id="app-table">
|
<Table id="app-table">
|
||||||
|
|
@ -68,7 +62,7 @@ class Applications extends Component<{}, IState> {
|
||||||
name={app.name}
|
name={app.name}
|
||||||
value={app.token}
|
value={app.token}
|
||||||
fUpload={() => this.uploadImage(app.id)}
|
fUpload={() => this.uploadImage(app.id)}
|
||||||
fDelete={() => this.showCloseDialog(app.id)}
|
fDelete={() => (this.deleteId = app.id)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
@ -84,23 +78,22 @@ class Applications extends Component<{}, IState> {
|
||||||
</Grid>
|
</Grid>
|
||||||
{createDialog && (
|
{createDialog && (
|
||||||
<AddApplicationDialog
|
<AddApplicationDialog
|
||||||
fClose={this.hideCreateDialog}
|
fClose={() => (this.createDialog = false)}
|
||||||
fOnSubmit={AppAction.createApp}
|
fOnSubmit={AppStore.create}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{deleteId !== -1 && (
|
{deleteId !== false && (
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
title="Confirm Delete"
|
title="Confirm Delete"
|
||||||
text={'Delete ' + AppStore.getById(deleteId).name + '?'}
|
text={'Delete ' + AppStore.getByID(deleteId).name + '?'}
|
||||||
fClose={this.hideCloseDialog}
|
fClose={() => (this.deleteId = false)}
|
||||||
fOnSubmit={() => AppAction.deleteApp(deleteId)}
|
fOnSubmit={() => AppStore.remove(deleteId)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</DefaultPage>
|
</DefaultPage>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateApps = () => this.setState({...this.state, apps: AppStore.get()});
|
|
||||||
private uploadImage = (id: number) => {
|
private uploadImage = (id: number) => {
|
||||||
this.uploadId = id;
|
this.uploadId = id;
|
||||||
if (this.upload) {
|
if (this.upload) {
|
||||||
|
|
@ -114,17 +107,11 @@ class Applications extends Component<{}, IState> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (['image/png', 'image/jpeg', 'image/gif'].indexOf(file.type) !== -1) {
|
if (['image/png', 'image/jpeg', 'image/gif'].indexOf(file.type) !== -1) {
|
||||||
AppAction.uploadImage(this.uploadId, file);
|
AppStore.uploadImage(this.uploadId, file);
|
||||||
} else {
|
} else {
|
||||||
alert('Uploaded file must be of type png, jpeg or gif.');
|
alert('Uploaded file must be of type png, jpeg or gif.');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
private showCreateDialog = () => this.setState({...this.state, createDialog: true});
|
|
||||||
|
|
||||||
private hideCreateDialog = () => this.setState({...this.state, createDialog: false});
|
|
||||||
private showCloseDialog = (deleteId: number) => this.setState({...this.state, deleteId});
|
|
||||||
|
|
||||||
private hideCloseDialog = () => this.setState({...this.state, deleteId: -1});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IRowProps {
|
interface IRowProps {
|
||||||
|
|
@ -136,7 +123,7 @@ interface IRowProps {
|
||||||
fDelete: VoidFunction;
|
fDelete: VoidFunction;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Row: SFC<IRowProps> = ({name, value, description, fDelete, fUpload, image}) => (
|
const Row: SFC<IRowProps> = observer(({name, value, description, fDelete, fUpload, image}) => (
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell padding="checkbox">
|
<TableCell padding="checkbox">
|
||||||
<div style={{display: 'flex'}}>
|
<div style={{display: 'flex'}}>
|
||||||
|
|
@ -157,6 +144,6 @@ const Row: SFC<IRowProps> = ({name, value, description, fDelete, fUpload, image}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
);
|
));
|
||||||
|
|
||||||
export default Applications;
|
export default Applications;
|
||||||
|
|
|
||||||
|
|
@ -1,38 +1,48 @@
|
||||||
import {EventEmitter} from 'events';
|
import {BaseStore} from './BaseStore';
|
||||||
import dispatcher, {IEvent} from './dispatcher';
|
import axios from 'axios';
|
||||||
|
import * as config from '../config';
|
||||||
|
import {action} from 'mobx';
|
||||||
|
import SnackManager, {SnackReporter} from './SnackManager';
|
||||||
|
|
||||||
class AppStore extends EventEmitter {
|
class NewAppStore extends BaseStore<IApplication> {
|
||||||
private apps: IApplication[] = [];
|
public constructor(private readonly snack: SnackReporter) {
|
||||||
|
super();
|
||||||
public get(): IApplication[] {
|
|
||||||
return this.apps;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public getById(id: number): IApplication {
|
protected requestItems = (): Promise<IApplication[]> => {
|
||||||
const app = this.getByIdOrUndefined(id);
|
return axios
|
||||||
if (!app) {
|
.get<IApplication[]>(`${config.get('url')}application`)
|
||||||
throw new Error('app is required to exist');
|
.then((response) => response.data);
|
||||||
}
|
};
|
||||||
return app;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getName(id: number): string {
|
protected requestDelete = (id: number): Promise<void> => {
|
||||||
const app = this.getByIdOrUndefined(id);
|
return axios
|
||||||
|
.delete(`${config.get('url')}application/${id}`)
|
||||||
|
.then(() => this.snack('Application deleted'));
|
||||||
|
};
|
||||||
|
|
||||||
|
@action
|
||||||
|
public uploadImage = async (id: number, file: Blob): Promise<void> => {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('file', file);
|
||||||
|
await axios.post(`${config.get('url')}application/${id}/image`, formData, {
|
||||||
|
headers: {'content-type': 'multipart/form-data'},
|
||||||
|
});
|
||||||
|
await this.refresh();
|
||||||
|
this.snack('Application image updated');
|
||||||
|
};
|
||||||
|
|
||||||
|
@action
|
||||||
|
public create = async (name: string, description: string): Promise<void> => {
|
||||||
|
await axios.post(`${config.get('url')}application`, {name, description});
|
||||||
|
await this.refresh();
|
||||||
|
this.snack('Application created');
|
||||||
|
};
|
||||||
|
|
||||||
|
public getName = (id: number): string => {
|
||||||
|
const app = this.getByIDOrUndefined(id);
|
||||||
return id === -1 ? 'All Messages' : app !== undefined ? app.name : 'unknown';
|
return id === -1 ? 'All Messages' : app !== undefined ? app.name : 'unknown';
|
||||||
}
|
};
|
||||||
|
|
||||||
public handle(data: IEvent): void {
|
|
||||||
if (data.type === 'UPDATE_APPS') {
|
|
||||||
this.apps = data.payload;
|
|
||||||
this.emit('change');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private getByIdOrUndefined(id: number): IApplication | undefined {
|
|
||||||
return this.apps.find((a) => a.id === id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const store = new AppStore();
|
export default new NewAppStore(SnackManager.snack);
|
||||||
dispatcher.register(store.handle.bind(store));
|
|
||||||
export default store;
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue