Use inject everywhere

This commit is contained in:
Jannis Mattheis 2018-10-21 15:46:08 +02:00
parent bbb344be72
commit d8c413df03
13 changed files with 172 additions and 119 deletions

View File

@ -15,9 +15,9 @@ import Clients from './pages/Clients';
import Login from './pages/Login'; import Login from './pages/Login';
import Messages from './pages/Messages'; import Messages from './pages/Messages';
import Users from './pages/Users'; import Users from './pages/Users';
import {currentUser} from './stores/CurrentUser';
import {observer} from 'mobx-react'; import {observer} from 'mobx-react';
import {observable} from 'mobx'; import {observable} from 'mobx';
import {inject, Stores} from './inject';
const lightTheme = createMuiTheme({ const lightTheme = createMuiTheme({
palette: { palette: {
@ -40,7 +40,7 @@ const styles = (theme: Theme) => ({
}); });
@observer @observer
class Layout extends React.Component<WithStyles<'content'>> { class Layout extends React.Component<WithStyles<'content'> & Stores<'currentUser'>> {
private static defaultVersion = '0.0.0'; private static defaultVersion = '0.0.0';
@observable @observable
@ -59,14 +59,16 @@ class Layout extends React.Component<WithStyles<'content'>> {
} }
public render() { public render() {
const {
loggedIn,
authenticating,
user: {name, admin},
} = currentUser;
const {version, showSettings, darkThemeVisible} = this; const {version, showSettings, darkThemeVisible} = this;
const {classes} = this.props; const {
classes,
currentUser: {
loggedIn,
authenticating,
user: {name, admin},
logout,
},
} = this.props;
const theme = darkThemeVisible ? darkTheme : lightTheme; const theme = darkThemeVisible ? darkTheme : lightTheme;
const loginRoute = () => (loggedIn ? <Redirect to="/" /> : <Login />); const loginRoute = () => (loggedIn ? <Redirect to="/" /> : <Login />);
return ( return (
@ -81,6 +83,7 @@ class Layout extends React.Component<WithStyles<'content'>> {
loggedIn={loggedIn} loggedIn={loggedIn}
toggleTheme={() => (this.darkThemeVisible = !this.darkThemeVisible)} toggleTheme={() => (this.darkThemeVisible = !this.darkThemeVisible)}
showSettings={() => (this.showSettings = true)} showSettings={() => (this.showSettings = true)}
logout={logout}
/> />
<Navigation loggedIn={loggedIn} /> <Navigation loggedIn={loggedIn} />
@ -112,4 +115,4 @@ class Layout extends React.Component<WithStyles<'content'>> {
} }
} }
export default withStyles(styles, {withTheme: true})<{}>(Layout); export default withStyles(styles, {withTheme: true})<{}>(inject('currentUser')(Layout));

29
ui/src/actions/axios.ts Normal file
View File

@ -0,0 +1,29 @@
import axios from 'axios';
import {CurrentUser} from '../stores/CurrentUser';
import {SnackReporter} from '../stores/SnackManager';
export const initAxios = (currentUser: CurrentUser, snack: SnackReporter) => {
axios.interceptors.request.use((config) => {
config.headers['X-Gotify-Key'] = currentUser.token();
return config;
});
axios.interceptors.response.use(undefined, (error) => {
if (!error.response) {
snack('Gotify server is not reachable, try refreshing the page.');
return Promise.reject(error);
}
const status = error.response.status;
if (status === 401) {
currentUser.tryAuthenticate().then(() => snack('Could not complete request.'));
}
if (status === 400) {
snack(error.response.data.error + ': ' + error.response.data.errorDescription);
}
return Promise.reject(error);
});
};

View File

@ -1,27 +0,0 @@
import axios from 'axios';
import {currentUser} from '../stores/CurrentUser';
import SnackManager from '../stores/SnackManager';
axios.interceptors.request.use((config) => {
config.headers['X-Gotify-Key'] = currentUser.token();
return config;
});
axios.interceptors.response.use(undefined, (error) => {
if (!error.response) {
SnackManager.snack('Gotify server is not reachable, try refreshing the page.');
return Promise.reject(error);
}
const status = error.response.status;
if (status === 401) {
currentUser.tryAuthenticate().then(() => SnackManager.snack('Could not complete request.'));
}
if (status === 400) {
SnackManager.snack(error.response.data.error + ': ' + error.response.data.errorDescription);
}
return Promise.reject(error);
});

View File

@ -12,7 +12,6 @@ import Highlight from '@material-ui/icons/Highlight';
import SupervisorAccount from '@material-ui/icons/SupervisorAccount'; import SupervisorAccount from '@material-ui/icons/SupervisorAccount';
import React, {Component} from 'react'; import React, {Component} from 'react';
import {Link} from 'react-router-dom'; import {Link} from 'react-router-dom';
import {currentUser} from '../stores/CurrentUser';
import {observer} from 'mobx-react'; import {observer} from 'mobx-react';
const styles = (theme: Theme) => ({ const styles = (theme: Theme) => ({
@ -42,12 +41,13 @@ interface IProps {
version: string; version: string;
toggleTheme: VoidFunction; toggleTheme: VoidFunction;
showSettings: VoidFunction; showSettings: VoidFunction;
logout: VoidFunction;
} }
@observer @observer
class Header extends Component<IProps & Styles> { class Header extends Component<IProps & Styles> {
public render() { public render() {
const {classes, version, name, loggedIn, admin, toggleTheme} = this.props; const {classes, version, name, loggedIn, admin, toggleTheme, logout} = this.props;
return ( return (
<AppBar position="absolute" className={classes.appBar}> <AppBar position="absolute" className={classes.appBar}>
@ -69,7 +69,7 @@ class Header extends Component<IProps & Styles> {
</Typography> </Typography>
</a> </a>
</div> </div>
{loggedIn && this.renderButtons(name, admin)} {loggedIn && this.renderButtons(name, admin, logout)}
<IconButton onClick={toggleTheme} color="inherit"> <IconButton onClick={toggleTheme} color="inherit">
<Highlight /> <Highlight />
</IconButton> </IconButton>
@ -78,7 +78,7 @@ class Header extends Component<IProps & Styles> {
); );
} }
private renderButtons(name: string, admin: boolean) { private renderButtons(name: string, admin: boolean, logout: VoidFunction) {
const {classes, showSettings} = this.props; const {classes, showSettings} = this.props;
return ( return (
<div> <div>
@ -109,7 +109,7 @@ class Header extends Component<IProps & Styles> {
&nbsp; &nbsp;
{name} {name}
</Button> </Button>
<Button color="inherit" onClick={currentUser.logout} id="logout"> <Button color="inherit" onClick={logout} id="logout">
<ExitToApp /> <ExitToApp />
&nbsp;Logout &nbsp;Logout
</Button> </Button>

View File

@ -5,8 +5,8 @@ import ListItemText from '@material-ui/core/ListItemText';
import {StyleRules, Theme, WithStyles, withStyles} from '@material-ui/core/styles'; import {StyleRules, Theme, WithStyles, withStyles} from '@material-ui/core/styles';
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 {observer} from 'mobx-react'; import {observer} from 'mobx-react';
import {inject, Stores} from '../inject';
const styles = (theme: Theme): StyleRules<'drawerPaper' | 'toolbar' | 'link'> => ({ const styles = (theme: Theme): StyleRules<'drawerPaper' | 'toolbar' | 'link'> => ({
drawerPaper: { drawerPaper: {
@ -29,10 +29,10 @@ interface IProps {
} }
@observer @observer
class Navigation extends Component<IProps & Styles> { class Navigation extends Component<IProps & Styles & Stores<'appStore'>> {
public render() { public render() {
const {classes, loggedIn} = this.props; const {classes, loggedIn, appStore} = this.props;
const apps = AppStore.getItems(); const apps = appStore.getItems();
const userApps = const userApps =
apps.length === 0 apps.length === 0
@ -78,4 +78,4 @@ class Navigation extends Component<IProps & Styles> {
} }
} }
export default withStyles(styles, {withTheme: true})<IProps>(Navigation); export default withStyles(styles, {withTheme: true})<IProps>(inject('appStore')(Navigation));

View File

@ -6,22 +6,22 @@ import DialogTitle from '@material-ui/core/DialogTitle';
import TextField from '@material-ui/core/TextField'; import TextField from '@material-ui/core/TextField';
import Tooltip from '@material-ui/core/Tooltip'; import Tooltip from '@material-ui/core/Tooltip';
import React, {Component} from 'react'; import React, {Component} from 'react';
import {currentUser} from '../stores/CurrentUser';
import {observable} from 'mobx'; import {observable} from 'mobx';
import {observer} from 'mobx-react'; import {observer} from 'mobx-react';
import {inject, Stores} from '../inject';
interface IProps { interface IProps {
fClose: VoidFunction; fClose: VoidFunction;
} }
@observer @observer
export default class SettingsDialog extends Component<IProps> { class SettingsDialog extends Component<IProps & Stores<'currentUser'>> {
@observable @observable
private pass = ''; private pass = '';
public render() { public render() {
const {pass} = this; const {pass} = this;
const {fClose} = this.props; const {fClose, currentUser} = this.props;
const submitAndClose = () => { const submitAndClose = () => {
currentUser.changePassword(pass); currentUser.changePassword(pass);
fClose(); fClose();
@ -64,3 +64,5 @@ export default class SettingsDialog extends Component<IProps> {
); );
} }
} }
export default inject('currentUser')(SettingsDialog);

View File

@ -4,10 +4,10 @@ import Close from '@material-ui/icons/Close';
import React, {Component} from 'react'; import React, {Component} from 'react';
import {observable, reaction} from 'mobx'; import {observable, reaction} from 'mobx';
import {observer} from 'mobx-react'; import {observer} from 'mobx-react';
import SnackManager from '../stores/SnackManager'; import {inject, Stores} from '../inject';
@observer @observer
class SnackBarHandler extends Component { class SnackBarHandler extends Component<Stores<'snackManager'>> {
private static MAX_VISIBLE_SNACK_TIME_IN_MS = 6000; private static MAX_VISIBLE_SNACK_TIME_IN_MS = 6000;
private static MIN_VISIBLE_SNACK_TIME_IN_MS = 1000; private static MIN_VISIBLE_SNACK_TIME_IN_MS = 1000;
@ -19,12 +19,12 @@ class SnackBarHandler extends Component {
private dispose: () => void = () => {}; private dispose: () => void = () => {};
public componentDidMount = () => public componentDidMount = () =>
(this.dispose = reaction(() => SnackManager.counter, this.onNewSnack)); (this.dispose = reaction(() => this.props.snackManager.counter, this.onNewSnack));
public componentWillUnmount = () => this.dispose(); public componentWillUnmount = () => this.dispose();
public render() { public render() {
const {message: current, hasNext} = SnackManager; const {message: current, hasNext} = this.props.snackManager;
const duration = hasNext() const duration = hasNext()
? SnackBarHandler.MIN_VISIBLE_SNACK_TIME_IN_MS ? SnackBarHandler.MIN_VISIBLE_SNACK_TIME_IN_MS
: SnackBarHandler.MAX_VISIBLE_SNACK_TIME_IN_MS; : SnackBarHandler.MAX_VISIBLE_SNACK_TIME_IN_MS;
@ -70,14 +70,14 @@ class SnackBarHandler extends Component {
}; };
private openNextSnack = () => { private openNextSnack = () => {
if (SnackManager.hasNext()) { if (this.props.snackManager.hasNext()) {
this.open = true; this.open = true;
this.openWhen = Date.now(); this.openWhen = Date.now();
SnackManager.next(); this.props.snackManager.next();
} }
}; };
private closeCurrentSnack = () => (this.open = false); private closeCurrentSnack = () => (this.open = false);
} }
export default SnackBarHandler; export default inject('snackManager')(SnackBarHandler);

View File

@ -2,16 +2,20 @@ import * as React from 'react';
import * as ReactDOM from 'react-dom'; import * as ReactDOM from 'react-dom';
import 'typeface-roboto'; import 'typeface-roboto';
import 'typeface-roboto-mono'; import 'typeface-roboto-mono';
import './actions/defaultAxios'; import {initAxios} from './actions/axios';
import * as config from './config'; import * as config from './config';
import Layout from './Layout'; import Layout from './Layout';
import registerServiceWorker from './registerServiceWorker'; import registerServiceWorker from './registerServiceWorker';
import * as Notifications from './stores/Notifications'; import * as Notifications from './stores/Notifications';
import {currentUser} from './stores/CurrentUser'; import {CurrentUser} from './stores/CurrentUser';
import AppStore from './stores/AppStore'; import {AppStore} from './stores/AppStore';
import {reaction} from 'mobx'; import {reaction} from 'mobx';
import {WebSocketStore} from './stores/WebSocketStore'; import {WebSocketStore} from './stores/WebSocketStore';
import SnackManager from './stores/SnackManager'; import {SnackManager} from './stores/SnackManager';
import {InjectProvider, StoreMapping} from './inject';
import {UserStore} from './stores/UserStore';
import {MessagesStore} from './stores/MessagesStore';
import {ClientStore} from './stores/ClientStore';
const defaultDevConfig = { const defaultDevConfig = {
url: 'http://localhost:80/', url: 'http://localhost:80/',
@ -33,6 +37,26 @@ declare global {
} }
} }
const initStores = (): StoreMapping => {
const snackManager = new SnackManager();
const appStore = new AppStore(snackManager.snack);
const userStore = new UserStore(snackManager.snack);
const messagesStore = new MessagesStore(appStore, snackManager.snack);
const currentUser = new CurrentUser(snackManager.snack);
const clientStore = new ClientStore(snackManager.snack);
const wsStore = new WebSocketStore(snackManager.snack, currentUser, messagesStore);
return {
appStore,
snackManager,
userStore,
messagesStore,
currentUser,
clientStore,
wsStore,
};
};
(function clientJS() { (function clientJS() {
Notifications.requestPermission(); Notifications.requestPermission();
if (process.env.NODE_ENV === 'production') { if (process.env.NODE_ENV === 'production') {
@ -40,20 +64,28 @@ declare global {
} else { } else {
config.set(window.config || defaultDevConfig); config.set(window.config || defaultDevConfig);
} }
const ws = new WebSocketStore(SnackManager.snack); const stores = initStores();
initAxios(stores.currentUser, stores.snackManager.snack);
reaction( reaction(
() => currentUser.loggedIn, () => stores.currentUser.loggedIn,
(loggedIn) => { (loggedIn) => {
if (loggedIn) { if (loggedIn) {
ws.listen(); stores.wsStore.listen();
} else { } else {
ws.close(); stores.wsStore.close();
} }
AppStore.refresh(); stores.appStore.refresh();
} }
); );
currentUser.tryAuthenticate(); stores.currentUser.tryAuthenticate();
ReactDOM.render(<Layout />, document.getElementById('root'));
ReactDOM.render(
<InjectProvider stores={stores}>
<Layout />
</InjectProvider>,
document.getElementById('root')
);
registerServiceWorker(); registerServiceWorker();
})(); })();

View File

@ -14,12 +14,12 @@ 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 AddApplicationDialog from './dialog/AddApplicationDialog'; import AddApplicationDialog from './dialog/AddApplicationDialog';
import AppStore from '../stores/AppStore';
import {observer} from 'mobx-react'; import {observer} from 'mobx-react';
import {observable} from 'mobx'; import {observable} from 'mobx';
import {inject, Stores} from '../inject';
@observer @observer
class Applications extends Component { class Applications extends Component<Stores<'appStore'>> {
@observable @observable
private deleteId: number | false = false; private deleteId: number | false = false;
@observable @observable
@ -28,11 +28,15 @@ class Applications extends Component {
private uploadId = -1; private uploadId = -1;
private upload: HTMLInputElement | null = null; private upload: HTMLInputElement | null = null;
public componentDidMount = AppStore.refresh; public componentDidMount = () => this.props.appStore.refresh();
public render() { public render() {
const {createDialog, deleteId} = this; const {
const apps = AppStore.getItems(); createDialog,
deleteId,
props: {appStore},
} = this;
const apps = appStore.getItems();
return ( return (
<DefaultPage <DefaultPage
title="Applications" title="Applications"
@ -79,15 +83,15 @@ class Applications extends Component {
{createDialog && ( {createDialog && (
<AddApplicationDialog <AddApplicationDialog
fClose={() => (this.createDialog = false)} fClose={() => (this.createDialog = false)}
fOnSubmit={AppStore.create} fOnSubmit={appStore.create}
/> />
)} )}
{deleteId !== false && ( {deleteId !== false && (
<ConfirmDialog <ConfirmDialog
title="Confirm Delete" title="Confirm Delete"
text={'Delete ' + AppStore.getByID(deleteId).name + '?'} text={'Delete ' + appStore.getByID(deleteId).name + '?'}
fClose={() => (this.deleteId = false)} fClose={() => (this.deleteId = false)}
fOnSubmit={() => AppStore.remove(deleteId)} fOnSubmit={() => appStore.remove(deleteId)}
/> />
)} )}
</DefaultPage> </DefaultPage>
@ -107,7 +111,7 @@ class Applications extends Component {
return; return;
} }
if (['image/png', 'image/jpeg', 'image/gif'].indexOf(file.type) !== -1) { if (['image/png', 'image/jpeg', 'image/gif'].indexOf(file.type) !== -1) {
AppStore.uploadImage(this.uploadId, file); this.props.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.');
} }
@ -146,4 +150,4 @@ const Row: SFC<IRowProps> = observer(({name, value, description, fDelete, fUploa
</TableRow> </TableRow>
)); ));
export default Applications; export default inject('appStore')(Applications);

View File

@ -12,22 +12,26 @@ 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 AddClientDialog from './dialog/AddClientDialog'; import AddClientDialog from './dialog/AddClientDialog';
import ClientStore from '../stores/ClientStore';
import {observer} from 'mobx-react'; import {observer} from 'mobx-react';
import {observable} from 'mobx'; import {observable} from 'mobx';
import {inject, Stores} from '../inject';
@observer @observer
class Clients extends Component { class Clients extends Component<Stores<'clientStore'>> {
@observable @observable
private showDialog = false; private showDialog = false;
@observable @observable
private deleteId: false | number = false; private deleteId: false | number = false;
public componentDidMount = ClientStore.refresh; public componentDidMount = () => this.props.clientStore.refresh();
public render() { public render() {
const {deleteId, showDialog} = this; const {
const clients = ClientStore.getItems(); deleteId,
showDialog,
props: {clientStore},
} = this;
const clients = clientStore.getItems();
return ( return (
<DefaultPage <DefaultPage
@ -63,15 +67,15 @@ class Clients extends Component {
{showDialog && ( {showDialog && (
<AddClientDialog <AddClientDialog
fClose={() => (this.showDialog = false)} fClose={() => (this.showDialog = false)}
fOnSubmit={ClientStore.create} fOnSubmit={clientStore.create}
/> />
)} )}
{deleteId !== false && ( {deleteId !== false && (
<ConfirmDialog <ConfirmDialog
title="Confirm Delete" title="Confirm Delete"
text={'Delete ' + ClientStore.getByID(deleteId).name + '?'} text={'Delete ' + clientStore.getByID(deleteId).name + '?'}
fClose={() => (this.deleteId = false)} fClose={() => (this.deleteId = false)}
fOnSubmit={() => ClientStore.remove(deleteId)} fOnSubmit={() => clientStore.remove(deleteId)}
/> />
)} )}
</DefaultPage> </DefaultPage>
@ -102,4 +106,4 @@ const Row: SFC<IRowProps> = ({name, value, fDelete}) => (
</TableRow> </TableRow>
); );
export default Clients; export default inject('clientStore')(Clients);

View File

@ -4,12 +4,12 @@ import TextField from '@material-ui/core/TextField';
import React, {Component, FormEvent} from 'react'; import React, {Component, FormEvent} from 'react';
import Container from '../component/Container'; import Container from '../component/Container';
import DefaultPage from '../component/DefaultPage'; import DefaultPage from '../component/DefaultPage';
import {currentUser} from '../stores/CurrentUser';
import {observable} from 'mobx'; import {observable} from 'mobx';
import {observer} from 'mobx-react'; import {observer} from 'mobx-react';
import {inject, Stores} from '../inject';
@observer @observer
class Login extends Component { class Login extends Component<Stores<'currentUser'>> {
@observable @observable
private username = ''; private username = '';
@observable @observable
@ -57,10 +57,10 @@ class Login extends Component {
private login = (e: React.MouseEvent<HTMLInputElement>) => { private login = (e: React.MouseEvent<HTMLInputElement>) => {
e.preventDefault(); e.preventDefault();
currentUser.login(this.username, this.password); this.props.currentUser.login(this.username, this.password);
}; };
private preventDefault = (e: FormEvent<HTMLFormElement>) => e.preventDefault(); private preventDefault = (e: FormEvent<HTMLFormElement>) => e.preventDefault();
} }
export default Login; export default inject('currentUser')(Login);

View File

@ -5,11 +5,10 @@ import React, {Component} from 'react';
import {RouteComponentProps} from 'react-router'; import {RouteComponentProps} from 'react-router';
import DefaultPage from '../component/DefaultPage'; import DefaultPage from '../component/DefaultPage';
import Message from '../component/Message'; import Message from '../component/Message';
import AppStore from '../stores/AppStore';
import MessagesStore from '../stores/MessagesStore';
import {observer} from 'mobx-react'; import {observer} from 'mobx-react';
// @ts-ignore // @ts-ignore
import InfiniteAnyHeight from 'react-infinite-any-height'; import InfiniteAnyHeight from 'react-infinite-any-height';
import {inject, Stores} from '../inject';
interface IProps extends RouteComponentProps<{id: string}> {} interface IProps extends RouteComponentProps<{id: string}> {}
@ -18,7 +17,7 @@ interface IState {
} }
@observer @observer
class Messages extends Component<IProps, IState> { class Messages extends Component<IProps & Stores<'messagesStore' | 'appStore'>, IState> {
private static appId(props: IProps) { private static appId(props: IProps) {
if (props === undefined) { if (props === undefined) {
return -1; return -1;
@ -31,7 +30,7 @@ class Messages extends Component<IProps, IState> {
private isLoadingMore = false; private isLoadingMore = false;
public componentWillReceiveProps(nextProps: IProps) { public componentWillReceiveProps(nextProps: IProps & Stores<'messagesStore' | 'appStore'>) {
this.updateAllWithProps(nextProps); this.updateAllWithProps(nextProps);
} }
@ -49,9 +48,10 @@ class Messages extends Component<IProps, IState> {
public render() { public render() {
const {appId} = this.state; const {appId} = this.state;
const messages = MessagesStore.get(appId); const {messagesStore, appStore} = this.props;
const hasMore = MessagesStore.canLoadMore(appId); const messages = messagesStore.get(appId);
const name = AppStore.getName(appId); const hasMore = messagesStore.canLoadMore(appId);
const name = appStore.getName(appId);
const hasMessages = messages.length !== 0; const hasMessages = messages.length !== 0;
return ( return (
@ -59,7 +59,7 @@ class Messages extends Component<IProps, IState> {
title={name} title={name}
buttonTitle="Delete All" buttonTitle="Delete All"
buttonId="delete-all" buttonId="delete-all"
fButton={() => MessagesStore.removeByApp(appId)} fButton={() => messagesStore.removeByApp(appId)}
buttonDisabled={!hasMessages}> buttonDisabled={!hasMessages}>
{hasMessages ? ( {hasMessages ? (
<div style={{width: '100%'}} id="messages"> <div style={{width: '100%'}} id="messages">
@ -84,18 +84,19 @@ class Messages extends Component<IProps, IState> {
); );
} }
private updateAllWithProps = (props: IProps) => { private updateAllWithProps = (props: IProps & Stores<'messagesStore'>) => {
const appId = Messages.appId(props); const appId = Messages.appId(props);
console.log('props', props);
this.setState({appId}); this.setState({appId});
if (!MessagesStore.exists(appId)) { if (!props.messagesStore.exists(appId)) {
MessagesStore.loadMore(appId); props.messagesStore.loadMore(appId);
} }
}; };
private updateAll = () => this.updateAllWithProps(this.props); private updateAll = () => this.updateAllWithProps(this.props);
private deleteMessage = (message: IMessage) => () => MessagesStore.removeSingle(message); private deleteMessage = (message: IMessage) => () =>
this.props.messagesStore.removeSingle(message);
private renderMessage = (message: IMessage) => { private renderMessage = (message: IMessage) => {
this.checkIfLoadMore(); this.checkIfLoadMore();
@ -113,9 +114,9 @@ class Messages extends Component<IProps, IState> {
private checkIfLoadMore() { private checkIfLoadMore() {
const {appId} = this.state; const {appId} = this.state;
if (!this.isLoadingMore && MessagesStore.canLoadMore(appId)) { if (!this.isLoadingMore && this.props.messagesStore.canLoadMore(appId)) {
this.isLoadingMore = true; this.isLoadingMore = true;
MessagesStore.loadMore(appId).then(() => (this.isLoadingMore = false)); this.props.messagesStore.loadMore(appId).then(() => (this.isLoadingMore = false));
} }
} }
@ -128,4 +129,4 @@ class Messages extends Component<IProps, IState> {
); );
} }
export default Messages; export default inject('messagesStore', 'appStore')(Messages);

View File

@ -13,9 +13,9 @@ import React, {Component, SFC} from 'react';
import ConfirmDialog from '../component/ConfirmDialog'; import ConfirmDialog from '../component/ConfirmDialog';
import DefaultPage from '../component/DefaultPage'; import DefaultPage from '../component/DefaultPage';
import AddEditDialog from './dialog/AddEditUserDialog'; import AddEditDialog from './dialog/AddEditUserDialog';
import UserStore from '../stores/UserStore';
import {observer} from 'mobx-react'; import {observer} from 'mobx-react';
import {observable} from 'mobx'; import {observable} from 'mobx';
import {inject, Stores} from '../inject';
const styles = () => ({ const styles = () => ({
wrapper: { wrapper: {
@ -47,7 +47,7 @@ const UserRow: SFC<IRowProps> = ({name, admin, fDelete, fEdit}) => (
); );
@observer @observer
class Users extends Component<WithStyles<'wrapper'>> { class Users extends Component<WithStyles<'wrapper'> & Stores<'userStore'>> {
@observable @observable
private createDialog = false; private createDialog = false;
@observable @observable
@ -55,11 +55,16 @@ class Users extends Component<WithStyles<'wrapper'>> {
@observable @observable
private editId: number | false = false; private editId: number | false = false;
public componentDidMount = UserStore.refresh; public componentDidMount = () => this.props.userStore.refresh();
public render() { public render() {
const users = UserStore.getItems(); const {
const {deleteId, editId, createDialog} = this; deleteId,
editId,
createDialog,
props: {userStore},
} = this;
const users = userStore.getItems();
return ( return (
<DefaultPage <DefaultPage
title="Users" title="Users"
@ -95,24 +100,24 @@ class Users extends Component<WithStyles<'wrapper'>> {
{createDialog && ( {createDialog && (
<AddEditDialog <AddEditDialog
fClose={() => (this.createDialog = false)} fClose={() => (this.createDialog = false)}
fOnSubmit={UserStore.create} fOnSubmit={userStore.create}
/> />
)} )}
{editId !== false && ( {editId !== false && (
<AddEditDialog <AddEditDialog
fClose={() => (this.editId = false)} fClose={() => (this.editId = false)}
fOnSubmit={UserStore.update.bind(this, editId)} fOnSubmit={userStore.update.bind(this, editId)}
name={UserStore.getByID(editId).name} name={userStore.getByID(editId).name}
admin={UserStore.getByID(editId).admin} admin={userStore.getByID(editId).admin}
isEdit={true} isEdit={true}
/> />
)} )}
{deleteId !== false && ( {deleteId !== false && (
<ConfirmDialog <ConfirmDialog
title="Confirm Delete" title="Confirm Delete"
text={'Delete ' + UserStore.getByID(deleteId).name + '?'} text={'Delete ' + userStore.getByID(deleteId).name + '?'}
fClose={() => (this.deleteId = false)} fClose={() => (this.deleteId = false)}
fOnSubmit={() => UserStore.remove(deleteId)} fOnSubmit={() => userStore.remove(deleteId)}
/> />
)} )}
</DefaultPage> </DefaultPage>
@ -120,4 +125,4 @@ class Users extends Component<WithStyles<'wrapper'>> {
} }
} }
export default withStyles(styles)(Users); export default withStyles(styles)(inject('userStore')(Users));