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:
@@ -17,6 +17,8 @@ Release date: TBD
|
|||||||
[#500](https://github.com/mattermost/desktop/issues/500)
|
[#500](https://github.com/mattermost/desktop/issues/500)
|
||||||
- Added the dialog to reopen the application when it quits unexpectedly.
|
- Added the dialog to reopen the application when it quits unexpectedly.
|
||||||
[#626](https://github.com/mattermost/desktop/pull/626)
|
[#626](https://github.com/mattermost/desktop/pull/626)
|
||||||
|
- Show animation icon when loading a page.
|
||||||
|
[#490](https://github.com/mattermost/desktop/issues/490)
|
||||||
|
|
||||||
#### Windows
|
#### Windows
|
||||||
- Added the feature to open the application via `mattermost://` link.
|
- Added the feature to open the application via `mattermost://` link.
|
||||||
|
BIN
src/assets/loading.gif
Normal file
BIN
src/assets/loading.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
@@ -3,6 +3,7 @@
|
|||||||
const React = require('react');
|
const React = require('react');
|
||||||
const PropTypes = require('prop-types');
|
const PropTypes = require('prop-types');
|
||||||
const {Grid, Row, Col} = require('react-bootstrap');
|
const {Grid, Row, Col} = require('react-bootstrap');
|
||||||
|
const {shell} = require('electron');
|
||||||
|
|
||||||
function ErrorView(props) {
|
function ErrorView(props) {
|
||||||
const classNames = ['container', 'ErrorView'];
|
const classNames = ['container', 'ErrorView'];
|
||||||
@@ -12,6 +13,10 @@ function ErrorView(props) {
|
|||||||
if (props.withTab) {
|
if (props.withTab) {
|
||||||
classNames.push('ErrorView-with-tab');
|
classNames.push('ErrorView-with-tab');
|
||||||
}
|
}
|
||||||
|
function handleClick(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
shell.openExternal(props.errorInfo.validatedURL);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<Grid
|
<Grid
|
||||||
id={props.id}
|
id={props.id}
|
||||||
@@ -39,11 +44,17 @@ function ErrorView(props) {
|
|||||||
<ul className='ErrorView-bullets' >
|
<ul className='ErrorView-bullets' >
|
||||||
<li>{'Your computer is connected to the internet.'}</li>
|
<li>{'Your computer is connected to the internet.'}</li>
|
||||||
<li>{'The Mattermost URL '}
|
<li>{'The Mattermost URL '}
|
||||||
<a href={props.errorInfo.validatedURL}>
|
<a
|
||||||
|
onClick={handleClick}
|
||||||
|
href={props.errorInfo.validatedURL}
|
||||||
|
>
|
||||||
{props.errorInfo.validatedURL}
|
{props.errorInfo.validatedURL}
|
||||||
</a>{' is correct.'}</li>
|
</a>{' is correct.'}</li>
|
||||||
<li>{'You can reach '}
|
<li>{'You can reach '}
|
||||||
<a href={props.errorInfo.validatedURL}>
|
<a
|
||||||
|
onClick={handleClick}
|
||||||
|
href={props.errorInfo.validatedURL}
|
||||||
|
>
|
||||||
{props.errorInfo.validatedURL}
|
{props.errorInfo.validatedURL}
|
||||||
</a>{' from a browser window.'}</li>
|
</a>{' from a browser window.'}</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@@ -30,7 +30,8 @@ const MattermostView = createReactClass({
|
|||||||
return {
|
return {
|
||||||
errorInfo: null,
|
errorInfo: null,
|
||||||
isContextMenuAdded: false,
|
isContextMenuAdded: false,
|
||||||
reloadTimeoutID: null
|
reloadTimeoutID: null,
|
||||||
|
isLoaded: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -51,7 +52,8 @@ const MattermostView = createReactClass({
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.setState({
|
self.setState({
|
||||||
errorInfo: e
|
errorInfo: e,
|
||||||
|
isLoaded: true
|
||||||
});
|
});
|
||||||
function reload() {
|
function reload() {
|
||||||
window.removeEventListener('online', reload);
|
window.removeEventListener('online', reload);
|
||||||
@@ -115,6 +117,11 @@ const MattermostView = createReactClass({
|
|||||||
|
|
||||||
webview.addEventListener('ipc-message', (event) => {
|
webview.addEventListener('ipc-message', (event) => {
|
||||||
switch (event.channel) {
|
switch (event.channel) {
|
||||||
|
case 'onGuestInitialized':
|
||||||
|
self.setState({
|
||||||
|
isLoaded: true
|
||||||
|
});
|
||||||
|
break;
|
||||||
case 'onUnreadCountChange':
|
case 'onUnreadCountChange':
|
||||||
var unreadCount = event.args[0];
|
var unreadCount = event.args[0];
|
||||||
var mentionCount = event.args[1];
|
var mentionCount = event.args[1];
|
||||||
@@ -161,7 +168,8 @@ const MattermostView = createReactClass({
|
|||||||
clearTimeout(this.state.reloadTimeoutID);
|
clearTimeout(this.state.reloadTimeoutID);
|
||||||
this.setState({
|
this.setState({
|
||||||
errorInfo: null,
|
errorInfo: null,
|
||||||
reloadTimeoutID: null
|
reloadTimeoutID: null,
|
||||||
|
isLoaded: false
|
||||||
});
|
});
|
||||||
var webview = findDOMNode(this.refs.webview);
|
var webview = findDOMNode(this.refs.webview);
|
||||||
webview.reload();
|
webview.reload();
|
||||||
@@ -235,20 +243,31 @@ const MattermostView = createReactClass({
|
|||||||
if (this.props.withTab) {
|
if (this.props.withTab) {
|
||||||
classNames.push('mattermostView-with-tab');
|
classNames.push('mattermostView-with-tab');
|
||||||
}
|
}
|
||||||
if (!this.props.active) {
|
if (!this.props.active || this.state.errorInfo) {
|
||||||
classNames.push('mattermostView-hidden');
|
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 (
|
return (
|
||||||
<div>
|
<div
|
||||||
|
className={classNames.join(' ')}
|
||||||
|
>
|
||||||
{ errorView }
|
{ errorView }
|
||||||
<webview
|
<webview
|
||||||
id={this.props.id}
|
id={this.props.id}
|
||||||
className={classNames.join(' ')}
|
|
||||||
preload={preloadJS}
|
preload={preloadJS}
|
||||||
src={this.props.src}
|
src={this.props.src}
|
||||||
ref='webview'
|
ref='webview'
|
||||||
/>
|
/>
|
||||||
|
{ loadingImage }
|
||||||
</div>);
|
</div>);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@@ -1,4 +1,12 @@
|
|||||||
.mattermostView {
|
.mattermostView {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mattermostView .ErrorView {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mattermostView webview, .mattermostView .mattermostView-loadingScreen {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0px;
|
top: 0px;
|
||||||
right: 0px;
|
right: 0px;
|
||||||
@@ -6,12 +14,27 @@
|
|||||||
left: 0px;
|
left: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mattermostView-with-tab {
|
.mattermostView-with-tab webview, .mattermostView-with-tab .mattermostView-loadingScreen {
|
||||||
top: 32px;
|
top: 31px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mattermostView-hidden {
|
.mattermostView-hidden webview {
|
||||||
flex: 0 1;
|
flex: 0 1;
|
||||||
width: 0px;
|
width: 0px;
|
||||||
height: 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;
|
||||||
|
}
|
||||||
|
@@ -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
|
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) {
|
function hasClass(element, className) {
|
||||||
var rclass = /[\t\r\n\f]/g;
|
var rclass = /[\t\r\n\f]/g;
|
||||||
if ((' ' + element.className + ' ').replace(rclass, ' ').indexOf(className) > -1) {
|
if ((' ' + element.className + ' ').replace(rclass, ' ').indexOf(className) > -1) {
|
||||||
|
@@ -77,8 +77,7 @@ describe('browser/index.html', function desc() {
|
|||||||
isVisible('#mattermostView0').then((visible) => visible.should.be.true).
|
isVisible('#mattermostView0').then((visible) => visible.should.be.true).
|
||||||
isVisible('#mattermostView1').then((visible) => visible.should.be.false).
|
isVisible('#mattermostView1').then((visible) => visible.should.be.false).
|
||||||
click('#teamTabItem1').
|
click('#teamTabItem1').
|
||||||
pause(1000).
|
waitForVisible('#mattermostView1', 2000).
|
||||||
isVisible('#mattermostView1').then((visible) => visible.should.be.true).
|
|
||||||
isVisible('#mattermostView0').then((visible) => visible.should.be.false);
|
isVisible('#mattermostView0').then((visible) => visible.should.be.false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user