mirror of
https://github.com/mattermost/focalboard.git
synced 2025-04-20 11:38:51 +02:00
* Fix #569: Show websocket connection error banner * Update fwlink
This commit is contained in:
parent
521be8f94d
commit
1cc2337948
@ -19,6 +19,7 @@ type WSMessage = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type OnChangeHandler = (blocks: IBlock[]) => void
|
type OnChangeHandler = (blocks: IBlock[]) => void
|
||||||
|
type OnStateChange = (state: 'open' | 'close') => void
|
||||||
|
|
||||||
//
|
//
|
||||||
// OctoListener calls a handler when a block or any of its children changes
|
// OctoListener calls a handler when a block or any of its children changes
|
||||||
@ -56,7 +57,7 @@ class OctoListener {
|
|||||||
return readToken
|
return readToken
|
||||||
}
|
}
|
||||||
|
|
||||||
open(workspaceId: string, blockIds: string[], onChange: OnChangeHandler, onReconnect: () => void): void {
|
open(workspaceId: string, blockIds: string[], onChange: OnChangeHandler, onReconnect: () => void, onStateChange?: OnStateChange): void {
|
||||||
if (this.ws) {
|
if (this.ws) {
|
||||||
this.close()
|
this.close()
|
||||||
}
|
}
|
||||||
@ -76,6 +77,7 @@ class OctoListener {
|
|||||||
this.authenticate(workspaceId)
|
this.authenticate(workspaceId)
|
||||||
this.addBlocks(blockIds)
|
this.addBlocks(blockIds)
|
||||||
this.isInitialized = true
|
this.isInitialized = true
|
||||||
|
onStateChange?.('open')
|
||||||
}
|
}
|
||||||
|
|
||||||
ws.onerror = (e) => {
|
ws.onerror = (e) => {
|
||||||
@ -88,6 +90,7 @@ class OctoListener {
|
|||||||
// Unexpected close, re-open
|
// Unexpected close, re-open
|
||||||
const reopenBlockIds = this.isInitialized ? this.blockIds.slice() : blockIds.slice()
|
const reopenBlockIds = this.isInitialized ? this.blockIds.slice() : blockIds.slice()
|
||||||
Utils.logError(`Unexpected close, re-opening with ${reopenBlockIds.length} blocks...`)
|
Utils.logError(`Unexpected close, re-opening with ${reopenBlockIds.length} blocks...`)
|
||||||
|
onStateChange?.('close')
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.open(workspaceId, reopenBlockIds, onChange, onReconnect)
|
this.open(workspaceId, reopenBlockIds, onChange, onReconnect)
|
||||||
onReconnect()
|
onReconnect()
|
||||||
|
@ -4,4 +4,14 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
> .banner {
|
||||||
|
background-color: rgba(230, 220, 192, 0.9);
|
||||||
|
text-align: center;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .banner.error {
|
||||||
|
background-color: rgba(230, 192, 192, 0.9);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {injectIntl, IntlShape} from 'react-intl'
|
import {FormattedMessage, injectIntl, IntlShape} from 'react-intl'
|
||||||
import {withRouter, RouteComponentProps} from 'react-router-dom'
|
import {withRouter, RouteComponentProps} from 'react-router-dom'
|
||||||
import HotKeys from 'react-hot-keys'
|
import HotKeys from 'react-hot-keys'
|
||||||
|
|
||||||
@ -30,6 +30,8 @@ type State = {
|
|||||||
workspaceTree: WorkspaceTree
|
workspaceTree: WorkspaceTree
|
||||||
boardTree?: BoardTree
|
boardTree?: BoardTree
|
||||||
syncFailed?: boolean
|
syncFailed?: boolean
|
||||||
|
websocketClosedTimeOutId?: ReturnType<typeof setTimeout>
|
||||||
|
websocketClosed?: boolean
|
||||||
workspaceUsers: WorkspaceUsersContextData
|
workspaceUsers: WorkspaceUsersContextData
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,6 +195,21 @@ class BoardPage extends React.Component<Props, State> {
|
|||||||
keyName='shift+ctrl+z,shift+cmd+z,ctrl+z,cmd+z'
|
keyName='shift+ctrl+z,shift+cmd+z,ctrl+z,cmd+z'
|
||||||
onKeyDown={this.undoRedoHandler}
|
onKeyDown={this.undoRedoHandler}
|
||||||
/>
|
/>
|
||||||
|
{(this.state.websocketClosed) &&
|
||||||
|
<div className='banner error'>
|
||||||
|
<a
|
||||||
|
href='https://www.focalboard.com/fwlink/websocket-connect-error.html'
|
||||||
|
target='_blank'
|
||||||
|
rel='noreferrer'
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='Error.websocket-closed'
|
||||||
|
defaultMessage='Websocket connection closed, connection interrupted. If this persists, check your server or web proxy configuration.'
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<Workspace
|
<Workspace
|
||||||
workspace={workspace}
|
workspace={workspace}
|
||||||
workspaceTree={workspaceTree}
|
workspaceTree={workspaceTree}
|
||||||
@ -266,6 +283,28 @@ class BoardPage extends React.Component<Props, State> {
|
|||||||
Utils.log('workspaceListener.onReconnect')
|
Utils.log('workspaceListener.onReconnect')
|
||||||
this.sync()
|
this.sync()
|
||||||
},
|
},
|
||||||
|
(state) => {
|
||||||
|
switch (state) {
|
||||||
|
case 'close': {
|
||||||
|
// Show error after a delay to ignore brief interruptions
|
||||||
|
if (!this.state.websocketClosed && !this.state.websocketClosedTimeOutId) {
|
||||||
|
const timeoutId = setTimeout(() => {
|
||||||
|
this.setState({websocketClosed: true, websocketClosedTimeOutId: undefined})
|
||||||
|
}, 5000)
|
||||||
|
this.setState({websocketClosedTimeOutId: timeoutId})
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'open': {
|
||||||
|
if (this.state.websocketClosedTimeOutId) {
|
||||||
|
clearTimeout(this.state.websocketClosedTimeOutId)
|
||||||
|
}
|
||||||
|
this.setState({websocketClosed: false, websocketClosedTimeOutId: undefined})
|
||||||
|
Utils.log('Connection established')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
if (boardId) {
|
if (boardId) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user