diff --git a/CHANGELOG.md b/CHANGELOG.md index 339e1289..24662de2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. diff --git a/src/assets/loading.gif b/src/assets/loading.gif new file mode 100644 index 00000000..ca82f75a Binary files /dev/null and b/src/assets/loading.gif differ diff --git a/src/browser/components/ErrorView.jsx b/src/browser/components/ErrorView.jsx index 631f7784..ea7066d8 100644 --- a/src/browser/components/ErrorView.jsx +++ b/src/browser/components/ErrorView.jsx @@ -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 (
  • {'Your computer is connected to the internet.'}
  • {'The Mattermost URL '} - + {props.errorInfo.validatedURL} {' is correct.'}
  • {'You can reach '} - + {props.errorInfo.validatedURL} {' from a browser window.'}
  • diff --git a/src/browser/components/MattermostView.jsx b/src/browser/components/MattermostView.jsx index 3bb97a47..64b24cc1 100644 --- a/src/browser/components/MattermostView.jsx +++ b/src/browser/components/MattermostView.jsx @@ -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 ? ( +
    + +
    + ) : null; + return ( -
    +
    { errorView } + { loadingImage }
    ); } }); diff --git a/src/browser/css/components/MattermostView.css b/src/browser/css/components/MattermostView.css index 8530511c..eef892ed 100644 --- a/src/browser/css/components/MattermostView.css +++ b/src/browser/css/components/MattermostView.css @@ -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; +} diff --git a/src/browser/webview/mattermost.js b/src/browser/webview/mattermost.js index 9767ccdf..f229411a 100644 --- a/src/browser/webview/mattermost.js +++ b/src/browser/webview/mattermost.js @@ -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) { diff --git a/test/specs/browser/index_test.js b/test/specs/browser/index_test.js index 1a3f83e3..151e1a94 100644 --- a/test/specs/browser/index_test.js +++ b/test/specs/browser/index_test.js @@ -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', () => {