Add Messages Component

This commit is contained in:
Jannis Mattheis 2018-03-15 20:32:47 +01:00 committed by Jannis Mattheis
parent 3b08e6ade3
commit 88249a8e82
4 changed files with 225 additions and 0 deletions

View File

@ -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)});
};
}

View File

@ -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);

71
ui/src/pages/Messages.js Normal file
View File

@ -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;

View File

@ -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;