Merge branch 'master' of https://github.com/yuya-oc/electron-mattermost into topic-active-channel-badge
This commit is contained in:
commit
306d383994
|
@ -23,9 +23,11 @@ You don't have to install any other software.
|
|||
## Usage
|
||||
|
||||
### 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).
|
||||
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
|
||||
Ctrl or Command + Q to quit.
|
||||
|
|
|
@ -1,25 +1,54 @@
|
|||
# Electron Mattermost Setup Guides
|
||||
# Electron-Mattermost Setup Guides
|
||||
|
||||
## 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.
|
||||
|
||||
3. Go to `/electron-mattermost...` directory and double-click on `electron-mattermost` file to start the application
|
||||
|
||||
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**
|
||||
|
||||
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`
|
||||
|
||||
|
||||
## 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`
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "electron-mattermost",
|
||||
"version": "1.0.1",
|
||||
"description": "Desktop app for Mattermost that run on Electron",
|
||||
"version": "1.0.2",
|
||||
"description": "Electron-based desktop application for Mattermost",
|
||||
"main": "main.js",
|
||||
"author": "Yuya Ochiai",
|
||||
"license": "MIT",
|
||||
|
@ -25,7 +25,7 @@
|
|||
"del": "^2.2.0",
|
||||
"electron-connect": "^0.3.3",
|
||||
"electron-packager": "^5.1.0",
|
||||
"electron-prebuilt": "0.36.2",
|
||||
"electron-prebuilt": "0.36.3",
|
||||
"esformatter": "^0.8.1",
|
||||
"esformatter-jsx": "^4.0.6",
|
||||
"gulp": "^3.9.0",
|
||||
|
|
|
@ -17,6 +17,8 @@ const path = require('path');
|
|||
|
||||
const settings = require('../common/settings');
|
||||
|
||||
remote.getCurrentWindow().removeAllListeners('focus');
|
||||
|
||||
var MainPage = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
|
@ -24,6 +26,19 @@ var MainPage = React.createClass({
|
|||
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) {
|
||||
this.setState({
|
||||
key: key
|
||||
|
@ -46,7 +61,7 @@ var MainPage = React.createClass({
|
|||
var visibility = visible ? 'visible' : 'hidden';
|
||||
return {
|
||||
position: 'absolute',
|
||||
top: 42,
|
||||
top: (this.props.teams.length > 1) ? 42 : 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
|
@ -55,19 +70,16 @@ var MainPage = React.createClass({
|
|||
},
|
||||
render: function() {
|
||||
var thisObj = this;
|
||||
var tabs = this.props.teams.map(function(team, index) {
|
||||
var badge;
|
||||
if (thisObj.state.unreadCounts[index] != 0) {
|
||||
badge = (<Badge>
|
||||
{ thisObj.state.unreadCounts[index] }
|
||||
</Badge>);
|
||||
}
|
||||
return (<NavItem className="teamTabItem" id={ 'teamTabItem' + index } eventKey={ index }>
|
||||
{ team.name }
|
||||
{ ' ' }
|
||||
{ badge }
|
||||
</NavItem>);
|
||||
});
|
||||
|
||||
var tabs_row;
|
||||
if (this.props.teams.length > 1) {
|
||||
tabs_row = (
|
||||
<Row>
|
||||
<TabBar id="tabBar" teams={ this.props.teams } unreadCounts={ this.state.unreadCounts } activeKey={ this.state.key } onSelect={ this.handleSelect }></TabBar>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
||||
var views = this.props.teams.map(function(team, index) {
|
||||
var handleUnreadCountChange = function(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 }
|
||||
/>)
|
||||
});
|
||||
var views_row = (<Row>
|
||||
{ views }
|
||||
</Row>);
|
||||
return (
|
||||
<Grid fluid>
|
||||
<Row>
|
||||
<Nav bsStyle="tabs" activeKey={ this.state.key } onSelect={ this.handleSelect }>
|
||||
{ tabs }
|
||||
</Nav>
|
||||
</Row>
|
||||
<Row>
|
||||
{ views }
|
||||
</Row>
|
||||
{ tabs_row }
|
||||
{ views_row }
|
||||
</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({
|
||||
getInitialState: function() {
|
||||
|
@ -178,6 +210,9 @@ try {
|
|||
} catch (e) {
|
||||
window.location = 'settings.html';
|
||||
}
|
||||
if (config.teams.length === 0) {
|
||||
window.location = 'settings.html';
|
||||
}
|
||||
|
||||
var contextMenu = require('./menus/context');
|
||||
var menu = contextMenu.createDefault();
|
||||
|
|
|
@ -6,6 +6,7 @@ const settings = require('../common/settings');
|
|||
const Grid = ReactBootstrap.Grid;
|
||||
const Row = ReactBootstrap.Row;
|
||||
const Col = ReactBootstrap.Col;
|
||||
const Input = ReactBootstrap.Input;
|
||||
const Button = ReactBootstrap.Button;
|
||||
const ListGroup = ReactBootstrap.ListGroup;
|
||||
const ListGroupItem = ReactBootstrap.ListGroupItem;
|
||||
|
@ -13,16 +14,17 @@ const Glyphicon = ReactBootstrap.Glyphicon;
|
|||
|
||||
var SettingsPage = React.createClass({
|
||||
getInitialState: function() {
|
||||
var config;
|
||||
try {
|
||||
config = settings.readFileSync(this.props.configFile);
|
||||
} catch (e) {
|
||||
config = settings.loadDefault();
|
||||
}
|
||||
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) {
|
||||
this.setState({
|
||||
teams: teams
|
||||
|
@ -31,28 +33,57 @@ var SettingsPage = React.createClass({
|
|||
handleSave: function() {
|
||||
var config = {
|
||||
teams: this.state.teams,
|
||||
version: 1
|
||||
hideMenuBar: this.state.hideMenuBar,
|
||||
version: settings.version
|
||||
};
|
||||
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';
|
||||
},
|
||||
handleCancel: function() {
|
||||
window.location = './index.html';
|
||||
},
|
||||
handleChangeHideMenuBar: function() {
|
||||
this.setState({
|
||||
hideMenuBar: this.refs.hideMenuBar.getChecked()
|
||||
});
|
||||
},
|
||||
render: function() {
|
||||
var teams_row = (
|
||||
<Row>
|
||||
<Col md={ 12 }>
|
||||
<h2>Teams</h2>
|
||||
<TeamList teams={ this.state.teams } onTeamsChange={ this.handleTeamsChange } />
|
||||
</Col>
|
||||
</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">
|
||||
<Row>
|
||||
<Col md={ 12 }>
|
||||
<h2>Teams</h2>
|
||||
<TeamList teams={ this.state.teams } onTeamsChange={ this.handleTeamsChange } />
|
||||
</Col>
|
||||
</Row>
|
||||
{ teams_row }
|
||||
{ options_row }
|
||||
<Row>
|
||||
<Col md={ 12 }>
|
||||
<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>
|
||||
</Row>
|
||||
</Grid>
|
||||
|
|
|
@ -9,6 +9,7 @@ var upgradeV0toV1 = function(config_v0) {
|
|||
name: 'Primary team',
|
||||
url: config_v0.url
|
||||
}],
|
||||
hideMenuBar: false,
|
||||
version: 1
|
||||
};
|
||||
};
|
||||
|
@ -38,5 +39,13 @@ module.exports = {
|
|||
}
|
||||
var data = JSON.stringify(config, null, ' ');
|
||||
fs.writeFileSync(configFile, data, 'utf8');
|
||||
},
|
||||
|
||||
loadDefault: function() {
|
||||
return {
|
||||
teams: [],
|
||||
hideMenuBar: false,
|
||||
version: version
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
25
src/main.js
25
src/main.js
|
@ -28,9 +28,10 @@ else {
|
|||
global['config-file'] = app.getPath('userData') + '/config.json'
|
||||
}
|
||||
|
||||
var config = {};
|
||||
try {
|
||||
var configFile = global['config-file'];
|
||||
var config = settings.readFileSync(configFile);
|
||||
config = settings.readFileSync(configFile);
|
||||
if (config.version != settings.version) {
|
||||
config = settings.upgrade(config);
|
||||
settings.writeFileSync(configFile, config);
|
||||
|
@ -61,8 +62,10 @@ app.on('window-all-closed', function() {
|
|||
// For win32, auto-hide menu bar.
|
||||
app.on('browser-window-created', function(event, window) {
|
||||
if (process.platform === 'win32') {
|
||||
window.setAutoHideMenuBar(true);
|
||||
window.setMenuBarVisibility(false);
|
||||
if (config.hideMenuBar) {
|
||||
window.setAutoHideMenuBar(true);
|
||||
window.setMenuBarVisibility(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -111,6 +114,9 @@ app.on('ready', function() {
|
|||
}
|
||||
window_options.icon = __dirname + '/resources/appicon.png';
|
||||
mainWindow = new BrowserWindow(window_options);
|
||||
if (window_options.maximized) {
|
||||
mainWindow.maximize();
|
||||
}
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadURL('file://' + __dirname + '/browser/index.html');
|
||||
|
@ -118,10 +124,16 @@ app.on('ready', function() {
|
|||
// Open the DevTools.
|
||||
// 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) {
|
||||
if (willAppQuit) { // when [Ctrl|Cmd]+Q
|
||||
var bounds = mainWindow.getBounds();
|
||||
fs.writeFileSync(bounds_info_path, JSON.stringify(bounds));
|
||||
saveWindowState(bounds_info_path, mainWindow);
|
||||
}
|
||||
else { // Minimize or hide the window for close button.
|
||||
event.preventDefault();
|
||||
|
@ -144,8 +156,7 @@ app.on('ready', function() {
|
|||
// 'blur' event was effective in order to avoid this.
|
||||
// Ideally, app should detect that OS is shutting down.
|
||||
mainWindow.on('blur', function() {
|
||||
var bounds = mainWindow.getBounds();
|
||||
fs.writeFileSync(bounds_info_path, JSON.stringify(bounds));
|
||||
saveWindowState(bounds_info_path, mainWindow);
|
||||
});
|
||||
|
||||
var app_menu = appMenu.createMenu(mainWindow);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "electron-mattermost",
|
||||
"version": "1.0.1",
|
||||
"description": "Desktop app for Mattermost that run on Electron",
|
||||
"version": "1.0.2",
|
||||
"description": "Electron-based desktop application for Mattermost",
|
||||
"main": "main.js",
|
||||
"author": "Yuya Ochiai",
|
||||
"license": "MIT",
|
||||
|
|
|
@ -106,10 +106,22 @@ describe('electron-mattermost', function() {
|
|||
}]
|
||||
};
|
||||
|
||||
before(function() {
|
||||
beforeEach(function() {
|
||||
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() {
|
||||
return client
|
||||
.init()
|
||||
|
|
Loading…
Reference in a new issue