Merge branch 'master' of https://github.com/yuya-oc/electron-mattermost into topic-active-channel-badge

This commit is contained in:
mid0111
2016-01-19 12:36:33 +09:00
9 changed files with 188 additions and 59 deletions

View File

@@ -23,9 +23,11 @@ You don't have to install any other software.
## Usage ## Usage
### Installation ### Installation
Detailed guides are available at [docs/setup.md](docs/setup.md).
1. Download and unarchive a file from [release page](http://github.com/yuya-oc/electron-mattermost/releases). 1. Download and unarchive a file from [release page](http://github.com/yuya-oc/electron-mattermost/releases).
2. Launch `electron-mattermost` in the unarchived folder. 2. Launch `electron-mattermost` in the unarchived folder.
3. After first launching, please input URL for your Mattermost team. For example, `http://mattermost.example.com/team`. 3. After first launching, please input name and URL for your Mattermost team. For example, `myteam : http://mattermost.example.com/team`.
### Quit ### Quit
Ctrl or Command + Q to quit. Ctrl or Command + Q to quit.

View File

@@ -1,12 +1,13 @@
# Electron Mattermost Setup Guides # Electron-Mattermost Setup Guides
## Step-by-step Windows 7 setup ## Step-by-step Windows 7 setup
Windows 8 and 10 have similar way.
1. Download [the latest release](https://github.com/yuya-oc/electron-mattermost/releases) of electron Mattermost 1. Download [the latest release](https://github.com/yuya-oc/electron-mattermost/releases) of electron-attermost
Find the file ending in `-win64.zip` if you're running a x64 version of Windows and `-win32.zip` if you're running an older 32-bit version. Find the file ending in `-win64.zip` if you're running a x64 version of Windows and `-win32.zip` if you're running an older 32-bit version.
2. From the `/Downloads` directory right click on the file and select "Extract All..." 2. From the `/Downloads` directory right-click on the file and select "Extract All..."
A new directory should be created on your PC. A new directory should be created on your PC.
@@ -14,12 +15,40 @@
You should see a new application called **electron-mattermost** open. You should see a new application called **electron-mattermost** open.
4. Click `Alt` to bring up the menu at the top of the screen, then click `Settings` 4. Press `Alt` key to bring up the menu at the top of the window, then click `File -> Settings`
5. For each Mattermost team you'd like to use, enter its **Name** and **URL** then click **Add** 5. For each Mattermost team you'd like to use, enter its **Name** and **URL** then click **Add**
6. Click **Save** to save your setting 6. Click **Save** to save your setting
You're now ready to use Electron Mattermost to interact with multiple teams from one desktop application You're now ready to use electron-mattermost to interact with multiple teams from one desktop application
To quit, use `Ctrl+Q` To quit, use `Ctrl+Q`
## Step-by-step OS X setup
For OS X 10.11 El Capitan. An older version of OS X has similar way.
1. Download [the latest release](https://github.com/yuya-oc/electron-mattermost/releases) of electron-mattermost
Find the file ending in `-osx.tar.gz`.
2. From the `/Downloads` directory double-click on the file."
A new directory should be created on your Mac.
3. Go to `/electron-mattermost...` directory and right-click on `electron-mattermost` package and select "Open"
If you see a dialog to confirm the application, select "Open".
You should see a new application called **electron-mattermost** open.
4. Click `electron-mattermost` from the menu at the top of the screen, then click `Settings`
5. For each Mattermost team you'd like to use, enter its **Name** and **URL** then click **Add**
6. Click **Save** to save your setting
You're now ready to use electron-mattermost to interact with multiple teams from one desktop application
To quit, use `Command+Q`

View File

@@ -1,7 +1,7 @@
{ {
"name": "electron-mattermost", "name": "electron-mattermost",
"version": "1.0.1", "version": "1.0.2",
"description": "Desktop app for Mattermost that run on Electron", "description": "Electron-based desktop application for Mattermost",
"main": "main.js", "main": "main.js",
"author": "Yuya Ochiai", "author": "Yuya Ochiai",
"license": "MIT", "license": "MIT",
@@ -25,7 +25,7 @@
"del": "^2.2.0", "del": "^2.2.0",
"electron-connect": "^0.3.3", "electron-connect": "^0.3.3",
"electron-packager": "^5.1.0", "electron-packager": "^5.1.0",
"electron-prebuilt": "0.36.2", "electron-prebuilt": "0.36.3",
"esformatter": "^0.8.1", "esformatter": "^0.8.1",
"esformatter-jsx": "^4.0.6", "esformatter-jsx": "^4.0.6",
"gulp": "^3.9.0", "gulp": "^3.9.0",

View File

@@ -17,6 +17,8 @@ const path = require('path');
const settings = require('../common/settings'); const settings = require('../common/settings');
remote.getCurrentWindow().removeAllListeners('focus');
var MainPage = React.createClass({ var MainPage = React.createClass({
getInitialState: function() { getInitialState: function() {
return { return {
@@ -24,6 +26,19 @@ var MainPage = React.createClass({
unreadCounts: new Array(this.props.teams.length) unreadCounts: new Array(this.props.teams.length)
}; };
}, },
componentDidMount: function() {
var thisObj = this;
var focusListener = function() {
var webview = document.getElementById('mattermostView' + thisObj.state.key);
webview.focus();
};
var currentWindow = remote.getCurrentWindow();
currentWindow.on('focus', focusListener);
window.addEventListener('beforeunload', function() {
currentWindow.removeListener('focus', focusListener);
});
},
handleSelect: function(key) { handleSelect: function(key) {
this.setState({ this.setState({
key: key key: key
@@ -46,7 +61,7 @@ var MainPage = React.createClass({
var visibility = visible ? 'visible' : 'hidden'; var visibility = visible ? 'visible' : 'hidden';
return { return {
position: 'absolute', position: 'absolute',
top: 42, top: (this.props.teams.length > 1) ? 42 : 0,
right: 0, right: 0,
bottom: 0, bottom: 0,
left: 0, left: 0,
@@ -55,19 +70,16 @@ var MainPage = React.createClass({
}, },
render: function() { render: function() {
var thisObj = this; var thisObj = this;
var tabs = this.props.teams.map(function(team, index) {
var badge; var tabs_row;
if (thisObj.state.unreadCounts[index] != 0) { if (this.props.teams.length > 1) {
badge = (<Badge> tabs_row = (
{ thisObj.state.unreadCounts[index] } <Row>
</Badge>); <TabBar id="tabBar" teams={ this.props.teams } unreadCounts={ this.state.unreadCounts } activeKey={ this.state.key } onSelect={ this.handleSelect }></TabBar>
</Row>
);
} }
return (<NavItem className="teamTabItem" id={ 'teamTabItem' + index } eventKey={ index }>
{ team.name }
{ ' ' }
{ badge }
</NavItem>);
});
var views = this.props.teams.map(function(team, index) { var views = this.props.teams.map(function(team, index) {
var handleUnreadCountChange = function(count) { var handleUnreadCountChange = function(count) {
thisObj.handleUnreadCountChange(index, count); thisObj.handleUnreadCountChange(index, count);
@@ -78,21 +90,41 @@ var MainPage = React.createClass({
return (<MattermostView id={ 'mattermostView' + index } style={ thisObj.visibleStyle(thisObj.state.key === index) } src={ team.url } onUnreadCountChange={ handleUnreadCountChange } onNotificationClick={ handleNotificationClick } return (<MattermostView id={ 'mattermostView' + index } style={ thisObj.visibleStyle(thisObj.state.key === index) } src={ team.url } onUnreadCountChange={ handleUnreadCountChange } onNotificationClick={ handleNotificationClick }
/>) />)
}); });
var views_row = (<Row>
{ views }
</Row>);
return ( return (
<Grid fluid> <Grid fluid>
<Row> { tabs_row }
<Nav bsStyle="tabs" activeKey={ this.state.key } onSelect={ this.handleSelect }> { views_row }
{ tabs }
</Nav>
</Row>
<Row>
{ views }
</Row>
</Grid> </Grid>
); );
} }
}); });
var TabBar = React.createClass({
render: function() {
var thisObj = this;
var tabs = this.props.teams.map(function(team, index) {
var badge;
if (thisObj.props.unreadCounts[index] != 0) {
badge = (<Badge>
{ thisObj.props.unreadCounts[index] }
</Badge>);
}
return (<NavItem className="teamTabItem" id={ 'teamTabItem' + index } eventKey={ index }>
{ team.name }
{ ' ' }
{ badge }
</NavItem>);
});
return (
<Nav id={ this.props.id } bsStyle="tabs" activeKey={ this.props.activeKey } onSelect={ this.props.onSelect }>
{ tabs }
</Nav>
);
}
});
var MattermostView = React.createClass({ var MattermostView = React.createClass({
getInitialState: function() { getInitialState: function() {
@@ -178,6 +210,9 @@ try {
} catch (e) { } catch (e) {
window.location = 'settings.html'; window.location = 'settings.html';
} }
if (config.teams.length === 0) {
window.location = 'settings.html';
}
var contextMenu = require('./menus/context'); var contextMenu = require('./menus/context');
var menu = contextMenu.createDefault(); var menu = contextMenu.createDefault();

View File

@@ -6,6 +6,7 @@ const settings = require('../common/settings');
const Grid = ReactBootstrap.Grid; const Grid = ReactBootstrap.Grid;
const Row = ReactBootstrap.Row; const Row = ReactBootstrap.Row;
const Col = ReactBootstrap.Col; const Col = ReactBootstrap.Col;
const Input = ReactBootstrap.Input;
const Button = ReactBootstrap.Button; const Button = ReactBootstrap.Button;
const ListGroup = ReactBootstrap.ListGroup; const ListGroup = ReactBootstrap.ListGroup;
const ListGroupItem = ReactBootstrap.ListGroupItem; const ListGroupItem = ReactBootstrap.ListGroupItem;
@@ -13,16 +14,17 @@ const Glyphicon = ReactBootstrap.Glyphicon;
var SettingsPage = React.createClass({ var SettingsPage = React.createClass({
getInitialState: function() { getInitialState: function() {
var config;
try {
config = settings.readFileSync(this.props.configFile);
} catch (e) {
config = settings.loadDefault();
}
return { return {
teams: [] teams: config.teams,
hideMenuBar: config.hideMenuBar
}; };
}, },
componentDidMount: function() {
var config = settings.readFileSync(this.props.configFile);
this.setState({
teams: config.teams
})
},
handleTeamsChange: function(teams) { handleTeamsChange: function(teams) {
this.setState({ this.setState({
teams: teams teams: teams
@@ -31,28 +33,57 @@ var SettingsPage = React.createClass({
handleSave: function() { handleSave: function() {
var config = { var config = {
teams: this.state.teams, teams: this.state.teams,
version: 1 hideMenuBar: this.state.hideMenuBar,
version: settings.version
}; };
settings.writeFileSync(this.props.configFile, config); settings.writeFileSync(this.props.configFile, config);
if (process.platform === 'win32') {
var currentWindow = remote.getCurrentWindow();
currentWindow.setAutoHideMenuBar(config.hideMenuBar);
currentWindow.setMenuBarVisibility(!config.hideMenuBar);
}
window.location = './index.html'; window.location = './index.html';
}, },
handleCancel: function() { handleCancel: function() {
window.location = './index.html'; window.location = './index.html';
}, },
handleChangeHideMenuBar: function() {
this.setState({
hideMenuBar: this.refs.hideMenuBar.getChecked()
});
},
render: function() { render: function() {
return ( var teams_row = (
<Grid className="settingsPage">
<Row> <Row>
<Col md={ 12 }> <Col md={ 12 }>
<h2>Teams</h2> <h2>Teams</h2>
<TeamList teams={ this.state.teams } onTeamsChange={ this.handleTeamsChange } /> <TeamList teams={ this.state.teams } onTeamsChange={ this.handleTeamsChange } />
</Col> </Col>
</Row> </Row>
);
var options = [];
if (process.platform === 'win32') {
options.push(<Input ref="hideMenuBar" type="checkbox" label="Hide menubar (Press Alt to show menubar)" checked={ this.state.hideMenuBar } onChange={ this.handleChangeHideMenuBar } />);
}
var options_row = (options.length > 0) ? (
<Row>
<Col md={ 12 }>
<h2>Options</h2>
{ options }
</Col>
</Row>
) : null;
return (
<Grid className="settingsPage">
{ teams_row }
{ options_row }
<Row> <Row>
<Col md={ 12 }> <Col md={ 12 }>
<Button id="btnCancel" onClick={ this.handleCancel }>Cancel</Button> <Button id="btnCancel" onClick={ this.handleCancel }>Cancel</Button>
{ ' ' } { ' ' }
<Button id="btnSave" bsStyle="primary" onClick={ this.handleSave }>Save</Button> <Button id="btnSave" bsStyle="primary" onClick={ this.handleSave } disabled={ this.state.teams.length === 0 }>Save</Button>
</Col> </Col>
</Row> </Row>
</Grid> </Grid>

View File

@@ -9,6 +9,7 @@ var upgradeV0toV1 = function(config_v0) {
name: 'Primary team', name: 'Primary team',
url: config_v0.url url: config_v0.url
}], }],
hideMenuBar: false,
version: 1 version: 1
}; };
}; };
@@ -38,5 +39,13 @@ module.exports = {
} }
var data = JSON.stringify(config, null, ' '); var data = JSON.stringify(config, null, ' ');
fs.writeFileSync(configFile, data, 'utf8'); fs.writeFileSync(configFile, data, 'utf8');
},
loadDefault: function() {
return {
teams: [],
hideMenuBar: false,
version: version
};
} }
}; };

View File

@@ -28,9 +28,10 @@ else {
global['config-file'] = app.getPath('userData') + '/config.json' global['config-file'] = app.getPath('userData') + '/config.json'
} }
var config = {};
try { try {
var configFile = global['config-file']; var configFile = global['config-file'];
var config = settings.readFileSync(configFile); config = settings.readFileSync(configFile);
if (config.version != settings.version) { if (config.version != settings.version) {
config = settings.upgrade(config); config = settings.upgrade(config);
settings.writeFileSync(configFile, config); settings.writeFileSync(configFile, config);
@@ -61,9 +62,11 @@ app.on('window-all-closed', function() {
// For win32, auto-hide menu bar. // For win32, auto-hide menu bar.
app.on('browser-window-created', function(event, window) { app.on('browser-window-created', function(event, window) {
if (process.platform === 'win32') { if (process.platform === 'win32') {
if (config.hideMenuBar) {
window.setAutoHideMenuBar(true); window.setAutoHideMenuBar(true);
window.setMenuBarVisibility(false); window.setMenuBarVisibility(false);
} }
}
}); });
// For OSX, show hidden mainWindow when clicking dock icon. // For OSX, show hidden mainWindow when clicking dock icon.
@@ -111,6 +114,9 @@ app.on('ready', function() {
} }
window_options.icon = __dirname + '/resources/appicon.png'; window_options.icon = __dirname + '/resources/appicon.png';
mainWindow = new BrowserWindow(window_options); mainWindow = new BrowserWindow(window_options);
if (window_options.maximized) {
mainWindow.maximize();
}
// and load the index.html of the app. // and load the index.html of the app.
mainWindow.loadURL('file://' + __dirname + '/browser/index.html'); mainWindow.loadURL('file://' + __dirname + '/browser/index.html');
@@ -118,10 +124,16 @@ app.on('ready', function() {
// Open the DevTools. // Open the DevTools.
// mainWindow.openDevTools(); // mainWindow.openDevTools();
var saveWindowState = function(file, window) {
var window_state = window.getBounds();
window_state.maximized = window.isMaximized();
window_state.fullscreen = window.isFullScreen();
fs.writeFileSync(bounds_info_path, JSON.stringify(window_state));
};
mainWindow.on('close', function(event) { mainWindow.on('close', function(event) {
if (willAppQuit) { // when [Ctrl|Cmd]+Q if (willAppQuit) { // when [Ctrl|Cmd]+Q
var bounds = mainWindow.getBounds(); saveWindowState(bounds_info_path, mainWindow);
fs.writeFileSync(bounds_info_path, JSON.stringify(bounds));
} }
else { // Minimize or hide the window for close button. else { // Minimize or hide the window for close button.
event.preventDefault(); event.preventDefault();
@@ -144,8 +156,7 @@ app.on('ready', function() {
// 'blur' event was effective in order to avoid this. // 'blur' event was effective in order to avoid this.
// Ideally, app should detect that OS is shutting down. // Ideally, app should detect that OS is shutting down.
mainWindow.on('blur', function() { mainWindow.on('blur', function() {
var bounds = mainWindow.getBounds(); saveWindowState(bounds_info_path, mainWindow);
fs.writeFileSync(bounds_info_path, JSON.stringify(bounds));
}); });
var app_menu = appMenu.createMenu(mainWindow); var app_menu = appMenu.createMenu(mainWindow);

View File

@@ -1,7 +1,7 @@
{ {
"name": "electron-mattermost", "name": "electron-mattermost",
"version": "1.0.1", "version": "1.0.2",
"description": "Desktop app for Mattermost that run on Electron", "description": "Electron-based desktop application for Mattermost",
"main": "main.js", "main": "main.js",
"author": "Yuya Ochiai", "author": "Yuya Ochiai",
"license": "MIT", "license": "MIT",

View File

@@ -106,10 +106,22 @@ describe('electron-mattermost', function() {
}] }]
}; };
before(function() { beforeEach(function() {
fs.writeFileSync(config_file_path, JSON.stringify(config)); fs.writeFileSync(config_file_path, JSON.stringify(config));
}); });
it('should NOT show tabs when there is one team', function() {
fs.writeFileSync(config_file_path, JSON.stringify({
url: mattermost_url
}));
return client
.init()
.isExisting('#tabBar').then(function(isExisting) {
isExisting.should.be.false();
})
.end();
});
it('should set src of webview from config file', function() { it('should set src of webview from config file', function() {
return client return client
.init() .init()