Add Messages Component
This commit is contained in:
parent
3b08e6ade3
commit
88249a8e82
|
|
@ -0,0 +1,48 @@
|
||||||
|
import dispatcher from '../stores/dispatcher';
|
||||||
|
import config from 'react-global-configuration';
|
||||||
|
import axios from 'axios';
|
||||||
|
import {getToken} from './defaultAxios';
|
||||||
|
|
||||||
|
/** Fetches all messages from the current user. */
|
||||||
|
export function fetchMessages() {
|
||||||
|
axios.get(config.get('url') + 'message').then((resp) => {
|
||||||
|
dispatcher.dispatch({type: 'UPDATE_MESSAGES', payload: resp.data});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Deletes all messages from the current user. */
|
||||||
|
export function deleteMessages() {
|
||||||
|
axios.delete(config.get('url') + 'message').then(fetchMessages);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes all messages from the current user and an application.
|
||||||
|
* @param {int} id the application id
|
||||||
|
*/
|
||||||
|
export function deleteMessagesByApp(id) {
|
||||||
|
axios.delete(config.get('url') + 'application/' + id + '/message').then(fetchMessages);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a message by id.
|
||||||
|
* @param {int} id the message id
|
||||||
|
*/
|
||||||
|
export function deleteMessage(id) {
|
||||||
|
axios.delete(config.get('url') + 'message/' + id).then(fetchMessages);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts listening to the stream for new messages.
|
||||||
|
*/
|
||||||
|
export function listenToWebSocket() {
|
||||||
|
const ws = new WebSocket('ws://localhost:80/stream?token=' + getToken());
|
||||||
|
|
||||||
|
ws.onerror = (e) => {
|
||||||
|
console.log('WebSocket connection errored; trying again in 60 seconds', e);
|
||||||
|
setTimeout(listenToWebSocket, 60000);
|
||||||
|
};
|
||||||
|
|
||||||
|
ws.onmessage = (data) => {
|
||||||
|
dispatcher.dispatch({type: 'ONE_MESSAGE', payload: JSON.parse(data.data)});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
import React, {Component} from 'react';
|
||||||
|
import {withStyles} from 'material-ui/styles';
|
||||||
|
import Typography from 'material-ui/Typography';
|
||||||
|
import IconButton from 'material-ui/IconButton';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import Container from './Container';
|
||||||
|
import TimeAgo from 'react-timeago';
|
||||||
|
import Delete from 'material-ui-icons/Delete';
|
||||||
|
|
||||||
|
const styles = () => ({
|
||||||
|
header: {
|
||||||
|
display: 'flex',
|
||||||
|
},
|
||||||
|
headerTitle: {
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
trash: {
|
||||||
|
marginTop: -15,
|
||||||
|
marginRight: -15,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
class Message extends Component {
|
||||||
|
static propTypes = {
|
||||||
|
classes: PropTypes.object.isRequired,
|
||||||
|
title: PropTypes.string.isRequired,
|
||||||
|
date: PropTypes.string.isRequired,
|
||||||
|
content: PropTypes.string.isRequired,
|
||||||
|
fDelete: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {fDelete, classes, title, date, content} = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<div className={classes.header}>
|
||||||
|
<Typography className={classes.headerTitle} variant="headline">
|
||||||
|
{title}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body1">
|
||||||
|
<TimeAgo date={date}/>
|
||||||
|
</Typography>
|
||||||
|
<IconButton onClick={fDelete} className={classes.trash}><Delete/></IconButton>
|
||||||
|
</div>
|
||||||
|
<Typography component="p">
|
||||||
|
{content}
|
||||||
|
</Typography>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withStyles(styles)(Message);
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
import React, {Component} from 'react';
|
||||||
|
import Grid from 'material-ui/Grid';
|
||||||
|
import Typography from 'material-ui/Typography';
|
||||||
|
import Message from '../component/Message';
|
||||||
|
import MessageStore from '../stores/MessageStore';
|
||||||
|
import AppStore from '../stores/AppStore';
|
||||||
|
import * as MessageAction from '../actions/MessageAction';
|
||||||
|
import DefaultPage from '../component/DefaultPage';
|
||||||
|
|
||||||
|
class Messages extends Component {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.state = {appId: -1, messages: [], name: 'unknown'};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
this.updateAllWithProps(nextProps);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillMount() {
|
||||||
|
MessageStore.on('change', this.updateAll);
|
||||||
|
AppStore.on('change', this.updateAll);
|
||||||
|
this.updateAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
MessageStore.removeListener('change', this.updateAll);
|
||||||
|
AppStore.removeListener('change', this.updateAll);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateAllWithProps = (props) => {
|
||||||
|
const appId = Messages.appId(props);
|
||||||
|
const messages = MessageStore.getForAppId(appId);
|
||||||
|
const name = AppStore.getName(appId);
|
||||||
|
this.setState({appId: appId, messages, name});
|
||||||
|
};
|
||||||
|
|
||||||
|
updateAll = () => this.updateAllWithProps(this.props);
|
||||||
|
|
||||||
|
static appId(props) {
|
||||||
|
if (props === undefined) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
const {match} = props;
|
||||||
|
return match.params.id !== undefined ? parseInt(match.params.id) : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {name, messages, appId} = this.state;
|
||||||
|
const fDelete = appId === -1 ? MessageAction.deleteMessages : MessageAction.deleteMessagesByApp.bind(this, appId);
|
||||||
|
|
||||||
|
const noMessages = (
|
||||||
|
<Grid item xs={12}><Typography variant="caption" gutterBottom align="center">No messages</Typography></Grid>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DefaultPage title={name} buttonTitle="Delete All" fButton={fDelete} buttonDisabled={messages.length === 0}>
|
||||||
|
{messages.length === 0 ? noMessages : messages.map((message) => {
|
||||||
|
return (
|
||||||
|
<Grid item xs={12} key={message.id}>
|
||||||
|
<Message fDelete={() => MessageAction.deleteMessage(message.id)} title={message.title}
|
||||||
|
date={message.date} content={message.message}/>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</DefaultPage>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Messages;
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
import {EventEmitter} from 'events';
|
||||||
|
import dispatcher from './dispatcher';
|
||||||
|
|
||||||
|
class MessageStore extends EventEmitter {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.messages = [];
|
||||||
|
this.messagesForApp = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
get() {
|
||||||
|
return this.messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
getForAppId(id) {
|
||||||
|
if (id === -1) {
|
||||||
|
return this.messages;
|
||||||
|
}
|
||||||
|
if (this.messagesForApp[id]) {
|
||||||
|
return this.messagesForApp[id];
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
handle(data) {
|
||||||
|
if (data.type === 'UPDATE_MESSAGES') {
|
||||||
|
this.messages = data.payload;
|
||||||
|
this.messagesForApp = [];
|
||||||
|
this.messages.forEach(function(message) {
|
||||||
|
this.createIfNotExist(message.appid);
|
||||||
|
this.messagesForApp[message.appid].push(message);
|
||||||
|
}.bind(this));
|
||||||
|
this.emit('change');
|
||||||
|
} else if (data.type === 'ONE_MESSAGE') {
|
||||||
|
const {payload} = data;
|
||||||
|
this.createIfNotExist(payload.appid);
|
||||||
|
this.messagesForApp[payload.appid].unshift(payload);
|
||||||
|
this.messages.unshift(payload);
|
||||||
|
this.emit('change');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createIfNotExist(id) {
|
||||||
|
if (!(id in this.messagesForApp)) {
|
||||||
|
this.messagesForApp[id] = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const store = new MessageStore();
|
||||||
|
dispatcher.register(store.handle.bind(store));
|
||||||
|
export default store;
|
||||||
Loading…
Reference in New Issue