Merge pull request #638 from yuya-oc/use-loading-icon

Use a Mattermost animation icon when loading a page in the app
This commit is contained in:
Yuya Ochiai
2017-11-07 20:51:35 +09:00
committed by GitHub
7 changed files with 100 additions and 14 deletions

View File

@@ -17,6 +17,8 @@ Release date: TBD
[#500](https://github.com/mattermost/desktop/issues/500)
- Added the dialog to reopen the application when it quits unexpectedly.
[#626](https://github.com/mattermost/desktop/pull/626)
- Show animation icon when loading a page.
[#490](https://github.com/mattermost/desktop/issues/490)
#### Windows
- Added the feature to open the application via `mattermost://` link.

BIN
src/assets/loading.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -3,6 +3,7 @@
const React = require('react');
const PropTypes = require('prop-types');
const {Grid, Row, Col} = require('react-bootstrap');
const {shell} = require('electron');
function ErrorView(props) {
const classNames = ['container', 'ErrorView'];
@@ -12,6 +13,10 @@ function ErrorView(props) {
if (props.withTab) {
classNames.push('ErrorView-with-tab');
}
function handleClick(event) {
event.preventDefault();
shell.openExternal(props.errorInfo.validatedURL);
}
return (
<Grid
id={props.id}
@@ -39,11 +44,17 @@ function ErrorView(props) {
<ul className='ErrorView-bullets' >
<li>{'Your computer is connected to the internet.'}</li>
<li>{'The Mattermost URL '}
<a href={props.errorInfo.validatedURL}>
<a
onClick={handleClick}
href={props.errorInfo.validatedURL}
>
{props.errorInfo.validatedURL}
</a>{' is correct.'}</li>
<li>{'You can reach '}
<a href={props.errorInfo.validatedURL}>
<a
onClick={handleClick}
href={props.errorInfo.validatedURL}
>
{props.errorInfo.validatedURL}
</a>{' from a browser window.'}</li>
</ul>

View File

@@ -30,7 +30,8 @@ const MattermostView = createReactClass({
return {
errorInfo: null,
isContextMenuAdded: false,
reloadTimeoutID: null
reloadTimeoutID: null,
isLoaded: false
};
},
@@ -51,7 +52,8 @@ const MattermostView = createReactClass({
}
self.setState({
errorInfo: e
errorInfo: e,
isLoaded: true
});
function reload() {
window.removeEventListener('online', reload);
@@ -115,6 +117,11 @@ const MattermostView = createReactClass({
webview.addEventListener('ipc-message', (event) => {
switch (event.channel) {
case 'onGuestInitialized':
self.setState({
isLoaded: true
});
break;
case 'onUnreadCountChange':
var unreadCount = event.args[0];
var mentionCount = event.args[1];
@@ -161,7 +168,8 @@ const MattermostView = createReactClass({
clearTimeout(this.state.reloadTimeoutID);
this.setState({
errorInfo: null,
reloadTimeoutID: null
reloadTimeoutID: null,
isLoaded: false
});
var webview = findDOMNode(this.refs.webview);
webview.reload();
@@ -235,20 +243,31 @@ const MattermostView = createReactClass({
if (this.props.withTab) {
classNames.push('mattermostView-with-tab');
}
if (!this.props.active) {
if (!this.props.active || this.state.errorInfo) {
classNames.push('mattermostView-hidden');
}
const loadingImage = !this.state.errorInfo && this.props.active && !this.state.isLoaded ? (
<div className='mattermostView-loadingScreen'>
<img
className='mattermostView-loadingImage'
src='../assets/loading.gif'
/>
</div>
) : null;
return (
<div>
<div
className={classNames.join(' ')}
>
{ errorView }
<webview
id={this.props.id}
className={classNames.join(' ')}
preload={preloadJS}
src={this.props.src}
ref='webview'
/>
{ loadingImage }
</div>);
}
});

View File

@@ -1,4 +1,12 @@
.mattermostView {
text-align: center;
}
.mattermostView .ErrorView {
text-align: left;
}
.mattermostView webview, .mattermostView .mattermostView-loadingScreen {
position: absolute;
top: 0px;
right: 0px;
@@ -6,12 +14,27 @@
left: 0px;
}
.mattermostView-with-tab {
top: 32px;
.mattermostView-with-tab webview, .mattermostView-with-tab .mattermostView-loadingScreen {
top: 31px;
}
.mattermostView-hidden {
.mattermostView-hidden webview {
flex: 0 1;
width: 0px;
height: 0px;
}
.mattermostView-loadingScreen {
vertical-align: middle;
background: white;
}
.mattermostView-loadingImage {
opacity: 0.5;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
}

View File

@@ -11,6 +11,38 @@ Notification = EnhancedNotification; // eslint-disable-line no-global-assign, no
Reflect.deleteProperty(global.Buffer); // http://electron.atom.io/docs/tutorial/security/#buffer-global
function isReactAppInitialized() {
const reactRoot = document.querySelector('div[data-reactroot]');
if (reactRoot === null) {
return false;
}
return reactRoot.children.length !== 0;
}
function watchReactAppUntilInitialized(callback) {
let count = 0;
const interval = 500;
const timeout = 30000;
const timer = setInterval(() => {
count += interval;
if (isReactAppInitialized() || count >= timeout) { // assumed as webapp has been initialized.
clearTimeout(timer);
callback();
}
}, interval);
}
window.addEventListener('load', () => {
if (document.getElementById('root') === null) {
console.log('The guest is not assumed as mattermost-webapp');
ipc.sendToHost('onGuestInitialized');
return;
}
watchReactAppUntilInitialized(() => {
ipc.sendToHost('onGuestInitialized');
});
});
function hasClass(element, className) {
var rclass = /[\t\r\n\f]/g;
if ((' ' + element.className + ' ').replace(rclass, ' ').indexOf(className) > -1) {

View File

@@ -77,9 +77,8 @@ describe('browser/index.html', function desc() {
isVisible('#mattermostView0').then((visible) => visible.should.be.true).
isVisible('#mattermostView1').then((visible) => visible.should.be.false).
click('#teamTabItem1').
pause(1000).
isVisible('#mattermostView1').then((visible) => visible.should.be.true).
isVisible('#mattermostView0').then((visible) => visible.should.be.false);
waitForVisible('#mattermostView1', 2000).
isVisible('#mattermostView0').then((visible) => visible.should.be.false);
});
it('should show error when using incorrect URL', () => {