import React, { Component } from 'react';
import './Timeline.scss';
import axios from 'axios';
import Event from './Event';
import EventNode from './Components/Timeline/EventNode';
import DateNode from './Components/Timeline/DateNode';
import EndNode from './Components/Timeline/EndNode';
import EventMessages from './Components/Timeline/EventMessages';
import moment from 'moment';
import WebSocketStatus from './Components/WebSocketStatus';
class Timeline extends Component {
constructor(props) {
super(props);
var host = window.location.protocol + '//' + window.location.host;
if (process.env.NODE_ENV === 'development') {
host = 'https://10.0.0.1:8002';
}
this.state = {
events: [],
loaded: false,
error: false,
wsIsOpen: false,
wsIsAuthenticated: false,
wsIsRecovering: false,
wsURI: null,
wsAuthKey: null,
host: host
};
this.wsSocket = null;
var self = this;
setInterval(function() {
self.recover();
}, 5000);
}
componentDidMount() {
this.fetchEventList();
this.initUserNotification();
}
initUserNotification() {
if(!('Notification' in window))
return;
// Not yet approved?
if (Notification.permission === 'default') {
// Request permission
return Notification.requestPermission(function() {
//console.log("Got permission!");
});
}
}
showUserNotification(event) {
if(!('Notification' in window))
return;
if(Notification.permission !== "granted")
return;
// define new notification
var n = new Notification(
event.getSubtitle(),
{
'body': event.getTitle(),
'tag' : "event-" + event.id
}
);
// notify when shown successfull
n.onshow = function () {
console.log("onshow");
};
// remove the notification from Notification Center when clicked.
n.onclick = function () {
this.close();
console.log("onclick");
};
// callback function when the notification is closed.
n.onclose = function () {
console.log("onclose");
};
// notification cannot be presented to the user, this event is fired if the permission level is set to denied or default.
n.onerror = function () {
console.log("onerror");
};
}
recover() {
var self = this;
if(!self.state.wsIsOpen) {
self.initWebsocketConnection(self.state.wsURI);
return;
}
if(!self.state.wsIsAuthenticated) {
self.authenticateWebsocketConnection();
return;
}
}
fetchEventList() {
var self = this;
axios.get(this.state.host + '/api/status')
.then(function(res) {
const events = res.data.events.map(obj => new Event(obj));
const wsURI = res.data['wss-uri'] === undefined ? null : res.data['wss-uri'];
self.setState({
events: events,
loaded: true,
error: false,
wsURI: wsURI,
wsAuthKey: res.data['auth-key']
});
// Once we get to know the web socket port, we can make the web socket connection
if(wsURI && !self.state.wsIsRecovering) {
self.initWebsocketConnection(res.data['wss-uri']);
}
})
.catch(err => {
console.warn(err);
this.setState({
loaded: true,
error: true
});
});
}
addOrUpdateEvent(event) {
this.setState((prevState, props) => {
var newEvents = [];
var inserted = false;
for(var key in prevState.events) {
if(prevState.hasOwnProperty(key))
continue;
var curEvent = prevState.events[key];
if(curEvent.id === event.id) {
newEvents.push(event);
inserted = true;
} else {
newEvents.push(curEvent);
}
}
if(!inserted) {
newEvents.push(event);
}
return {
events: newEvents
}
});
}
getEventWithId(id) {
var self = this;
for(var key in self.state.events) {
if(self.state.hasOwnProperty(key))
continue;
var event = self.state.events[key];
if(event.id === id)
return event;
}
return undefined;
}
authenticateWebsocketConnection(authKey) {
var self = this;
// Authenticate
self.wsSocket.send(JSON.stringify({
"type": "authenticate",
"auth-key": self.state.wsAuthKey
}));
}
handleJSONMessage(data) {
var event;
var self = this;
// Auth key was invalid, maybe the server restarted
if(data.type === "bad-auth-key") {
self.setState({
wsIsAuthenticated: false,
wsIsRecovering: true
});
// Get a fresh auth key
self.fetchEventList();
} else if(data.type === "authenticated") {
if(self.state.wsIsRecovering) {
self.fetchEventList();
}
self.setState({
wsIsAuthenticated: true,
wsIsRecovering: false
});
} else if(data.type === "new-event") {
event = new Event(data.event);
this.addOrUpdateEvent(event);
} else if(data.type === "event-updated") {
event = new Event(data.event);
this.addOrUpdateEvent(event);
} else if(data.type === "event-success") {
event = this.getEventWithId(data.id);
if(event && event.type === "WebhookAction") {
this.showUserNotification(event);
}
} else {
console.log("Unknown event: " + data.type);
}
}
initWebsocketConnection(uri) {
var self = this;
if(!uri)
return;
self.wsSocket = new WebSocket(uri);
self.wsSocket.binaryType = "arraybuffer";
self.wsSocket.onopen = function() {
self.setState({
wsIsOpen: true,
});
self.authenticateWebsocketConnection();
};
self.wsSocket.onmessage = (e) => {
if (typeof e.data === "string") {
try {
var data = JSON.parse(e.data);
self.handleJSONMessage(data);
} catch(e) {
console.error(e);
}
} else {
var arr = new Uint8Array(e.data);
var hex = '';
for (var i = 0; i < arr.length; i++) {
hex += ('00' + arr[i].toString(16)).substr(-2);
}
console.log("Binary message received: " + hex);
}
};
self.wsSocket.onclose = function() {
self.wsSocket.close();
self.wsSocket = null;
self.setState({
wsIsOpen: false,
wsIsRecovering: true
});
if(self.wsReconnectTimeout !== undefined) {
clearTimeout(self.wsReconnectTimeout);
}
// Try to reconnect again after 2 seconds
self.wsReconnectTimeout = setTimeout(function() {
self.initWebsocketConnection(uri);
self.wsReconnectTimeout = undefined;
}, 2000);
};
}
/*
function sendText() {
if (isopen) {
socket.send("Hello, world!");
console.log("Text message sent.");
} else {
console.log("Connection not opened.")
}
};
function sendBinary() {
if (isopen) {
var buf = new ArrayBuffer(32);
var arr = new Uint8Array(buf);
for (i = 0; i < arr.length; ++i) arr[i] = i;
socket.send(buf);
console.log("Binary message sent.");
} else {
console.log("Connection not opened.")
}
};
*/
getDate(timestamp) {
return moment.unix(timestamp).format("YYYY-MM-DD");
}
getTimelineObjects() {
var rows = [];
var last_date = '';
var events = this.state.events;
rows.push(