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 {Link} from 'react-router-dom';
|
||||
import AppStore from '../stores/AppStore';
|
||||
import {observer} from 'mobx-react';
|
||||
|
||||
const styles = (theme: Theme): StyleRules<'drawerPaper' | 'toolbar' | 'link'> => ({
|
||||
drawerPaper: {
|
||||
|
|
@ -27,24 +28,11 @@ interface IProps {
|
|||
loggedIn: boolean;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
apps: IApplication[];
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@observer
|
||||
class Navigation extends Component<IProps & Styles> {
|
||||
public render() {
|
||||
const {classes, loggedIn} = this.props;
|
||||
const {apps} = this.state;
|
||||
const apps = AppStore.getItems();
|
||||
|
||||
const userApps =
|
||||
apps.length === 0
|
||||
|
|
@ -88,8 +76,6 @@ class Navigation extends Component<IProps & Styles, IState> {
|
|||
</Drawer>
|
||||
);
|
||||
}
|
||||
|
||||
private updateApps = () => this.setState({apps: AppStore.get()});
|
||||
}
|
||||
|
||||
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 Edit from '@material-ui/icons/Edit';
|
||||
import React, {ChangeEvent, Component, SFC} from 'react';
|
||||
import * as AppAction from '../actions/AppAction';
|
||||
import ConfirmDialog from '../component/ConfirmDialog';
|
||||
import DefaultPage from '../component/DefaultPage';
|
||||
import ToggleVisibility from '../component/ToggleVisibility';
|
||||
import AppStore from '../stores/AppStore';
|
||||
import AddApplicationDialog from './dialog/AddApplicationDialog';
|
||||
import AppStore from '../stores/AppStore';
|
||||
import {observer} from 'mobx-react';
|
||||
import {observable} from 'mobx';
|
||||
|
||||
interface IState {
|
||||
apps: IApplication[];
|
||||
createDialog: boolean;
|
||||
deleteId: number;
|
||||
}
|
||||
@observer
|
||||
class Applications extends Component {
|
||||
@observable
|
||||
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 upload: HTMLInputElement | null = null;
|
||||
|
||||
public componentWillMount() {
|
||||
AppStore.on('change', this.updateApps);
|
||||
this.updateApps();
|
||||
}
|
||||
|
||||
public componentWillUnmount() {
|
||||
AppStore.removeListener('change', this.updateApps);
|
||||
}
|
||||
public componentDidMount = AppStore.refresh;
|
||||
|
||||
public render() {
|
||||
const {apps, createDialog, deleteId} = this.state;
|
||||
const {createDialog, deleteId} = this;
|
||||
const apps = AppStore.getItems();
|
||||
return (
|
||||
<DefaultPage
|
||||
title="Applications"
|
||||
buttonTitle="Create Application"
|
||||
buttonId="create-app"
|
||||
maxWidth={1000}
|
||||
fButton={this.showCreateDialog}>
|
||||
fButton={() => (this.createDialog = true)}>
|
||||
<Grid item xs={12}>
|
||||
<Paper elevation={6}>
|
||||
<Table id="app-table">
|
||||
|
|
@ -68,7 +62,7 @@ class Applications extends Component<{}, IState> {
|
|||
name={app.name}
|
||||
value={app.token}
|
||||
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>
|
||||
{createDialog && (
|
||||
<AddApplicationDialog
|
||||
fClose={this.hideCreateDialog}
|
||||
fOnSubmit={AppAction.createApp}
|
||||
fClose={() => (this.createDialog = false)}
|
||||
fOnSubmit={AppStore.create}
|
||||
/>
|
||||
)}
|
||||
{deleteId !== -1 && (
|
||||
{deleteId !== false && (
|
||||
<ConfirmDialog
|
||||
title="Confirm Delete"
|
||||
text={'Delete ' + AppStore.getById(deleteId).name + '?'}
|
||||
fClose={this.hideCloseDialog}
|
||||
fOnSubmit={() => AppAction.deleteApp(deleteId)}
|
||||
text={'Delete ' + AppStore.getByID(deleteId).name + '?'}
|
||||
fClose={() => (this.deleteId = false)}
|
||||
fOnSubmit={() => AppStore.remove(deleteId)}
|
||||
/>
|
||||
)}
|
||||
</DefaultPage>
|
||||
);
|
||||
}
|
||||
|
||||
private updateApps = () => this.setState({...this.state, apps: AppStore.get()});
|
||||
private uploadImage = (id: number) => {
|
||||
this.uploadId = id;
|
||||
if (this.upload) {
|
||||
|
|
@ -114,17 +107,11 @@ class Applications extends Component<{}, IState> {
|
|||
return;
|
||||
}
|
||||
if (['image/png', 'image/jpeg', 'image/gif'].indexOf(file.type) !== -1) {
|
||||
AppAction.uploadImage(this.uploadId, file);
|
||||
AppStore.uploadImage(this.uploadId, file);
|
||||
} else {
|
||||
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 {
|
||||
|
|
@ -136,7 +123,7 @@ interface IRowProps {
|
|||
fDelete: VoidFunction;
|
||||
}
|
||||
|
||||
const Row: SFC<IRowProps> = ({name, value, description, fDelete, fUpload, image}) => (
|
||||
const Row: SFC<IRowProps> = observer(({name, value, description, fDelete, fUpload, image}) => (
|
||||
<TableRow>
|
||||
<TableCell padding="checkbox">
|
||||
<div style={{display: 'flex'}}>
|
||||
|
|
@ -157,6 +144,6 @@ const Row: SFC<IRowProps> = ({name, value, description, fDelete, fUpload, image}
|
|||
</IconButton>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
));
|
||||
|
||||
export default Applications;
|
||||
|
|
|
|||
|
|
@ -1,38 +1,48 @@
|
|||
import {EventEmitter} from 'events';
|
||||
import dispatcher, {IEvent} from './dispatcher';
|
||||
import {BaseStore} from './BaseStore';
|
||||
import axios from 'axios';
|
||||
import * as config from '../config';
|
||||
import {action} from 'mobx';
|
||||
import SnackManager, {SnackReporter} from './SnackManager';
|
||||
|
||||
class AppStore extends EventEmitter {
|
||||
private apps: IApplication[] = [];
|
||||
|
||||
public get(): IApplication[] {
|
||||
return this.apps;
|
||||
class NewAppStore extends BaseStore<IApplication> {
|
||||
public constructor(private readonly snack: SnackReporter) {
|
||||
super();
|
||||
}
|
||||
|
||||
public getById(id: number): IApplication {
|
||||
const app = this.getByIdOrUndefined(id);
|
||||
if (!app) {
|
||||
throw new Error('app is required to exist');
|
||||
}
|
||||
return app;
|
||||
}
|
||||
protected requestItems = (): Promise<IApplication[]> => {
|
||||
return axios
|
||||
.get<IApplication[]>(`${config.get('url')}application`)
|
||||
.then((response) => response.data);
|
||||
};
|
||||
|
||||
public getName(id: number): string {
|
||||
const app = this.getByIdOrUndefined(id);
|
||||
protected requestDelete = (id: number): Promise<void> => {
|
||||
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';
|
||||
};
|
||||
}
|
||||
|
||||
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();
|
||||
dispatcher.register(store.handle.bind(store));
|
||||
export default store;
|
||||
export default new NewAppStore(SnackManager.snack);
|
||||
|
|
|
|||
Loading…
Reference in New Issue