Files
mattermostest/src/browser/components/MainPage.jsx
sudheer e95c8cc3c5 Add CTRL+F shortcut to work as browser search
* Add new finder for each webview contents
2018-07-06 12:21:06 +05:30

403 lines
12 KiB
JavaScript

// Copyright (c) 2015-2016 Yuya Ochiai
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import url from 'url';
import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import {CSSTransition, TransitionGroup} from 'react-transition-group';
import {Grid, Row} from 'react-bootstrap';
import {ipcRenderer, remote} from 'electron';
import Finder from '../../main/finder';
import Utils from '../../utils/util.js';
import LoginModal from './LoginModal.jsx';
import MattermostView from './MattermostView.jsx';
import TabBar from './TabBar.jsx';
import HoveringURL from './HoveringURL.jsx';
import PermissionRequestDialog from './PermissionRequestDialog.jsx';
import NewTeamModal from './NewTeamModal.jsx';
const MainPage = createReactClass({
propTypes: {
onUnreadCountChange: PropTypes.func.isRequired,
teams: PropTypes.array.isRequired,
onTeamConfigChange: PropTypes.func.isRequired,
initialIndex: PropTypes.number.isRequired,
useSpellChecker: PropTypes.bool.isRequired,
onSelectSpellCheckerLocale: PropTypes.func.isRequired,
deeplinkingUrl: PropTypes.string,
showAddServerButton: PropTypes.bool.isRequired,
requestingPermission: TabBar.propTypes.requestingPermission,
onClickPermissionDialog: PropTypes.func,
},
getInitialState() {
let key = this.props.initialIndex;
if (this.props.deeplinkingUrl !== null) {
for (var i = 0; i < this.props.teams.length; i++) {
if (this.props.deeplinkingUrl.includes(this.props.teams[i].url)) {
key = i;
break;
}
}
}
return {
key,
unreadCounts: new Array(this.props.teams.length),
mentionCounts: new Array(this.props.teams.length),
unreadAtActive: new Array(this.props.teams.length),
mentionAtActiveCounts: new Array(this.props.teams.length),
loginQueue: [],
targetURL: '',
};
},
componentDidMount() {
var self = this;
ipcRenderer.on('login-request', (event, request, authInfo) => {
self.setState({
loginRequired: true,
});
const loginQueue = self.state.loginQueue;
loginQueue.push({
request,
authInfo,
});
self.setState({
loginQueue,
});
});
// can't switch tabs sequencially for some reason...
ipcRenderer.on('switch-tab', (event, key) => {
this.handleSelect(key);
});
ipcRenderer.on('select-next-tab', () => {
this.handleSelect(this.state.key + 1);
});
ipcRenderer.on('select-previous-tab', () => {
this.handleSelect(this.state.key - 1);
});
// reload the activated tab
ipcRenderer.on('reload-tab', () => {
this.refs[`mattermostView${this.state.key}`].reload();
});
ipcRenderer.on('clear-cache-and-reload-tab', () => {
this.refs[`mattermostView${this.state.key}`].clearCacheAndReload();
});
function focusListener() {
self.handleOnTeamFocused(self.state.key);
self.refs[`mattermostView${self.state.key}`].focusOnWebView();
}
var currentWindow = remote.getCurrentWindow();
currentWindow.on('focus', focusListener);
window.addEventListener('beforeunload', () => {
currentWindow.removeListener('focus', focusListener);
});
// https://github.com/mattermost/desktop/pull/371#issuecomment-263072803
currentWindow.webContents.on('devtools-closed', () => {
focusListener();
});
//goBack and goForward
ipcRenderer.on('go-back', () => {
const mattermost = self.refs[`mattermostView${self.state.key}`];
if (mattermost.canGoBack()) {
mattermost.goBack();
}
});
ipcRenderer.on('go-forward', () => {
const mattermost = self.refs[`mattermostView${self.state.key}`];
if (mattermost.canGoForward()) {
mattermost.goForward();
}
});
ipcRenderer.on('add-server', () => {
this.addServer();
});
ipcRenderer.on('focus-on-webview', () => {
this.focusOnWebView();
});
ipcRenderer.on('protocol-deeplink', (event, deepLinkUrl) => {
const lastUrlDomain = Utils.getDomain(deepLinkUrl);
for (var i = 0; i < this.props.teams.length; i++) {
if (lastUrlDomain === Utils.getDomain(self.refs[`mattermostView${i}`].getSrc())) {
if (this.state.key !== i) {
this.handleSelect(i);
}
self.refs[`mattermostView${i}`].handleDeepLink(deepLinkUrl.replace(lastUrlDomain, ''));
break;
}
}
});
const webview = document.getElementById('mattermostView' + this.state.key);
this.finder = new Finder(webview);
ipcRenderer.on('toggle-find', () => {
this.finder.toggle();
});
},
componentDidUpdate(prevProps, prevState) {
if (prevState.key !== this.state.key) { // i.e. When tab has been changed
this.refs[`mattermostView${this.state.key}`].focusOnWebView();
this.finder.destroy();
const webview = document.getElementById('mattermostView' + this.state.key);
this.finder = new Finder(webview);
}
},
handleSelect(key) {
const newKey = (this.props.teams.length + key) % this.props.teams.length;
this.setState({
key: newKey,
});
var webview = document.getElementById('mattermostView' + newKey);
ipcRenderer.send('update-title', {
title: webview.getTitle(),
});
this.handleOnTeamFocused(newKey);
},
handleUnreadCountChange(index, unreadCount, mentionCount, isUnread, isMentioned) {
var unreadCounts = this.state.unreadCounts;
var mentionCounts = this.state.mentionCounts;
var unreadAtActive = this.state.unreadAtActive;
var mentionAtActiveCounts = this.state.mentionAtActiveCounts;
unreadCounts[index] = unreadCount;
mentionCounts[index] = mentionCount;
// Never turn on the unreadAtActive flag at current focused tab.
if (this.state.key !== index || !remote.getCurrentWindow().isFocused()) {
unreadAtActive[index] = unreadAtActive[index] || isUnread;
if (isMentioned) {
mentionAtActiveCounts[index]++;
}
}
this.setState({
unreadCounts,
mentionCounts,
unreadAtActive,
mentionAtActiveCounts,
});
this.handleUnreadCountTotalChange();
},
markReadAtActive(index) {
var unreadAtActive = this.state.unreadAtActive;
var mentionAtActiveCounts = this.state.mentionAtActiveCounts;
unreadAtActive[index] = false;
mentionAtActiveCounts[index] = 0;
this.setState({
unreadAtActive,
mentionAtActiveCounts,
});
this.handleUnreadCountTotalChange();
},
handleUnreadCountTotalChange() {
if (this.props.onUnreadCountChange) {
var allUnreadCount = this.state.unreadCounts.reduce((prev, curr) => {
return prev + curr;
}, 0);
this.state.unreadAtActive.forEach((state) => {
if (state) {
allUnreadCount += 1;
}
});
var allMentionCount = this.state.mentionCounts.reduce((prev, curr) => {
return prev + curr;
}, 0);
this.state.mentionAtActiveCounts.forEach((count) => {
allMentionCount += count;
});
this.props.onUnreadCountChange(allUnreadCount, allMentionCount);
}
},
handleOnTeamFocused(index) {
// Turn off the flag to indicate whether unread message of active channel contains at current tab.
this.markReadAtActive(index);
},
handleLogin(request, username, password) {
ipcRenderer.send('login-credentials', request, username, password);
const loginQueue = this.state.loginQueue;
loginQueue.shift();
this.setState({loginQueue});
},
handleLoginCancel() {
const loginQueue = this.state.loginQueue;
loginQueue.shift();
this.setState({loginQueue});
},
handleTargetURLChange(targetURL) {
clearTimeout(this.targetURLDisappearTimeout);
if (targetURL === '') {
// set delay to avoid momentary disappearance when hovering over multiple links
this.targetURLDisappearTimeout = setTimeout(() => {
this.setState({targetURL: ''});
}, 500);
} else {
this.setState({targetURL});
}
},
addServer() {
this.setState({
showNewTeamModal: true,
});
},
focusOnWebView() {
this.refs[`mattermostView${this.state.key}`].focusOnWebView();
},
render() {
var self = this;
var tabsRow;
if (this.props.teams.length > 1) {
tabsRow = (
<Row>
<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}
onAddServer={this.addServer}
showAddServerButton={this.props.showAddServerButton}
requestingPermission={this.props.requestingPermission}
onClickPermissionDialog={this.props.onClickPermissionDialog}
/>
</Row>
);
}
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);
}
var id = 'mattermostView' + index;
var isActive = self.state.key === index;
let teamUrl = team.url;
const deeplinkingUrl = this.props.deeplinkingUrl;
if (deeplinkingUrl !== null && deeplinkingUrl.includes(teamUrl)) {
teamUrl = deeplinkingUrl;
}
return (
<MattermostView
key={id}
id={id}
withTab={this.props.teams.length > 1}
useSpellChecker={this.props.useSpellChecker}
onSelectSpellCheckerLocale={this.props.onSelectSpellCheckerLocale}
src={teamUrl}
name={team.name}
onTargetURLChange={self.handleTargetURLChange}
onUnreadCountChange={handleUnreadCountChange}
onNotificationClick={handleNotificationClick}
ref={id}
active={isActive}
/>);
});
var viewsRow = (
<Row>
{views}
</Row>);
var request = null;
var authServerURL = null;
var authInfo = null;
if (this.state.loginQueue.length !== 0) {
request = this.state.loginQueue[0].request;
const tmpURL = url.parse(this.state.loginQueue[0].request.url);
authServerURL = `${tmpURL.protocol}//${tmpURL.host}`;
authInfo = this.state.loginQueue[0].authInfo;
}
var modal = (
<NewTeamModal
show={this.state.showNewTeamModal}
onClose={() => {
this.setState({
showNewTeamModal: false,
});
}}
onSave={(newTeam) => {
this.props.teams.push(newTeam);
this.setState({
showNewTeamModal: false,
key: this.props.teams.length - 1,
});
this.render();
this.props.onTeamConfigChange(this.props.teams);
}}
/>
);
return (
<div
className='MainPage'
onClick={this.focusOnWebView}
>
<LoginModal
show={this.state.loginQueue.length !== 0}
request={request}
authInfo={authInfo}
authServerURL={authServerURL}
onLogin={this.handleLogin}
onCancel={this.handleLoginCancel}
/>
{this.props.teams.length === 1 && this.props.requestingPermission[0] ? // eslint-disable-line multiline-ternary
<PermissionRequestDialog
id='MainPage-permissionDialog'
placement='bottom'
{...this.props.requestingPermission[0]}
onClickAllow={this.props.onClickPermissionDialog.bind(null, 0, 'allow')}
onClickBlock={this.props.onClickPermissionDialog.bind(null, 0, 'block')}
onClickClose={this.props.onClickPermissionDialog.bind(null, 0, 'close')}
/> : null
}
<Grid fluid={true}>
{ tabsRow }
{ viewsRow }
</Grid>
<TransitionGroup>
{ (this.state.targetURL === '') ?
null :
<CSSTransition
classNames='hovering'
timeout={{enter: 300, exit: 500}}
>
<HoveringURL
key='hoveringURL'
targetURL={this.state.targetURL}
/>
</CSSTransition>
}
</TransitionGroup>
<div>
{ modal }
</div>
</div>
);
},
});
export default MainPage;