Show a refresh banner on plugin update (#1720)

* Reload plugin on plugin update

* Add a banner that shows when updating the plugin and asks the user to reload

* Fix linter
This commit is contained in:
Miguel de la Cruz 2021-11-10 16:33:56 +01:00 committed by GitHub
parent 1160477849
commit 3ede6df028
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 68 additions and 0 deletions

View file

@ -182,6 +182,7 @@ export default class Plugin {
// register websocket handlers // register websocket handlers
this.registry?.registerWebSocketEventHandler(`custom_${manifest.id}_${ACTION_UPDATE_BLOCK}`, (e: any) => wsClient.updateBlockHandler(e.data)) this.registry?.registerWebSocketEventHandler(`custom_${manifest.id}_${ACTION_UPDATE_BLOCK}`, (e: any) => wsClient.updateBlockHandler(e.data))
this.registry?.registerWebSocketEventHandler(`custom_${manifest.id}_${ACTION_UPDATE_CLIENT_CONFIG}`, (e: any) => wsClient.updateClientConfigHandler(e.data)) this.registry?.registerWebSocketEventHandler(`custom_${manifest.id}_${ACTION_UPDATE_CLIENT_CONFIG}`, (e: any) => wsClient.updateClientConfigHandler(e.data))
this.registry?.registerWebSocketEventHandler(`plugin_statuses_changed`, (e: any) => wsClient.pluginStatusesChangedHandler(e.data))
this.registry?.registerWebSocketEventHandler('preferences_changed', (e: any) => { this.registry?.registerWebSocketEventHandler('preferences_changed', (e: any) => {
let preferences let preferences
try { try {

View file

@ -8,6 +8,7 @@
"BoardComponent.no-property-title": "Items with an empty {property} property will go here. This column cannot be removed.", "BoardComponent.no-property-title": "Items with an empty {property} property will go here. This column cannot be removed.",
"BoardComponent.show": "Show", "BoardComponent.show": "Show",
"BoardPage.syncFailed": "Board may be deleted or access revoked.", "BoardPage.syncFailed": "Board may be deleted or access revoked.",
"BoardPage.newVersion": "A new version of Boards is available, click here to reload.",
"Calculations.Options.average.displayName": "Average", "Calculations.Options.average.displayName": "Average",
"Calculations.Options.average.label": "Average", "Calculations.Options.average.label": "Average",
"Calculations.Options.count.displayName": "Count", "Calculations.Options.count.displayName": "Count",

View file

@ -19,6 +19,7 @@ import TelemetryClient from './telemetry/telemetryClient'
import {IAppWindow} from './types' import {IAppWindow} from './types'
import {getMessages} from './i18n' import {getMessages} from './i18n'
import {FlashMessages} from './components/flashMessages' import {FlashMessages} from './components/flashMessages'
import NewVersionBanner from './components/newVersionBanner'
import BoardPage from './pages/boardPage' import BoardPage from './pages/boardPage'
import ChangePasswordPage from './pages/changePasswordPage' import ChangePasswordPage from './pages/changePasswordPage'
import DashboardPage from './pages/dashboard/dashboardPage' import DashboardPage from './pages/dashboard/dashboardPage'
@ -141,6 +142,7 @@ const App = React.memo((): JSX.Element => {
> >
<div id='frame'> <div id='frame'>
<div id='main'> <div id='main'>
<NewVersionBanner/>
<Switch> <Switch>
{globalErrorRedirect} {globalErrorRedirect}
<Route path='/error'> <Route path='/error'>

View file

@ -0,0 +1,5 @@
.NewVersionBanner {
background-color: var(--prop-blue);
text-align: center;
padding: 10px;
}

View file

@ -0,0 +1,41 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {useState, useEffect} from 'react'
import {FormattedMessage} from 'react-intl'
import wsClient from '../wsclient'
import './newVersionBanner.scss'
const NewVersionBanner = () => {
const [appVersionChanged, setAppVersionChanged] = useState(false)
useEffect(() => {
wsClient.onAppVersionChangeHandler = setAppVersionChanged
}, [])
if (!appVersionChanged) {
return null
}
const newVersionReload = (e: any) => {
e.preventDefault()
location.reload()
}
return (
<div className='NewVersionBanner'>
<a
target='_blank'
rel='noreferrer'
onClick={newVersionReload}
>
<FormattedMessage
id='BoardPage.newVersion'
defaultMessage='A new version of Boards is available, click here to reload.'
/>
</a>
</div>
)
}
export default NewVersionBanner

View file

@ -49,6 +49,8 @@ type OnConfigChangeHandler = (client: WSClient, clientConfig: ClientConfig) => v
class WSClient { class WSClient {
ws: WebSocket|null = null ws: WebSocket|null = null
client: MMWebSocketClient|null = null client: MMWebSocketClient|null = null
pluginId = ''
onAppVersionChangeHandler: ((versionHasChanged: boolean) => void) | null = null
clientPrefix = '' clientPrefix = ''
serverUrl: string | undefined serverUrl: string | undefined
state: 'init'|'open'|'close' = 'init' state: 'init'|'open'|'close' = 'init'
@ -88,6 +90,7 @@ class WSClient {
} }
initPlugin(pluginId: string, client: MMWebSocketClient): void { initPlugin(pluginId: string, client: MMWebSocketClient): void {
this.pluginId = pluginId
this.clientPrefix = `custom_${pluginId}_` this.clientPrefix = `custom_${pluginId}_`
this.client = client this.client = client
Utils.log(`WSClient initialised for plugin id "${pluginId}"`) Utils.log(`WSClient initialised for plugin id "${pluginId}"`)
@ -299,6 +302,21 @@ class WSClient {
} }
} }
setOnAppVersionChangeHandler(fn: (versionHasChanged: boolean) => void): void {
this.onAppVersionChangeHandler = fn
}
pluginStatusesChangedHandler(data: any): void {
if (this.pluginId === '' || !this.onAppVersionChangeHandler) {
return
}
if (data.plugin_statuses.some((s: any) => s.plugin_id === this.pluginId)) {
Utils.log('Boards plugin has been updated')
this.onAppVersionChangeHandler(true)
}
}
authenticate(workspaceId: string, token: string): void { authenticate(workspaceId: string, token: string): void {
if (!this.hasConn()) { if (!this.hasConn()) {
Utils.assertFailure('WSClient.addBlocks: ws is not open') Utils.assertFailure('WSClient.addBlocks: ws is not open')