2015-12-22 07:34:24 -08:00
|
|
|
'use strict';
|
|
|
|
|
2016-09-25 07:14:01 -07:00
|
|
|
window.eval = global.eval = () => {
|
|
|
|
throw new Error('Sorry, Mattermost does not support window.eval() for security reasons.');
|
|
|
|
};
|
2016-07-22 14:03:03 -07:00
|
|
|
|
2016-01-30 07:50:43 -08:00
|
|
|
const React = require('react');
|
|
|
|
const ReactDOM = require('react-dom');
|
|
|
|
const ReactBootstrap = require('react-bootstrap');
|
|
|
|
|
2015-12-22 07:34:24 -08:00
|
|
|
const Grid = ReactBootstrap.Grid;
|
|
|
|
const Row = ReactBootstrap.Row;
|
|
|
|
const Col = ReactBootstrap.Col;
|
2015-12-22 23:21:33 -08:00
|
|
|
const Nav = ReactBootstrap.Nav;
|
|
|
|
const NavItem = ReactBootstrap.NavItem;
|
2015-12-22 07:34:24 -08:00
|
|
|
|
2016-04-22 08:50:57 -07:00
|
|
|
const LoginModal = require('./components/loginModal.jsx');
|
|
|
|
|
2016-09-25 07:14:01 -07:00
|
|
|
const {remote, ipcRenderer, shell} = require('electron');
|
|
|
|
const electronContextMenu = require('electron-context-menu');
|
2015-12-22 07:34:24 -08:00
|
|
|
|
2015-12-22 22:53:34 -08:00
|
|
|
const osLocale = require('os-locale');
|
|
|
|
const fs = require('fs');
|
|
|
|
const url = require('url');
|
|
|
|
|
2015-12-22 07:34:24 -08:00
|
|
|
const settings = require('../common/settings');
|
2016-09-25 07:14:01 -07:00
|
|
|
const badge = require('./js/badge');
|
2015-12-22 07:34:24 -08:00
|
|
|
|
2016-01-12 04:28:08 -08:00
|
|
|
remote.getCurrentWindow().removeAllListeners('focus');
|
|
|
|
|
2016-09-25 07:14:01 -07:00
|
|
|
var config;
|
|
|
|
try {
|
|
|
|
var configFile = remote.getGlobal('config-file');
|
|
|
|
config = settings.readFileSync(configFile);
|
|
|
|
} catch (e) {
|
|
|
|
window.location = 'settings.html';
|
|
|
|
}
|
|
|
|
if (config.teams.length === 0) {
|
|
|
|
window.location = 'settings.html';
|
|
|
|
}
|
|
|
|
|
2015-12-22 07:34:24 -08:00
|
|
|
var MainPage = React.createClass({
|
2016-09-25 07:14:01 -07:00
|
|
|
getInitialState() {
|
2015-12-22 07:34:24 -08:00
|
|
|
return {
|
2015-12-22 23:21:33 -08:00
|
|
|
key: 0,
|
2016-01-18 17:53:17 -08:00
|
|
|
unreadCounts: new Array(this.props.teams.length),
|
2016-01-23 04:08:05 -08:00
|
|
|
mentionCounts: new Array(this.props.teams.length),
|
2016-01-23 21:31:56 -08:00
|
|
|
unreadAtActive: new Array(this.props.teams.length),
|
2016-04-22 08:50:57 -07:00
|
|
|
mentionAtActiveCounts: new Array(this.props.teams.length),
|
|
|
|
loginQueue: []
|
2015-12-22 07:34:24 -08:00
|
|
|
};
|
|
|
|
},
|
2016-09-25 07:14:01 -07:00
|
|
|
componentDidMount() {
|
|
|
|
var self = this;
|
|
|
|
ipcRenderer.on('login-request', (event, request, authInfo) => {
|
|
|
|
self.setState({
|
2016-04-22 08:50:57 -07:00
|
|
|
loginRequired: true
|
|
|
|
});
|
2016-09-25 07:14:01 -07:00
|
|
|
const loginQueue = self.state.loginQueue;
|
2016-04-22 08:50:57 -07:00
|
|
|
loginQueue.push({
|
2016-09-25 07:14:01 -07:00
|
|
|
request,
|
|
|
|
authInfo
|
2016-04-22 08:50:57 -07:00
|
|
|
});
|
2016-09-25 07:14:01 -07:00
|
|
|
self.setState({
|
|
|
|
loginQueue
|
2016-04-22 08:50:57 -07:00
|
|
|
});
|
|
|
|
});
|
2016-09-25 07:14:01 -07:00
|
|
|
|
2016-05-18 08:25:21 -07:00
|
|
|
// can't switch tabs sequencially for some reason...
|
2016-05-18 07:02:03 -07:00
|
|
|
ipcRenderer.on('switch-tab', (event, key) => {
|
|
|
|
this.handleSelect(key);
|
|
|
|
});
|
2016-09-25 07:14:01 -07:00
|
|
|
ipcRenderer.on('select-next-tab', () => {
|
2016-05-18 08:25:21 -07:00
|
|
|
this.handleSelect(this.state.key + 1);
|
|
|
|
});
|
2016-09-25 07:14:01 -07:00
|
|
|
ipcRenderer.on('select-previous-tab', () => {
|
2016-05-18 08:25:21 -07:00
|
|
|
this.handleSelect(this.state.key - 1);
|
|
|
|
});
|
2016-04-22 08:50:57 -07:00
|
|
|
|
2016-07-28 06:44:42 -07:00
|
|
|
// reload the activated tab
|
2016-09-25 07:14:01 -07:00
|
|
|
ipcRenderer.on('reload-tab', () => {
|
2016-07-28 06:44:42 -07:00
|
|
|
this.refs[`mattermostView${this.state.key}`].reload();
|
|
|
|
});
|
2016-09-25 07:14:01 -07:00
|
|
|
ipcRenderer.on('clear-cache-and-reload-tab', () => {
|
2016-07-28 06:44:42 -07:00
|
|
|
this.refs[`mattermostView${this.state.key}`].clearCacheAndReload();
|
|
|
|
});
|
|
|
|
|
2016-08-13 08:08:18 -07:00
|
|
|
// activate search box in current tab
|
2016-09-25 07:14:01 -07:00
|
|
|
ipcRenderer.on('activate-search-box', () => {
|
|
|
|
const webview = document.getElementById('mattermostView' + self.state.key);
|
2016-08-13 08:08:18 -07:00
|
|
|
webview.send('activate-search-box');
|
|
|
|
});
|
|
|
|
|
2016-09-04 14:36:22 -07:00
|
|
|
// activate search box in current chunnel
|
2016-09-25 07:14:01 -07:00
|
|
|
ipcRenderer.on('activate-search-box-in-channel', () => {
|
|
|
|
const webview = document.getElementById('mattermostView' + self.state.key);
|
2016-09-04 14:36:22 -07:00
|
|
|
webview.send('activate-search-box-in-channel');
|
|
|
|
});
|
|
|
|
|
2016-09-25 07:14:01 -07:00
|
|
|
function focusListener() {
|
|
|
|
self.handleOnTeamFocused(self.state.key);
|
|
|
|
self.refs[`mattermostView${self.state.key}`].focusOnWebView();
|
|
|
|
}
|
2016-01-14 04:51:06 -08:00
|
|
|
|
|
|
|
var currentWindow = remote.getCurrentWindow();
|
|
|
|
currentWindow.on('focus', focusListener);
|
2016-09-25 07:14:01 -07:00
|
|
|
window.addEventListener('beforeunload', () => {
|
2016-01-14 04:51:06 -08:00
|
|
|
currentWindow.removeListener('focus', focusListener);
|
2016-01-12 04:28:08 -08:00
|
|
|
});
|
2016-09-03 02:01:05 -07:00
|
|
|
|
|
|
|
//goBack and goForward
|
|
|
|
ipcRenderer.on('go-back', () => {
|
2016-09-25 07:14:01 -07:00
|
|
|
const mattermost = self.refs[`mattermostView${self.state.key}`];
|
2016-09-03 02:01:05 -07:00
|
|
|
if (mattermost.canGoBack()) {
|
|
|
|
mattermost.goBack();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
ipcRenderer.on('go-forward', () => {
|
2016-09-25 07:14:01 -07:00
|
|
|
const mattermost = self.refs[`mattermostView${self.state.key}`];
|
2016-09-03 02:01:05 -07:00
|
|
|
if (mattermost.canGoForward()) {
|
|
|
|
mattermost.goForward();
|
|
|
|
}
|
|
|
|
});
|
2016-01-12 04:28:08 -08:00
|
|
|
},
|
2016-09-25 07:14:01 -07:00
|
|
|
componentDidUpdate(prevProps, prevState) {
|
2016-09-29 07:25:22 -07:00
|
|
|
if (prevState.key !== this.state.key) { // i.e. When tab has been changed
|
|
|
|
this.refs[`mattermostView${this.state.key}`].focusOnWebView();
|
|
|
|
}
|
2016-08-25 08:38:24 -07:00
|
|
|
},
|
2016-09-25 07:14:01 -07:00
|
|
|
handleSelect(key) {
|
2016-05-18 08:25:21 -07:00
|
|
|
const newKey = (this.props.teams.length + key) % this.props.teams.length;
|
2015-12-22 07:34:24 -08:00
|
|
|
this.setState({
|
2016-05-18 07:02:03 -07:00
|
|
|
key: newKey
|
2015-12-22 23:21:33 -08:00
|
|
|
});
|
2016-08-25 08:38:24 -07:00
|
|
|
this.handleOnTeamFocused(newKey);
|
2016-08-12 11:49:43 -07:00
|
|
|
|
2016-08-25 08:38:24 -07:00
|
|
|
var webview = document.getElementById('mattermostView' + newKey);
|
2016-08-12 11:49:43 -07:00
|
|
|
ipcRenderer.send('update-title', {
|
|
|
|
title: webview.getTitle()
|
|
|
|
});
|
2015-12-22 23:21:33 -08:00
|
|
|
},
|
2016-09-25 07:14:01 -07:00
|
|
|
handleUnreadCountChange(index, unreadCount, mentionCount, isUnread, isMentioned) {
|
2016-01-18 17:53:17 -08:00
|
|
|
var unreadCounts = this.state.unreadCounts;
|
|
|
|
var mentionCounts = this.state.mentionCounts;
|
2016-01-25 07:35:59 -08:00
|
|
|
var unreadAtActive = this.state.unreadAtActive;
|
2016-02-01 06:58:59 -08:00
|
|
|
var mentionAtActiveCounts = this.state.mentionAtActiveCounts;
|
2016-01-18 17:53:17 -08:00
|
|
|
unreadCounts[index] = unreadCount;
|
|
|
|
mentionCounts[index] = mentionCount;
|
2016-09-25 07:14:01 -07:00
|
|
|
|
2016-01-25 07:35:59 -08:00
|
|
|
// Never turn on the unreadAtActive flag at current focused tab.
|
|
|
|
if (this.state.key !== index || !remote.getCurrentWindow().isFocused()) {
|
|
|
|
unreadAtActive[index] = unreadAtActive[index] || isUnread;
|
2016-02-01 06:58:59 -08:00
|
|
|
if (isMentioned) {
|
|
|
|
mentionAtActiveCounts[index]++;
|
|
|
|
}
|
2016-01-25 07:35:59 -08:00
|
|
|
}
|
2015-12-22 23:21:33 -08:00
|
|
|
this.setState({
|
2016-09-25 07:14:01 -07:00
|
|
|
unreadCounts,
|
|
|
|
mentionCounts,
|
|
|
|
unreadAtActive,
|
|
|
|
mentionAtActiveCounts
|
2015-12-22 07:34:24 -08:00
|
|
|
});
|
2016-01-18 19:43:32 -08:00
|
|
|
this.handleUnreadCountTotalChange();
|
|
|
|
},
|
2016-09-25 07:14:01 -07:00
|
|
|
markReadAtActive(index) {
|
2016-01-18 19:43:32 -08:00
|
|
|
var unreadAtActive = this.state.unreadAtActive;
|
2016-02-01 06:58:59 -08:00
|
|
|
var mentionAtActiveCounts = this.state.mentionAtActiveCounts;
|
2016-01-25 07:35:59 -08:00
|
|
|
unreadAtActive[index] = false;
|
2016-02-01 06:58:59 -08:00
|
|
|
mentionAtActiveCounts[index] = 0;
|
2016-01-18 19:43:32 -08:00
|
|
|
this.setState({
|
2016-09-25 07:14:01 -07:00
|
|
|
unreadAtActive,
|
|
|
|
mentionAtActiveCounts
|
2016-01-18 19:43:32 -08:00
|
|
|
});
|
|
|
|
this.handleUnreadCountTotalChange();
|
|
|
|
},
|
2016-09-25 07:14:01 -07:00
|
|
|
handleUnreadCountTotalChange() {
|
2015-12-23 00:06:17 -08:00
|
|
|
if (this.props.onUnreadCountChange) {
|
2016-09-25 07:14:01 -07:00
|
|
|
var allUnreadCount = this.state.unreadCounts.reduce((prev, curr) => {
|
2015-12-23 00:06:17 -08:00
|
|
|
return prev + curr;
|
2016-01-18 19:43:32 -08:00
|
|
|
}, 0);
|
2016-09-25 07:14:01 -07:00
|
|
|
this.state.unreadAtActive.forEach((state) => {
|
2016-01-18 19:43:32 -08:00
|
|
|
if (state) {
|
2016-01-23 04:08:05 -08:00
|
|
|
allUnreadCount += 1;
|
2016-01-18 19:43:32 -08:00
|
|
|
}
|
2015-12-23 00:06:17 -08:00
|
|
|
});
|
2016-09-25 07:14:01 -07:00
|
|
|
var allMentionCount = this.state.mentionCounts.reduce((prev, curr) => {
|
2016-01-18 17:53:17 -08:00
|
|
|
return prev + curr;
|
2016-01-23 04:08:05 -08:00
|
|
|
}, 0);
|
2016-09-25 07:14:01 -07:00
|
|
|
this.state.mentionAtActiveCounts.forEach((count) => {
|
2016-02-01 06:58:59 -08:00
|
|
|
allMentionCount += count;
|
2016-01-23 21:31:56 -08:00
|
|
|
});
|
2016-01-18 17:53:17 -08:00
|
|
|
this.props.onUnreadCountChange(allUnreadCount, allMentionCount);
|
2015-12-23 00:06:17 -08:00
|
|
|
}
|
2015-12-22 07:34:24 -08:00
|
|
|
},
|
2016-09-25 07:14:01 -07:00
|
|
|
handleOnTeamFocused(index) {
|
2016-01-18 19:43:32 -08:00
|
|
|
// Turn off the flag to indicate whether unread message of active channel contains at current tab.
|
2016-01-25 07:35:59 -08:00
|
|
|
this.markReadAtActive(index);
|
2016-01-18 19:43:32 -08:00
|
|
|
},
|
|
|
|
|
2016-09-25 07:14:01 -07:00
|
|
|
visibleStyle(visible) {
|
2015-12-23 03:53:54 -08:00
|
|
|
var visibility = visible ? 'visible' : 'hidden';
|
2015-12-22 07:34:24 -08:00
|
|
|
return {
|
|
|
|
position: 'absolute',
|
2016-01-13 05:07:37 -08:00
|
|
|
top: (this.props.teams.length > 1) ? 42 : 0,
|
2015-12-22 07:34:24 -08:00
|
|
|
right: 0,
|
|
|
|
bottom: 0,
|
|
|
|
left: 0,
|
2016-09-25 07:14:01 -07:00
|
|
|
visibility
|
2015-12-22 07:34:24 -08:00
|
|
|
};
|
|
|
|
},
|
2016-04-22 08:50:57 -07:00
|
|
|
|
2016-09-25 07:14:01 -07:00
|
|
|
handleLogin(request, username, password) {
|
2016-04-22 08:50:57 -07:00
|
|
|
ipcRenderer.send('login-credentials', request, username, password);
|
|
|
|
const loginQueue = this.state.loginQueue;
|
|
|
|
loginQueue.shift();
|
|
|
|
this.setState(loginQueue);
|
|
|
|
},
|
2016-09-25 07:14:01 -07:00
|
|
|
handleLoginCancel() {
|
2016-04-22 08:50:57 -07:00
|
|
|
const loginQueue = this.state.loginQueue;
|
|
|
|
loginQueue.shift();
|
|
|
|
this.setState(loginQueue);
|
|
|
|
},
|
2016-09-25 07:14:01 -07:00
|
|
|
render() {
|
|
|
|
var self = this;
|
2016-01-13 05:07:37 -08:00
|
|
|
|
2016-09-25 07:14:01 -07:00
|
|
|
var tabsRow;
|
2016-01-13 05:07:37 -08:00
|
|
|
if (this.props.teams.length > 1) {
|
2016-09-25 07:14:01 -07:00
|
|
|
tabsRow = (
|
2016-01-13 05:07:37 -08:00
|
|
|
<Row>
|
2016-09-25 07:14:01 -07:00
|
|
|
<TabBar
|
|
|
|
id='tabBar'
|
|
|
|
teams={this.props.teams}
|
|
|
|
unreadCounts={this.state.unreadCounts}
|
|
|
|
mentionCounts={this.state.mentionCounts}
|
|
|
|
unreadAtActive={this.state.unreadAtActive}
|
|
|
|
mentionAtActiveCounts={this.state.mentionAtActiveCounts}
|
|
|
|
activeKey={this.state.key}
|
|
|
|
onSelect={this.handleSelect}
|
|
|
|
/>
|
2016-01-13 05:07:37 -08:00
|
|
|
</Row>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2016-09-25 07:14:01 -07:00
|
|
|
var views = this.props.teams.map((team, index) => {
|
|
|
|
function handleUnreadCountChange(unreadCount, mentionCount, isUnread, isMentioned) {
|
|
|
|
self.handleUnreadCountChange(index, unreadCount, mentionCount, isUnread, isMentioned);
|
|
|
|
}
|
|
|
|
function handleNotificationClick() {
|
|
|
|
self.handleSelect(index);
|
2015-12-23 00:16:53 -08:00
|
|
|
}
|
2016-03-30 07:03:43 -07:00
|
|
|
var id = 'mattermostView' + index;
|
2016-09-25 07:14:01 -07:00
|
|
|
var isActive = self.state.key === index;
|
|
|
|
return (
|
|
|
|
<MattermostView
|
|
|
|
key={id}
|
|
|
|
id={id}
|
|
|
|
style={self.visibleStyle(isActive)}
|
|
|
|
src={team.url}
|
|
|
|
name={team.name}
|
|
|
|
onUnreadCountChange={handleUnreadCountChange}
|
|
|
|
onNotificationClick={handleNotificationClick}
|
|
|
|
ref={id}
|
|
|
|
active={isActive}
|
|
|
|
/>);
|
2015-12-22 07:34:24 -08:00
|
|
|
});
|
2016-09-25 07:14:01 -07:00
|
|
|
var viewsRow = (
|
|
|
|
<Row>
|
|
|
|
{views}
|
|
|
|
</Row>);
|
2016-04-22 08:50:57 -07:00
|
|
|
|
|
|
|
var request = null;
|
|
|
|
var authServerURL = null;
|
2016-04-25 04:41:02 -07:00
|
|
|
var authInfo = null;
|
2016-04-22 08:50:57 -07:00
|
|
|
if (this.state.loginQueue.length !== 0) {
|
|
|
|
request = this.state.loginQueue[0].request;
|
2016-09-25 07:14:01 -07:00
|
|
|
const tmpURL = url.parse(this.state.loginQueue[0].request.url);
|
|
|
|
authServerURL = `${tmpURL.protocol}//${tmpURL.host}`;
|
2016-04-25 04:41:02 -07:00
|
|
|
authInfo = this.state.loginQueue[0].authInfo;
|
2016-04-22 08:50:57 -07:00
|
|
|
}
|
2015-12-22 07:34:24 -08:00
|
|
|
return (
|
2016-04-22 08:50:57 -07:00
|
|
|
<div>
|
2016-09-25 07:14:01 -07:00
|
|
|
<LoginModal
|
|
|
|
show={this.state.loginQueue.length !== 0}
|
|
|
|
request={request}
|
|
|
|
authInfo={authInfo}
|
|
|
|
authServerURL={authServerURL}
|
|
|
|
onLogin={this.handleLogin}
|
|
|
|
onCancel={this.handleLoginCancel}
|
|
|
|
/>
|
2016-04-22 08:50:57 -07:00
|
|
|
<Grid fluid>
|
2016-09-25 07:14:01 -07:00
|
|
|
{ tabsRow }
|
|
|
|
{ viewsRow }
|
2016-04-22 08:50:57 -07:00
|
|
|
</Grid>
|
|
|
|
</div>
|
2016-11-08 06:54:53 -08:00
|
|
|
);
|
2015-12-22 07:34:24 -08:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2016-01-13 05:07:37 -08:00
|
|
|
var TabBar = React.createClass({
|
2016-09-25 07:14:01 -07:00
|
|
|
render() {
|
|
|
|
var self = this;
|
|
|
|
var tabs = this.props.teams.map((team, index) => {
|
2016-01-18 19:43:32 -08:00
|
|
|
var unreadCount = 0;
|
2016-04-13 10:39:36 -07:00
|
|
|
var badgeStyle = {
|
2016-07-21 08:35:05 -07:00
|
|
|
background: '#FF1744',
|
|
|
|
float: 'right',
|
|
|
|
color: 'white',
|
|
|
|
minWidth: '19px',
|
|
|
|
fontSize: '12px',
|
|
|
|
textAlign: 'center',
|
|
|
|
lineHeight: '20px',
|
|
|
|
height: '19px',
|
|
|
|
marginLeft: '5px',
|
2016-09-25 07:14:01 -07:00
|
|
|
borderRadius: '50%'
|
2016-04-13 10:39:36 -07:00
|
|
|
};
|
|
|
|
|
2016-09-25 07:14:01 -07:00
|
|
|
if (self.props.unreadCounts[index] > 0) {
|
|
|
|
unreadCount = self.props.unreadCounts[index];
|
2016-01-18 19:43:32 -08:00
|
|
|
}
|
2016-09-25 07:14:01 -07:00
|
|
|
if (self.props.unreadAtActive[index]) {
|
2016-01-18 19:43:32 -08:00
|
|
|
unreadCount += 1;
|
|
|
|
}
|
2016-01-25 07:35:59 -08:00
|
|
|
|
|
|
|
var mentionCount = 0;
|
2016-09-25 07:14:01 -07:00
|
|
|
if (self.props.mentionCounts[index] > 0) {
|
|
|
|
mentionCount = self.props.mentionCounts[index];
|
2016-01-25 07:35:59 -08:00
|
|
|
}
|
2016-09-25 07:14:01 -07:00
|
|
|
if (self.props.mentionAtActiveCounts[index] > 0) {
|
|
|
|
mentionCount += self.props.mentionAtActiveCounts[index];
|
2016-01-25 07:35:59 -08:00
|
|
|
}
|
|
|
|
|
2016-09-25 07:14:01 -07:00
|
|
|
var badgeDiv;
|
|
|
|
if (mentionCount !== 0) {
|
|
|
|
badgeDiv = (
|
|
|
|
<div style={badgeStyle}>
|
|
|
|
{mentionCount}
|
|
|
|
</div>);
|
2016-01-13 05:07:37 -08:00
|
|
|
}
|
2016-09-25 07:14:01 -07:00
|
|
|
var id = 'teamTabItem' + index;
|
|
|
|
if (unreadCount === 0) {
|
|
|
|
return (
|
|
|
|
<NavItem
|
|
|
|
className='teamTabItem'
|
|
|
|
key={id}
|
|
|
|
id={id}
|
|
|
|
eventKey={index}
|
|
|
|
>
|
|
|
|
{ team.name }
|
|
|
|
{ ' ' }
|
|
|
|
{ badgeDiv }
|
|
|
|
</NavItem>);
|
2016-07-21 08:35:05 -07:00
|
|
|
}
|
2016-09-25 07:14:01 -07:00
|
|
|
return (
|
|
|
|
<NavItem
|
|
|
|
className='teamTabItem'
|
|
|
|
key={id}
|
|
|
|
id={id}
|
|
|
|
eventKey={index}
|
|
|
|
>
|
|
|
|
<b>{ team.name }</b>
|
|
|
|
{ ' ' }
|
|
|
|
{ badgeDiv }
|
|
|
|
</NavItem>);
|
2016-01-13 05:07:37 -08:00
|
|
|
});
|
|
|
|
return (
|
2016-09-25 07:14:01 -07:00
|
|
|
<Nav
|
|
|
|
id={this.props.id}
|
|
|
|
bsStyle='tabs'
|
|
|
|
activeKey={this.props.activeKey}
|
|
|
|
onSelect={this.props.onSelect}
|
|
|
|
>
|
2016-01-13 05:07:37 -08:00
|
|
|
{ tabs }
|
|
|
|
</Nav>
|
2016-11-08 06:54:53 -08:00
|
|
|
);
|
2016-01-13 05:07:37 -08:00
|
|
|
}
|
|
|
|
});
|
2015-12-22 23:21:33 -08:00
|
|
|
|
2015-12-22 07:34:24 -08:00
|
|
|
var MattermostView = React.createClass({
|
2016-09-25 07:14:01 -07:00
|
|
|
getInitialState() {
|
2015-12-22 22:53:34 -08:00
|
|
|
return {
|
2016-06-13 07:55:26 -07:00
|
|
|
errorInfo: null
|
2015-12-22 22:53:34 -08:00
|
|
|
};
|
|
|
|
},
|
2016-09-25 07:14:01 -07:00
|
|
|
handleUnreadCountChange(unreadCount, mentionCount, isUnread, isMentioned) {
|
2015-12-22 22:53:34 -08:00
|
|
|
if (this.props.onUnreadCountChange) {
|
2016-01-25 07:35:59 -08:00
|
|
|
this.props.onUnreadCountChange(unreadCount, mentionCount, isUnread, isMentioned);
|
2016-01-18 19:43:32 -08:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2016-09-25 07:14:01 -07:00
|
|
|
componentDidMount() {
|
|
|
|
var self = this;
|
2015-12-22 22:53:34 -08:00
|
|
|
var webview = ReactDOM.findDOMNode(this.refs.webview);
|
|
|
|
|
2016-03-11 04:47:39 -08:00
|
|
|
// This option disables the same-origin policy and allows js/css/plugins not only content like images.
|
2016-04-17 07:07:17 -07:00
|
|
|
if (config.disablewebsecurity === true) {
|
|
|
|
// webview.setAttribute('disablewebsecurity', false) disables websecurity. (electron's bug?)
|
|
|
|
webview.setAttribute('disablewebsecurity', true);
|
|
|
|
}
|
2016-03-11 04:47:39 -08:00
|
|
|
|
2016-09-25 07:14:01 -07:00
|
|
|
webview.addEventListener('did-fail-load', (e) => {
|
|
|
|
console.log(self.props.name, 'webview did-fail-load', e);
|
2016-09-09 11:37:48 -07:00
|
|
|
if (e.errorCode === -3) { // An operation was aborted (due to user action).
|
2016-03-06 04:58:52 -08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-09-25 07:14:01 -07:00
|
|
|
self.setState({
|
2016-06-13 07:55:26 -07:00
|
|
|
errorInfo: e
|
2016-03-03 08:51:34 -08:00
|
|
|
});
|
2016-09-25 07:14:01 -07:00
|
|
|
function reload() {
|
2016-08-15 08:20:34 -07:00
|
|
|
window.removeEventListener('online', reload);
|
2016-09-25 07:14:01 -07:00
|
|
|
self.reload();
|
|
|
|
}
|
2016-08-15 08:20:34 -07:00
|
|
|
if (navigator.onLine) {
|
|
|
|
setTimeout(reload, 30000);
|
|
|
|
} else {
|
|
|
|
window.addEventListener('online', reload);
|
|
|
|
}
|
2016-03-03 08:51:34 -08:00
|
|
|
});
|
|
|
|
|
2015-12-22 22:53:34 -08:00
|
|
|
// Open link in browserWindow. for exmaple, attached files.
|
2016-09-25 07:14:01 -07:00
|
|
|
webview.addEventListener('new-window', (e) => {
|
2015-12-22 22:53:34 -08:00
|
|
|
var currentURL = url.parse(webview.getURL());
|
|
|
|
var destURL = url.parse(e.url);
|
2016-04-26 06:40:21 -07:00
|
|
|
if (destURL.protocol !== 'http:' && destURL.protocol !== 'https:') {
|
2016-05-21 21:39:52 -07:00
|
|
|
ipcRenderer.send('confirm-protocol', destURL.protocol, e.url);
|
2016-04-23 04:39:15 -07:00
|
|
|
return;
|
|
|
|
}
|
2015-12-22 22:53:34 -08:00
|
|
|
if (currentURL.host === destURL.host) {
|
2016-04-23 04:37:57 -07:00
|
|
|
// New window should disable nodeIntergration.
|
|
|
|
window.open(e.url, 'Mattermost', 'nodeIntegration=no');
|
2015-12-22 22:53:34 -08:00
|
|
|
} else {
|
|
|
|
// if the link is external, use default browser.
|
2016-06-12 06:56:22 -07:00
|
|
|
shell.openExternal(e.url);
|
2015-12-22 22:53:34 -08:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2016-09-25 07:14:01 -07:00
|
|
|
webview.addEventListener('dom-ready', () => {
|
2015-12-22 22:53:34 -08:00
|
|
|
// webview.openDevTools();
|
|
|
|
|
|
|
|
// Use 'Meiryo UI' and 'MS Gothic' to prevent CJK fonts on Windows(JP).
|
|
|
|
if (process.platform === 'win32') {
|
2016-09-25 07:14:01 -07:00
|
|
|
function applyCssFile(cssFile) {
|
|
|
|
fs.readFile(cssFile, 'utf8', (err, data) => {
|
2015-12-22 22:53:34 -08:00
|
|
|
if (err) {
|
|
|
|
console.log(err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
webview.insertCSS(data);
|
|
|
|
});
|
2016-09-25 07:14:01 -07:00
|
|
|
}
|
2015-12-22 22:53:34 -08:00
|
|
|
|
2016-09-25 07:14:01 -07:00
|
|
|
osLocale((err, locale) => {
|
2015-12-22 22:53:34 -08:00
|
|
|
if (err) {
|
|
|
|
console.log(err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (locale === 'ja_JP') {
|
|
|
|
applyCssFile(__dirname + '/css/jp_fonts.css');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2016-09-04 12:05:28 -07:00
|
|
|
|
2016-09-25 07:14:01 -07:00
|
|
|
electronContextMenu({
|
2016-09-04 12:05:28 -07:00
|
|
|
window: webview
|
|
|
|
});
|
2015-12-22 22:53:34 -08:00
|
|
|
});
|
|
|
|
|
2016-09-25 07:14:01 -07:00
|
|
|
webview.addEventListener('ipc-message', (event) => {
|
2015-12-22 22:53:34 -08:00
|
|
|
switch (event.channel) {
|
2016-09-25 07:14:01 -07:00
|
|
|
case 'onUnreadCountChange':
|
|
|
|
var unreadCount = event.args[0];
|
|
|
|
var mentionCount = event.args[1];
|
|
|
|
|
|
|
|
// isUnread and isMentioned is pulse flag.
|
|
|
|
var isUnread = event.args[2];
|
|
|
|
var isMentioned = event.args[3];
|
|
|
|
self.handleUnreadCountChange(unreadCount, mentionCount, isUnread, isMentioned);
|
|
|
|
break;
|
|
|
|
case 'onNotificationClick':
|
|
|
|
self.props.onNotificationClick();
|
|
|
|
break;
|
2015-12-22 22:53:34 -08:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2016-09-25 07:14:01 -07:00
|
|
|
webview.addEventListener('page-title-updated', (event) => {
|
|
|
|
if (self.props.active) {
|
2016-08-20 03:41:23 -07:00
|
|
|
ipcRenderer.send('update-title', {
|
|
|
|
title: event.title
|
|
|
|
});
|
|
|
|
}
|
2016-08-12 11:49:43 -07:00
|
|
|
});
|
|
|
|
|
2016-02-13 02:30:37 -08:00
|
|
|
webview.addEventListener('console-message', (e) => {
|
2016-02-13 02:30:37 -08:00
|
|
|
const message = `[${this.props.name}] ${e.message}`;
|
|
|
|
switch (e.level) {
|
2016-09-25 07:14:01 -07:00
|
|
|
case 0:
|
|
|
|
console.log(message);
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
console.warn(message);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
console.error(message);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
console.log(message);
|
|
|
|
break;
|
2016-02-13 02:30:37 -08:00
|
|
|
}
|
2016-02-13 02:30:37 -08:00
|
|
|
});
|
2015-12-22 22:53:34 -08:00
|
|
|
},
|
2016-09-25 07:14:01 -07:00
|
|
|
reload() {
|
2016-08-15 08:20:34 -07:00
|
|
|
this.setState({
|
|
|
|
errorInfo: null
|
|
|
|
});
|
2016-07-28 06:44:42 -07:00
|
|
|
var webview = ReactDOM.findDOMNode(this.refs.webview);
|
|
|
|
webview.reload();
|
|
|
|
},
|
|
|
|
clearCacheAndReload() {
|
2016-08-15 08:20:34 -07:00
|
|
|
this.setState({
|
|
|
|
errorInfo: null
|
|
|
|
});
|
2016-07-28 06:44:42 -07:00
|
|
|
var webContents = ReactDOM.findDOMNode(this.refs.webview).getWebContents();
|
|
|
|
webContents.session.clearCache(() => {
|
|
|
|
webContents.reload();
|
|
|
|
});
|
|
|
|
},
|
2016-09-03 02:01:05 -07:00
|
|
|
|
2016-09-25 07:14:01 -07:00
|
|
|
focusOnWebView() {
|
2016-08-25 08:38:24 -07:00
|
|
|
const webview = ReactDOM.findDOMNode(this.refs.webview);
|
|
|
|
if (!webview.getWebContents().isFocused()) {
|
|
|
|
webview.focus();
|
|
|
|
webview.getWebContents().focus();
|
|
|
|
}
|
|
|
|
},
|
2016-09-03 02:01:05 -07:00
|
|
|
|
|
|
|
canGoBack() {
|
|
|
|
const webview = ReactDOM.findDOMNode(this.refs.webview);
|
|
|
|
return webview.getWebContents().canGoBack();
|
|
|
|
},
|
|
|
|
|
|
|
|
canGoForward() {
|
|
|
|
const webview = ReactDOM.findDOMNode(this.refs.webview);
|
|
|
|
return webview.getWebContents().canGoForward();
|
|
|
|
},
|
|
|
|
|
|
|
|
goBack() {
|
|
|
|
const webview = ReactDOM.findDOMNode(this.refs.webview);
|
|
|
|
webview.getWebContents().goBack();
|
|
|
|
},
|
|
|
|
|
|
|
|
goForward() {
|
|
|
|
const webview = ReactDOM.findDOMNode(this.refs.webview);
|
|
|
|
webview.getWebContents().goForward();
|
|
|
|
},
|
|
|
|
|
2016-09-25 07:14:01 -07:00
|
|
|
render() {
|
|
|
|
const errorView = this.state.errorInfo ? (
|
|
|
|
<ErrorView
|
|
|
|
id={this.props.id + '-fail'}
|
|
|
|
style={this.props.style}
|
|
|
|
className='errorView'
|
|
|
|
errorInfo={this.state.errorInfo}
|
|
|
|
/>) : null;
|
|
|
|
|
2015-12-22 07:34:24 -08:00
|
|
|
// 'disablewebsecurity' is necessary to display external images.
|
|
|
|
// However, it allows also CSS/JavaScript.
|
|
|
|
// So webview should use 'allowDisplayingInsecureContent' as same as BrowserWindow.
|
2016-06-13 07:55:26 -07:00
|
|
|
|
|
|
|
// Need to keep webview mounted when failed to load.
|
2016-09-25 07:14:01 -07:00
|
|
|
return (
|
|
|
|
<div>
|
|
|
|
{ errorView }
|
|
|
|
<webview
|
|
|
|
id={this.props.id}
|
|
|
|
className='mattermostView'
|
|
|
|
style={this.props.style}
|
|
|
|
preload='webview/mattermost.js'
|
|
|
|
src={this.props.src}
|
|
|
|
ref='webview'
|
|
|
|
nodeintegration='false'
|
|
|
|
/>
|
|
|
|
</div>);
|
2016-03-03 08:51:34 -08:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// ErrorCode: https://code.google.com/p/chromium/codesearch#chromium/src/net/base/net_error_list.h
|
2016-08-18 10:46:52 -07:00
|
|
|
const errorPage = {
|
|
|
|
tableStyle: {
|
|
|
|
display: 'table',
|
|
|
|
width: '100%',
|
|
|
|
height: '100%',
|
|
|
|
position: 'absolute',
|
|
|
|
top: '0',
|
|
|
|
left: '0'
|
|
|
|
},
|
|
|
|
|
|
|
|
cellStyle: {
|
|
|
|
display: 'table-cell',
|
2016-10-05 11:53:33 -07:00
|
|
|
verticalAlign: 'top',
|
|
|
|
paddingTop: '2em'
|
2016-08-18 10:46:52 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
bullets: {
|
|
|
|
paddingLeft: '15px',
|
|
|
|
lineHeight: '1.7'
|
|
|
|
},
|
|
|
|
|
|
|
|
techInfo: {
|
|
|
|
fontSize: '12px',
|
|
|
|
color: '#aaa'
|
2016-09-25 07:14:01 -07:00
|
|
|
}
|
2016-08-18 10:46:52 -07:00
|
|
|
};
|
|
|
|
|
2016-03-03 08:51:34 -08:00
|
|
|
var ErrorView = React.createClass({
|
2016-09-25 07:14:01 -07:00
|
|
|
render() {
|
2016-03-03 08:51:34 -08:00
|
|
|
return (
|
2016-09-25 07:14:01 -07:00
|
|
|
<Grid
|
|
|
|
id={this.props.id}
|
|
|
|
style={this.props.style}
|
|
|
|
>
|
|
|
|
<div style={errorPage.tableStyle}>
|
|
|
|
<div style={errorPage.cellStyle}>
|
2016-08-18 10:46:52 -07:00
|
|
|
<Row>
|
2016-09-25 07:14:01 -07:00
|
|
|
<Col
|
|
|
|
xs={0}
|
|
|
|
sm={1}
|
|
|
|
md={1}
|
|
|
|
lg={2}
|
|
|
|
/>
|
|
|
|
<Col
|
|
|
|
xs={12}
|
|
|
|
sm={10}
|
|
|
|
md={10}
|
|
|
|
lg={8}
|
|
|
|
>
|
|
|
|
<h2>{'Cannot connect to Mattermost'}</h2>
|
|
|
|
<hr/>
|
|
|
|
<p>{'We\'re having trouble connecting to Mattermost. If refreshing this page (Ctrl+R or Command+R) does not work please verify that:'}</p>
|
|
|
|
<br/>
|
|
|
|
<ul style={errorPage.bullets}>
|
|
|
|
<li>{'Your computer is connected to the internet.'}</li>
|
|
|
|
<li>{'The Mattermost URL '}
|
|
|
|
<a href={this.props.errorInfo.validatedURL}>
|
|
|
|
{this.props.errorInfo.validatedURL}
|
|
|
|
</a>{' is correct.'}</li>
|
|
|
|
<li>{'You can reach '}
|
|
|
|
<a href={this.props.errorInfo.validatedURL}>
|
|
|
|
{this.props.errorInfo.validatedURL}
|
|
|
|
</a>{' from a browser window.'}</li>
|
|
|
|
</ul>
|
|
|
|
<br/>
|
|
|
|
<div style={errorPage.techInfo}>
|
|
|
|
{this.props.errorInfo.errorDescription}{' ('}
|
|
|
|
{this.props.errorInfo.errorCode }{')'}</div>
|
2016-08-18 10:46:52 -07:00
|
|
|
</Col>
|
2016-09-25 07:14:01 -07:00
|
|
|
<Col
|
|
|
|
xs={0}
|
|
|
|
sm={1}
|
|
|
|
md={1}
|
|
|
|
lg={2}
|
|
|
|
/>
|
2016-08-18 10:46:52 -07:00
|
|
|
</Row>
|
|
|
|
</div>
|
|
|
|
</div>
|
2016-03-03 08:51:34 -08:00
|
|
|
</Grid>
|
2016-11-08 06:54:53 -08:00
|
|
|
);
|
2015-12-22 07:34:24 -08:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2016-09-25 07:14:01 -07:00
|
|
|
function showUnreadBadgeWindows(unreadCount, mentionCount) {
|
|
|
|
function sendBadge(dataURL, description) {
|
2016-02-11 07:44:16 -08:00
|
|
|
// window.setOverlayIcon() does't work with NativeImage across remote boundaries.
|
|
|
|
// https://github.com/atom/electron/issues/4011
|
2016-05-20 11:06:38 -07:00
|
|
|
ipcRenderer.send('update-unread', {
|
2016-02-11 07:44:16 -08:00
|
|
|
overlayDataURL: dataURL,
|
2016-09-25 07:14:01 -07:00
|
|
|
description,
|
|
|
|
unreadCount,
|
|
|
|
mentionCount
|
2016-02-11 07:44:16 -08:00
|
|
|
});
|
2016-09-25 07:14:01 -07:00
|
|
|
}
|
2016-02-11 07:44:16 -08:00
|
|
|
|
|
|
|
if (mentionCount > 0) {
|
2016-07-12 06:01:01 -07:00
|
|
|
const dataURL = badge.createDataURL(mentionCount.toString());
|
2016-06-03 16:41:15 -07:00
|
|
|
sendBadge(dataURL, 'You have unread mentions (' + mentionCount + ')');
|
2016-07-15 04:04:14 -07:00
|
|
|
} else if (unreadCount > 0 && config.showUnreadBadge) {
|
2016-07-12 06:01:01 -07:00
|
|
|
const dataURL = badge.createDataURL('•');
|
2016-06-03 16:41:15 -07:00
|
|
|
sendBadge(dataURL, 'You have unread channels (' + unreadCount + ')');
|
2016-02-11 07:44:16 -08:00
|
|
|
} else {
|
2016-02-25 05:21:28 -08:00
|
|
|
sendBadge(null, 'You have no unread messages');
|
2016-02-11 07:44:16 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-25 07:14:01 -07:00
|
|
|
function showUnreadBadgeOSX(unreadCount, mentionCount) {
|
2016-02-11 07:44:16 -08:00
|
|
|
if (mentionCount > 0) {
|
|
|
|
remote.app.dock.setBadge(mentionCount.toString());
|
2016-07-15 04:04:14 -07:00
|
|
|
} else if (unreadCount > 0 && config.showUnreadBadge) {
|
2016-02-11 07:44:16 -08:00
|
|
|
remote.app.dock.setBadge('•');
|
|
|
|
} else {
|
|
|
|
remote.app.dock.setBadge('');
|
|
|
|
}
|
2016-04-06 08:49:20 -07:00
|
|
|
|
2016-05-20 11:06:38 -07:00
|
|
|
ipcRenderer.send('update-unread', {
|
2016-09-25 07:14:01 -07:00
|
|
|
unreadCount,
|
|
|
|
mentionCount
|
2016-04-06 08:49:20 -07:00
|
|
|
});
|
2016-02-11 07:44:16 -08:00
|
|
|
}
|
|
|
|
|
2016-09-25 07:14:01 -07:00
|
|
|
function showUnreadBadgeLinux(unreadCount, mentionCount) {
|
2016-06-29 12:28:43 -07:00
|
|
|
if (remote.app.isUnityRunning()) {
|
|
|
|
remote.app.setBadgeCount(mentionCount);
|
|
|
|
}
|
2016-05-07 13:21:07 -07:00
|
|
|
|
2016-05-20 11:06:38 -07:00
|
|
|
ipcRenderer.send('update-unread', {
|
2016-09-25 07:14:01 -07:00
|
|
|
unreadCount,
|
|
|
|
mentionCount
|
2016-05-07 13:21:07 -07:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-09-25 07:14:01 -07:00
|
|
|
function showUnreadBadge(unreadCount, mentionCount) {
|
2015-12-23 00:06:17 -08:00
|
|
|
switch (process.platform) {
|
2016-09-25 07:14:01 -07:00
|
|
|
case 'win32':
|
|
|
|
showUnreadBadgeWindows(unreadCount, mentionCount);
|
|
|
|
break;
|
|
|
|
case 'darwin':
|
|
|
|
showUnreadBadgeOSX(unreadCount, mentionCount);
|
|
|
|
break;
|
|
|
|
case 'linux':
|
|
|
|
showUnreadBadgeLinux(unreadCount, mentionCount);
|
|
|
|
break;
|
|
|
|
default:
|
2015-12-23 00:06:17 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-20 23:23:42 -07:00
|
|
|
ReactDOM.render(
|
2016-09-25 07:14:01 -07:00
|
|
|
<MainPage
|
|
|
|
teams={config.teams}
|
|
|
|
onUnreadCountChange={showUnreadBadge}
|
|
|
|
/>,
|
2016-05-20 23:23:42 -07:00
|
|
|
document.getElementById('content')
|
|
|
|
);
|