[MM-63224] Add incompatible server screen (#3348)
* [MM-63224] Add incompatible server screen * Fixed issue where init isn't called on no server case * Amend check
This commit is contained in:
@@ -169,9 +169,13 @@
|
|||||||
"renderer.components.developerModeIndicator.tooltip": "Developer mode is enabled. You should only have this enabled if a Mattermost developer has instructed you to.",
|
"renderer.components.developerModeIndicator.tooltip": "Developer mode is enabled. You should only have this enabled if a Mattermost developer has instructed you to.",
|
||||||
"renderer.components.errorView.cannotConnectToThisServer": "Couldn't connect to this server",
|
"renderer.components.errorView.cannotConnectToThisServer": "Couldn't connect to this server",
|
||||||
"renderer.components.errorView.contactAdmin": "If the issue persists, please contact your admin",
|
"renderer.components.errorView.contactAdmin": "If the issue persists, please contact your admin",
|
||||||
|
"renderer.components.errorView.contactAdminUpgrade": "If the issue persists, contact your {appName} Administrator or IT department to upgrade this {appName} Server.",
|
||||||
"renderer.components.errorView.havingTroubleConnecting": "We're having trouble connecting to this {appName} server. We'll keep trying to establish a connection.",
|
"renderer.components.errorView.havingTroubleConnecting": "We're having trouble connecting to this {appName} server. We'll keep trying to establish a connection.",
|
||||||
|
"renderer.components.errorView.incompatibleServerVersion": "Incompatible server version",
|
||||||
"renderer.components.errorView.refreshThenVerify": "If refreshing this page (Ctrl+R or Command+R) doesn't help, please check the following:",
|
"renderer.components.errorView.refreshThenVerify": "If refreshing this page (Ctrl+R or Command+R) doesn't help, please check the following:",
|
||||||
|
"renderer.components.errorView.serverVersionIsIncompatible": "The {appName} Server you are accessing is incompatible with this version of the {appName} Desktop App. To connect to this server, please try the following:",
|
||||||
"renderer.components.errorView.troubleshooting.computerIsConnected": "Ensure your computer is connected to the internet.",
|
"renderer.components.errorView.troubleshooting.computerIsConnected": "Ensure your computer is connected to the internet.",
|
||||||
|
"renderer.components.errorView.troubleshooting.downgradeApp": "<link>Downgrade your {appName} Desktop App</link> to version v5.10 or earlier.",
|
||||||
"renderer.components.errorView.troubleshooting.urlIsCorrect.appNameIsCorrect": "Verify that the URL <link>{url}</link> is correct.",
|
"renderer.components.errorView.troubleshooting.urlIsCorrect.appNameIsCorrect": "Verify that the URL <link>{url}</link> is correct.",
|
||||||
"renderer.components.errorView.troubleshooting.webContentsView.canReachFromBrowserWindow": "Try opening <link>{url}</link> in a browser window.",
|
"renderer.components.errorView.troubleshooting.webContentsView.canReachFromBrowserWindow": "Try opening <link>{url}</link> in a browser window.",
|
||||||
"renderer.components.input.required": "This field is required",
|
"renderer.components.input.required": "This field is required",
|
||||||
|
@@ -59,6 +59,11 @@ export class ServerViewState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
init = () => {
|
init = () => {
|
||||||
|
// Don't need to init twice
|
||||||
|
if (this.currentServerId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const orderedServers = ServerManager.getOrderedServers();
|
const orderedServers = ServerManager.getOrderedServers();
|
||||||
if (orderedServers.length) {
|
if (orderedServers.length) {
|
||||||
if (Config.lastActiveServer && orderedServers[Config.lastActiveServer]) {
|
if (Config.lastActiveServer && orderedServers[Config.lastActiveServer]) {
|
||||||
|
@@ -30,6 +30,7 @@ export const APP_MENU_WILL_CLOSE = 'app-menu-will-close';
|
|||||||
export const LOAD_RETRY = 'load_retry';
|
export const LOAD_RETRY = 'load_retry';
|
||||||
export const LOAD_SUCCESS = 'load_success';
|
export const LOAD_SUCCESS = 'load_success';
|
||||||
export const LOAD_FAILED = 'load_fail';
|
export const LOAD_FAILED = 'load_fail';
|
||||||
|
export const LOAD_INCOMPATIBLE_SERVER = 'load_incompatible_server';
|
||||||
|
|
||||||
export const MAXIMIZE_CHANGE = 'maximized_change';
|
export const MAXIMIZE_CHANGE = 'maximized_change';
|
||||||
|
|
||||||
@@ -102,6 +103,7 @@ export const UPDATE_PATHS = 'update-paths';
|
|||||||
export const UPDATE_URL_VIEW_WIDTH = 'update-url-view-width';
|
export const UPDATE_URL_VIEW_WIDTH = 'update-url-view-width';
|
||||||
|
|
||||||
export const OPEN_SERVER_EXTERNALLY = 'open-server-externally';
|
export const OPEN_SERVER_EXTERNALLY = 'open-server-externally';
|
||||||
|
export const OPEN_SERVER_UPGRADE_LINK = 'open-server-upgrade-link';
|
||||||
|
|
||||||
export const PING_DOMAIN = 'ping-domain';
|
export const PING_DOMAIN = 'ping-domain';
|
||||||
|
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
import type {BuildConfig} from 'types/config';
|
import type {BuildConfig} from 'types/config';
|
||||||
|
|
||||||
import {DEFAULT_ACADEMY_LINK, DEFAULT_HELP_LINK} from '../../common/constants';
|
import {DEFAULT_ACADEMY_LINK, DEFAULT_HELP_LINK, DEFAULT_UPGRADE_LINK} from '../../common/constants';
|
||||||
|
|
||||||
// For detailed guides, please refer to https://docs.mattermost.com/deployment/desktop-app-deployment.html
|
// For detailed guides, please refer to https://docs.mattermost.com/deployment/desktop-app-deployment.html
|
||||||
|
|
||||||
@@ -31,6 +31,7 @@ const buildConfig: BuildConfig = {
|
|||||||
*/],
|
*/],
|
||||||
helpLink: DEFAULT_HELP_LINK,
|
helpLink: DEFAULT_HELP_LINK,
|
||||||
academyLink: DEFAULT_ACADEMY_LINK,
|
academyLink: DEFAULT_ACADEMY_LINK,
|
||||||
|
upgradeLink: DEFAULT_UPGRADE_LINK,
|
||||||
enableServerManagement: true,
|
enableServerManagement: true,
|
||||||
enableAutoUpdater: true,
|
enableAutoUpdater: true,
|
||||||
managedResources: ['trusted'],
|
managedResources: ['trusted'],
|
||||||
|
@@ -219,6 +219,9 @@ export class Config extends EventEmitter {
|
|||||||
get academyLink() {
|
get academyLink() {
|
||||||
return this.combinedData?.academyLink;
|
return this.combinedData?.academyLink;
|
||||||
}
|
}
|
||||||
|
get upgradeLink() {
|
||||||
|
return this.combinedData?.upgradeLink;
|
||||||
|
}
|
||||||
get minimizeToTray() {
|
get minimizeToTray() {
|
||||||
return this.combinedData?.minimizeToTray;
|
return this.combinedData?.minimizeToTray;
|
||||||
}
|
}
|
||||||
|
@@ -48,3 +48,4 @@ export const DEFAULT_HELP_LINK = 'https://docs.mattermost.com/guides/collaborate
|
|||||||
export const DEFAULT_ACADEMY_LINK = 'https://academy.mattermost.com/';
|
export const DEFAULT_ACADEMY_LINK = 'https://academy.mattermost.com/';
|
||||||
export const DEFAULT_TE_REPORT_PROBLEM_LINK = 'https://mattermost.com/pl/report-a-bug';
|
export const DEFAULT_TE_REPORT_PROBLEM_LINK = 'https://mattermost.com/pl/report-a-bug';
|
||||||
export const DEFAULT_EE_REPORT_PROBLEM_LINK = 'https://support.mattermost.com/hc/en-us/requests/new';
|
export const DEFAULT_EE_REPORT_PROBLEM_LINK = 'https://support.mattermost.com/hc/en-us/requests/new';
|
||||||
|
export const DEFAULT_UPGRADE_LINK = 'https://forum.mattermost.com/t/mattermost-desktop-app-5-11-important-compatibility-notice/22599';
|
||||||
|
@@ -308,7 +308,6 @@ async function initializeAfterAppReady() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ServerManager.reloadFromConfig();
|
ServerManager.reloadFromConfig();
|
||||||
updateServerInfos(ServerManager.getAllServers());
|
|
||||||
ServerManager.on(SERVERS_URL_MODIFIED, (serverIds?: string[]) => {
|
ServerManager.on(SERVERS_URL_MODIFIED, (serverIds?: string[]) => {
|
||||||
if (serverIds && serverIds.length) {
|
if (serverIds && serverIds.length) {
|
||||||
updateServerInfos(serverIds.map((srvId) => ServerManager.getServer(srvId)!));
|
updateServerInfos(serverIds.map((srvId) => ServerManager.getServer(srvId)!));
|
||||||
|
@@ -93,6 +93,8 @@ import {
|
|||||||
IS_DEVELOPER_MODE_ENABLED,
|
IS_DEVELOPER_MODE_ENABLED,
|
||||||
METRICS_REQUEST,
|
METRICS_REQUEST,
|
||||||
METRICS_RECEIVE,
|
METRICS_RECEIVE,
|
||||||
|
LOAD_INCOMPATIBLE_SERVER,
|
||||||
|
OPEN_SERVER_UPGRADE_LINK,
|
||||||
} from 'common/communication';
|
} from 'common/communication';
|
||||||
|
|
||||||
console.log('Preload initialized');
|
console.log('Preload initialized');
|
||||||
@@ -120,6 +122,7 @@ contextBridge.exposeInMainWorld('desktop', {
|
|||||||
doubleClickOnWindow: (windowName) => ipcRenderer.send(DOUBLE_CLICK_ON_WINDOW, windowName),
|
doubleClickOnWindow: (windowName) => ipcRenderer.send(DOUBLE_CLICK_ON_WINDOW, windowName),
|
||||||
focusCurrentView: () => ipcRenderer.send(FOCUS_BROWSERVIEW),
|
focusCurrentView: () => ipcRenderer.send(FOCUS_BROWSERVIEW),
|
||||||
openServerExternally: () => ipcRenderer.send(OPEN_SERVER_EXTERNALLY),
|
openServerExternally: () => ipcRenderer.send(OPEN_SERVER_EXTERNALLY),
|
||||||
|
openServerUpgradeLink: () => ipcRenderer.send(OPEN_SERVER_UPGRADE_LINK),
|
||||||
closeDownloadsDropdown: () => ipcRenderer.send(CLOSE_DOWNLOADS_DROPDOWN),
|
closeDownloadsDropdown: () => ipcRenderer.send(CLOSE_DOWNLOADS_DROPDOWN),
|
||||||
closeDownloadsDropdownMenu: () => ipcRenderer.send(CLOSE_DOWNLOADS_DROPDOWN_MENU),
|
closeDownloadsDropdownMenu: () => ipcRenderer.send(CLOSE_DOWNLOADS_DROPDOWN_MENU),
|
||||||
openDownloadsDropdown: () => ipcRenderer.send(OPEN_DOWNLOADS_DROPDOWN),
|
openDownloadsDropdown: () => ipcRenderer.send(OPEN_DOWNLOADS_DROPDOWN),
|
||||||
@@ -154,6 +157,7 @@ contextBridge.exposeInMainWorld('desktop', {
|
|||||||
onLoadRetry: (listener) => ipcRenderer.on(LOAD_RETRY, (_, viewId, retry, err, loadUrl) => listener(viewId, retry, err, loadUrl)),
|
onLoadRetry: (listener) => ipcRenderer.on(LOAD_RETRY, (_, viewId, retry, err, loadUrl) => listener(viewId, retry, err, loadUrl)),
|
||||||
onLoadSuccess: (listener) => ipcRenderer.on(LOAD_SUCCESS, (_, viewId) => listener(viewId)),
|
onLoadSuccess: (listener) => ipcRenderer.on(LOAD_SUCCESS, (_, viewId) => listener(viewId)),
|
||||||
onLoadFailed: (listener) => ipcRenderer.on(LOAD_FAILED, (_, viewId, err, loadUrl) => listener(viewId, err, loadUrl)),
|
onLoadFailed: (listener) => ipcRenderer.on(LOAD_FAILED, (_, viewId, err, loadUrl) => listener(viewId, err, loadUrl)),
|
||||||
|
onLoadIncompatibleServer: (listener) => ipcRenderer.on(LOAD_INCOMPATIBLE_SERVER, (_, viewId, loadUrl) => listener(viewId, loadUrl)),
|
||||||
onSetActiveView: (listener) => ipcRenderer.on(SET_ACTIVE_VIEW, (_, serverId, viewId) => listener(serverId, viewId)),
|
onSetActiveView: (listener) => ipcRenderer.on(SET_ACTIVE_VIEW, (_, serverId, viewId) => listener(serverId, viewId)),
|
||||||
onMaximizeChange: (listener) => ipcRenderer.on(MAXIMIZE_CHANGE, (_, maximize) => listener(maximize)),
|
onMaximizeChange: (listener) => ipcRenderer.on(MAXIMIZE_CHANGE, (_, maximize) => listener(maximize)),
|
||||||
onEnterFullScreen: (listener) => ipcRenderer.on('enter-full-screen', () => listener()),
|
onEnterFullScreen: (listener) => ipcRenderer.on('enter-full-screen', () => listener()),
|
||||||
|
@@ -6,6 +6,7 @@
|
|||||||
import AppState from 'common/appState';
|
import AppState from 'common/appState';
|
||||||
import {LOAD_FAILED, UPDATE_TARGET_URL} from 'common/communication';
|
import {LOAD_FAILED, UPDATE_TARGET_URL} from 'common/communication';
|
||||||
import {MattermostServer} from 'common/servers/MattermostServer';
|
import {MattermostServer} from 'common/servers/MattermostServer';
|
||||||
|
import ServerManager from 'common/servers/serverManager';
|
||||||
import MessagingView from 'common/views/MessagingView';
|
import MessagingView from 'common/views/MessagingView';
|
||||||
|
|
||||||
import {MattermostWebContentsView} from './MattermostWebContentsView';
|
import {MattermostWebContentsView} from './MattermostWebContentsView';
|
||||||
@@ -68,6 +69,16 @@ jest.mock('main/performanceMonitor', () => ({
|
|||||||
registerServerView: jest.fn(),
|
registerServerView: jest.fn(),
|
||||||
unregisterView: jest.fn(),
|
unregisterView: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
jest.mock('common/servers/serverManager', () => ({
|
||||||
|
getRemoteInfo: jest.fn(),
|
||||||
|
getViewLog: jest.fn().mockReturnValue({
|
||||||
|
verbose: jest.fn(),
|
||||||
|
info: jest.fn(),
|
||||||
|
error: jest.fn(),
|
||||||
|
silly: jest.fn(),
|
||||||
|
}),
|
||||||
|
on: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
const server = new MattermostServer({name: 'server_name', url: 'http://server-1.com'});
|
const server = new MattermostServer({name: 'server_name', url: 'http://server-1.com'});
|
||||||
const view = new MessagingView(server, true);
|
const view = new MessagingView(server, true);
|
||||||
@@ -268,6 +279,7 @@ describe('main/views/MattermostWebContentsView', () => {
|
|||||||
mattermostView.setInitialized = jest.fn();
|
mattermostView.setInitialized = jest.fn();
|
||||||
mattermostView.updateMentionsFromTitle = jest.fn();
|
mattermostView.updateMentionsFromTitle = jest.fn();
|
||||||
mattermostView.findUnreadState = jest.fn();
|
mattermostView.findUnreadState = jest.fn();
|
||||||
|
ServerManager.getRemoteInfo.mockReturnValue({serverVersion: '10.0.0'});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
|
@@ -4,6 +4,7 @@
|
|||||||
import {WebContentsView, app, ipcMain} from 'electron';
|
import {WebContentsView, app, ipcMain} from 'electron';
|
||||||
import type {WebContentsViewConstructorOptions, Event, Input} from 'electron/main';
|
import type {WebContentsViewConstructorOptions, Event, Input} from 'electron/main';
|
||||||
import {EventEmitter} from 'events';
|
import {EventEmitter} from 'events';
|
||||||
|
import semver from 'semver';
|
||||||
|
|
||||||
import AppState from 'common/appState';
|
import AppState from 'common/appState';
|
||||||
import {
|
import {
|
||||||
@@ -16,6 +17,7 @@ import {
|
|||||||
BROWSER_HISTORY_STATUS_UPDATED,
|
BROWSER_HISTORY_STATUS_UPDATED,
|
||||||
CLOSE_SERVERS_DROPDOWN,
|
CLOSE_SERVERS_DROPDOWN,
|
||||||
CLOSE_DOWNLOADS_DROPDOWN,
|
CLOSE_DOWNLOADS_DROPDOWN,
|
||||||
|
LOAD_INCOMPATIBLE_SERVER,
|
||||||
} from 'common/communication';
|
} from 'common/communication';
|
||||||
import type {Logger} from 'common/log';
|
import type {Logger} from 'common/log';
|
||||||
import ServerManager from 'common/servers/serverManager';
|
import ServerManager from 'common/servers/serverManager';
|
||||||
@@ -426,15 +428,22 @@ export class MattermostWebContentsView extends EventEmitter {
|
|||||||
|
|
||||||
private loadSuccess = (loadURL: string) => {
|
private loadSuccess = (loadURL: string) => {
|
||||||
return () => {
|
return () => {
|
||||||
this.log.verbose(`finished loading ${loadURL}`);
|
const serverInfo = ServerManager.getRemoteInfo(this.view.server.id);
|
||||||
MainWindow.sendToRenderer(LOAD_SUCCESS, this.id);
|
if (serverInfo?.serverVersion && semver.gte(serverInfo.serverVersion, '9.4.0')) {
|
||||||
this.maxRetries = MAX_SERVER_RETRIES;
|
this.log.verbose(`finished loading ${loadURL}`);
|
||||||
this.status = Status.WAITING_MM;
|
MainWindow.sendToRenderer(LOAD_SUCCESS, this.id);
|
||||||
this.removeLoading = setTimeout(this.setInitialized, MAX_LOADING_SCREEN_SECONDS, true);
|
this.maxRetries = MAX_SERVER_RETRIES;
|
||||||
this.emit(LOAD_SUCCESS, this.id, loadURL);
|
this.status = Status.WAITING_MM;
|
||||||
const mainWindow = MainWindow.get();
|
this.removeLoading = setTimeout(this.setInitialized, MAX_LOADING_SCREEN_SECONDS, true);
|
||||||
if (mainWindow && this.currentURL) {
|
this.emit(LOAD_SUCCESS, this.id, loadURL);
|
||||||
this.setBounds(getWindowBoundaries(mainWindow));
|
const mainWindow = MainWindow.get();
|
||||||
|
if (mainWindow && this.currentURL) {
|
||||||
|
this.setBounds(getWindowBoundaries(mainWindow));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
MainWindow.sendToRenderer(LOAD_INCOMPATIBLE_SERVER, this.id, loadURL.toString());
|
||||||
|
this.emit(LOAD_FAILED, this.id, 'Incompatible server version', loadURL.toString());
|
||||||
|
this.status = Status.ERROR;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@@ -31,6 +31,7 @@ import {
|
|||||||
UNREADS_AND_MENTIONS,
|
UNREADS_AND_MENTIONS,
|
||||||
TAB_LOGIN_CHANGED,
|
TAB_LOGIN_CHANGED,
|
||||||
DEVELOPER_MODE_UPDATED,
|
DEVELOPER_MODE_UPDATED,
|
||||||
|
OPEN_SERVER_UPGRADE_LINK,
|
||||||
} from 'common/communication';
|
} from 'common/communication';
|
||||||
import Config from 'common/config';
|
import Config from 'common/config';
|
||||||
import {Logger} from 'common/log';
|
import {Logger} from 'common/log';
|
||||||
@@ -42,7 +43,7 @@ import Utils from 'common/utils/util';
|
|||||||
import type {MattermostView} from 'common/views/View';
|
import type {MattermostView} from 'common/views/View';
|
||||||
import {TAB_MESSAGING} from 'common/views/View';
|
import {TAB_MESSAGING} from 'common/views/View';
|
||||||
import {handleWelcomeScreenModal} from 'main/app/intercom';
|
import {handleWelcomeScreenModal} from 'main/app/intercom';
|
||||||
import {flushCookiesStore} from 'main/app/utils';
|
import {flushCookiesStore, updateServerInfos} from 'main/app/utils';
|
||||||
import DeveloperMode from 'main/developerMode';
|
import DeveloperMode from 'main/developerMode';
|
||||||
import performanceMonitor from 'main/performanceMonitor';
|
import performanceMonitor from 'main/performanceMonitor';
|
||||||
import PermissionsManager from 'main/permissionsManager';
|
import PermissionsManager from 'main/permissionsManager';
|
||||||
@@ -82,6 +83,7 @@ export class ViewManager {
|
|||||||
ipcMain.on(BROWSER_HISTORY_PUSH, this.handleBrowserHistoryPush);
|
ipcMain.on(BROWSER_HISTORY_PUSH, this.handleBrowserHistoryPush);
|
||||||
ipcMain.on(TAB_LOGIN_CHANGED, this.handleTabLoginChanged);
|
ipcMain.on(TAB_LOGIN_CHANGED, this.handleTabLoginChanged);
|
||||||
ipcMain.on(OPEN_SERVER_EXTERNALLY, this.handleOpenServerExternally);
|
ipcMain.on(OPEN_SERVER_EXTERNALLY, this.handleOpenServerExternally);
|
||||||
|
ipcMain.on(OPEN_SERVER_UPGRADE_LINK, this.handleOpenServerUpgradeLink);
|
||||||
ipcMain.on(UNREADS_AND_MENTIONS, this.handleUnreadsAndMentionsChanged);
|
ipcMain.on(UNREADS_AND_MENTIONS, this.handleUnreadsAndMentionsChanged);
|
||||||
ipcMain.on(SESSION_EXPIRED, this.handleSessionExpired);
|
ipcMain.on(SESSION_EXPIRED, this.handleSessionExpired);
|
||||||
|
|
||||||
@@ -91,8 +93,11 @@ export class ViewManager {
|
|||||||
DeveloperMode.on(DEVELOPER_MODE_UPDATED, this.handleDeveloperModeUpdated);
|
DeveloperMode.on(DEVELOPER_MODE_UPDATED, this.handleDeveloperModeUpdated);
|
||||||
}
|
}
|
||||||
|
|
||||||
private init = () => {
|
private init = async () => {
|
||||||
if (ServerManager.hasServers()) {
|
if (ServerManager.hasServers()) {
|
||||||
|
// TODO: This init should be happening elsewhere, future refactor will fix this
|
||||||
|
ServerViewState.init();
|
||||||
|
await updateServerInfos(ServerManager.getAllServers());
|
||||||
LoadingScreen.show();
|
LoadingScreen.show();
|
||||||
ServerManager.getAllServers().forEach((server) => this.loadServer(server));
|
ServerManager.getAllServers().forEach((server) => this.loadServer(server));
|
||||||
this.showInitial();
|
this.showInitial();
|
||||||
@@ -303,7 +308,6 @@ export class ViewManager {
|
|||||||
private showInitial = () => {
|
private showInitial = () => {
|
||||||
log.verbose('showInitial');
|
log.verbose('showInitial');
|
||||||
|
|
||||||
// TODO: This init should be happening elsewhere, future refactor will fix this
|
|
||||||
ServerViewState.init();
|
ServerViewState.init();
|
||||||
if (ServerManager.hasServers()) {
|
if (ServerManager.hasServers()) {
|
||||||
const lastActiveServer = ServerViewState.getCurrentServer();
|
const lastActiveServer = ServerViewState.getCurrentServer();
|
||||||
@@ -577,6 +581,12 @@ export class ViewManager {
|
|||||||
shell.openExternal(view.view.server.url.toString());
|
shell.openExternal(view.view.server.url.toString());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private handleOpenServerUpgradeLink = () => {
|
||||||
|
if (Config.upgradeLink) {
|
||||||
|
shell.openExternal(Config.upgradeLink);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private handleUnreadsAndMentionsChanged = (e: IpcMainEvent, isUnread: boolean, mentionCount: number) => {
|
private handleUnreadsAndMentionsChanged = (e: IpcMainEvent, isUnread: boolean, mentionCount: number) => {
|
||||||
log.silly('handleUnreadsAndMentionsChanged', {webContentsId: e.sender.id, isUnread, mentionCount});
|
log.silly('handleUnreadsAndMentionsChanged', {webContentsId: e.sender.id, isUnread, mentionCount});
|
||||||
|
|
||||||
|
90
src/renderer/components/ConnectionErrorView.tsx
Normal file
90
src/renderer/components/ConnectionErrorView.tsx
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import {FormattedMessage} from 'react-intl';
|
||||||
|
|
||||||
|
import ErrorView from './ErrorView';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
darkMode: boolean;
|
||||||
|
appName?: string;
|
||||||
|
url?: string;
|
||||||
|
errorInfo?: string;
|
||||||
|
handleLink: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function ConnectionErrorView({darkMode, appName, url, handleLink, errorInfo}: Props) {
|
||||||
|
const header = (
|
||||||
|
<FormattedMessage
|
||||||
|
id='renderer.components.errorView.cannotConnectToThisServer'
|
||||||
|
defaultMessage="Couldn't connect to this server"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
const subHeader = (
|
||||||
|
<>
|
||||||
|
<FormattedMessage
|
||||||
|
id='renderer.components.errorView.havingTroubleConnecting'
|
||||||
|
defaultMessage={'We\'re having trouble connecting to this {appName} server. We\'ll keep trying to establish a connection.'}
|
||||||
|
values={{
|
||||||
|
appName,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<br/>
|
||||||
|
<FormattedMessage
|
||||||
|
id='renderer.components.errorView.refreshThenVerify'
|
||||||
|
defaultMessage="If refreshing this page (Ctrl+R or Command+R) doesn't help, please check the following:"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
const bullets = (
|
||||||
|
<>
|
||||||
|
<li>
|
||||||
|
<FormattedMessage
|
||||||
|
id='renderer.components.errorView.troubleshooting.computerIsConnected'
|
||||||
|
defaultMessage='Ensure your computer is connected to the internet.'
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<FormattedMessage
|
||||||
|
id='renderer.components.errorView.troubleshooting.urlIsCorrect.appNameIsCorrect'
|
||||||
|
defaultMessage='Verify that the URL <link>{url}</link> is correct.'
|
||||||
|
values={{
|
||||||
|
appName,
|
||||||
|
url,
|
||||||
|
link: (msg: React.ReactNode) => (
|
||||||
|
<a
|
||||||
|
onClick={handleLink}
|
||||||
|
href='#'
|
||||||
|
>
|
||||||
|
{msg}
|
||||||
|
</a>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
const contactAdmin = (
|
||||||
|
<FormattedMessage
|
||||||
|
id='renderer.components.errorView.contactAdmin'
|
||||||
|
defaultMessage='If the issue persists, please contact your admin'
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ErrorView
|
||||||
|
darkMode={darkMode}
|
||||||
|
header={header}
|
||||||
|
subHeader={subHeader}
|
||||||
|
bullets={bullets}
|
||||||
|
contactAdmin={contactAdmin}
|
||||||
|
handleLink={handleLink}
|
||||||
|
errorInfo={errorInfo}
|
||||||
|
url={url}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
@@ -12,72 +12,47 @@ import AlertImage from './Images/alert';
|
|||||||
|
|
||||||
import 'renderer/css/components/ErrorView.scss';
|
import 'renderer/css/components/ErrorView.scss';
|
||||||
|
|
||||||
type Props = {
|
type ErrorViewProps = {
|
||||||
darkMode: boolean;
|
darkMode: boolean;
|
||||||
|
header: React.ReactNode;
|
||||||
|
subHeader: React.ReactNode;
|
||||||
|
bullets: React.ReactNode;
|
||||||
|
contactAdmin: React.ReactNode;
|
||||||
errorInfo?: string;
|
errorInfo?: string;
|
||||||
url?: string;
|
url?: string;
|
||||||
appName?: string;
|
|
||||||
handleLink: () => void;
|
handleLink: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function ErrorView(props: Props) {
|
export default function ErrorView({
|
||||||
|
darkMode,
|
||||||
|
header,
|
||||||
|
subHeader,
|
||||||
|
bullets,
|
||||||
|
contactAdmin,
|
||||||
|
errorInfo,
|
||||||
|
url,
|
||||||
|
handleLink,
|
||||||
|
}: ErrorViewProps) {
|
||||||
return (
|
return (
|
||||||
<div className={classNames('ErrorView', {darkMode: props.darkMode})}>
|
<div className={classNames('ErrorView', {darkMode})}>
|
||||||
<AlertImage/>
|
<AlertImage/>
|
||||||
<span className='ErrorView-header'>
|
<span className='ErrorView-header'>
|
||||||
<FormattedMessage
|
{header}
|
||||||
id='renderer.components.errorView.cannotConnectToThisServer'
|
|
||||||
defaultMessage="Couldn't connect to this server"
|
|
||||||
/>
|
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
<FormattedMessage
|
{subHeader}
|
||||||
id='renderer.components.errorView.havingTroubleConnecting'
|
|
||||||
defaultMessage={'We\'re having trouble connecting to this {appName} server. We\'ll keep trying to establish a connection.'}
|
|
||||||
values={{
|
|
||||||
appName: props.appName,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<br/>
|
|
||||||
<FormattedMessage
|
|
||||||
id='renderer.components.errorView.refreshThenVerify'
|
|
||||||
defaultMessage="If refreshing this page (Ctrl+R or Command+R) doesn't help, please check the following:"
|
|
||||||
/>
|
|
||||||
</span>
|
</span>
|
||||||
<ul className='ErrorView-bullets'>
|
<ul className='ErrorView-bullets'>
|
||||||
<li>
|
{bullets}
|
||||||
<FormattedMessage
|
|
||||||
id='renderer.components.errorView.troubleshooting.computerIsConnected'
|
|
||||||
defaultMessage='Ensure your computer is connected to the internet.'
|
|
||||||
/>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<FormattedMessage
|
|
||||||
id='renderer.components.errorView.troubleshooting.urlIsCorrect.appNameIsCorrect'
|
|
||||||
defaultMessage='Verify that the URL <link>{url}</link> is correct.'
|
|
||||||
values={{
|
|
||||||
appName: props.appName,
|
|
||||||
url: props.url,
|
|
||||||
link: (msg: React.ReactNode) => (
|
|
||||||
<a
|
|
||||||
onClick={props.handleLink}
|
|
||||||
href='#'
|
|
||||||
>
|
|
||||||
{msg}
|
|
||||||
</a>
|
|
||||||
),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</li>
|
|
||||||
<li>
|
<li>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='renderer.components.errorView.troubleshooting.webContentsView.canReachFromBrowserWindow'
|
id='renderer.components.errorView.troubleshooting.webContentsView.canReachFromBrowserWindow'
|
||||||
defaultMessage='Try opening <link>{url}</link> in a browser window.'
|
defaultMessage='Try opening <link>{url}</link> in a browser window.'
|
||||||
values={{
|
values={{
|
||||||
url: props.url,
|
url,
|
||||||
link: (msg: React.ReactNode) => (
|
link: (msg: React.ReactNode) => (
|
||||||
<a
|
<a
|
||||||
onClick={props.handleLink}
|
onClick={handleLink}
|
||||||
href='#'
|
href='#'
|
||||||
>
|
>
|
||||||
{msg}
|
{msg}
|
||||||
@@ -88,13 +63,10 @@ export default function ErrorView(props: Props) {
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<span>
|
<span>
|
||||||
<FormattedMessage
|
{contactAdmin}
|
||||||
id='renderer.components.errorView.contactAdmin'
|
|
||||||
defaultMessage='If the issue persists, please contact your admin'
|
|
||||||
/>
|
|
||||||
</span>
|
</span>
|
||||||
<span className='ErrorView-techInfo'>
|
<span className='ErrorView-techInfo'>
|
||||||
{props.errorInfo}
|
{errorInfo}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
80
src/renderer/components/IncompatibleErrorView.tsx
Normal file
80
src/renderer/components/IncompatibleErrorView.tsx
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import {FormattedMessage} from 'react-intl';
|
||||||
|
|
||||||
|
import ErrorView from './ErrorView';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
darkMode: boolean;
|
||||||
|
appName?: string;
|
||||||
|
url?: string;
|
||||||
|
handleLink: () => void;
|
||||||
|
handleUpgradeLink: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function IncompatibleErrorView({darkMode, appName, url, handleLink, handleUpgradeLink}: Props) {
|
||||||
|
const header = (
|
||||||
|
<FormattedMessage
|
||||||
|
id='renderer.components.errorView.incompatibleServerVersion'
|
||||||
|
defaultMessage='Incompatible server version'
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
const subHeader = (
|
||||||
|
<>
|
||||||
|
<FormattedMessage
|
||||||
|
id='renderer.components.errorView.serverVersionIsIncompatible'
|
||||||
|
defaultMessage={'The {appName} Server you are accessing is incompatible with this version of the {appName} Desktop App. To connect to this server, please try the following:'}
|
||||||
|
values={{
|
||||||
|
appName,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
const bullets = (
|
||||||
|
<>
|
||||||
|
<li>
|
||||||
|
<FormattedMessage
|
||||||
|
id='renderer.components.errorView.troubleshooting.downgradeApp'
|
||||||
|
defaultMessage='<link>Downgrade your {appName} Desktop App</link> to version v5.10 or earlier.'
|
||||||
|
values={{
|
||||||
|
appName,
|
||||||
|
link: (msg: React.ReactNode) => (
|
||||||
|
<a
|
||||||
|
href='#'
|
||||||
|
onClick={handleUpgradeLink}
|
||||||
|
>
|
||||||
|
{msg}
|
||||||
|
</a>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
const contactAdmin = (
|
||||||
|
<FormattedMessage
|
||||||
|
id='renderer.components.errorView.contactAdminUpgrade'
|
||||||
|
defaultMessage='If the issue persists, contact your {appName} Administrator or IT department to upgrade this {appName} Server.'
|
||||||
|
values={{
|
||||||
|
appName,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ErrorView
|
||||||
|
darkMode={darkMode}
|
||||||
|
header={header}
|
||||||
|
subHeader={subHeader}
|
||||||
|
bullets={bullets}
|
||||||
|
contactAdmin={contactAdmin}
|
||||||
|
handleLink={handleLink}
|
||||||
|
url={url}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
@@ -11,9 +11,10 @@ import {injectIntl} from 'react-intl';
|
|||||||
import type {UniqueView, UniqueServer} from 'types/config';
|
import type {UniqueView, UniqueServer} from 'types/config';
|
||||||
import type {DownloadedItems} from 'types/downloads';
|
import type {DownloadedItems} from 'types/downloads';
|
||||||
|
|
||||||
|
import ConnectionErrorView from './ConnectionErrorView';
|
||||||
import DeveloperModeIndicator from './DeveloperModeIndicator';
|
import DeveloperModeIndicator from './DeveloperModeIndicator';
|
||||||
import DownloadsDropdownButton from './DownloadsDropdown/DownloadsDropdownButton';
|
import DownloadsDropdownButton from './DownloadsDropdown/DownloadsDropdownButton';
|
||||||
import ErrorView from './ErrorView';
|
import IncompatibleErrorView from './IncompatibleErrorView';
|
||||||
import ServerDropdownButton from './ServerDropdownButton';
|
import ServerDropdownButton from './ServerDropdownButton';
|
||||||
import TabBar from './TabBar';
|
import TabBar from './TabBar';
|
||||||
|
|
||||||
@@ -28,6 +29,7 @@ enum Status {
|
|||||||
RETRY = -1,
|
RETRY = -1,
|
||||||
FAILED = 0,
|
FAILED = 0,
|
||||||
NOSERVERS = -2,
|
NOSERVERS = -2,
|
||||||
|
INCOMPATIBLE = -3,
|
||||||
}
|
}
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -61,7 +63,7 @@ type TabViewStatus = {
|
|||||||
status: Status;
|
status: Status;
|
||||||
extra?: {
|
extra?: {
|
||||||
url: string;
|
url: string;
|
||||||
error: string;
|
error?: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,6 +188,17 @@ class MainPage extends React.PureComponent<Props, State> {
|
|||||||
this.updateTabStatus(viewId, statusValue);
|
this.updateTabStatus(viewId, statusValue);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
window.desktop.onLoadIncompatibleServer((viewId, loadUrl) => {
|
||||||
|
console.error(`${viewId}: tried to load incompatible server`);
|
||||||
|
const statusValue = {
|
||||||
|
status: Status.INCOMPATIBLE,
|
||||||
|
extra: {
|
||||||
|
url: loadUrl,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
this.updateTabStatus(viewId, statusValue);
|
||||||
|
});
|
||||||
|
|
||||||
// can't switch tabs sequentially for some reason...
|
// can't switch tabs sequentially for some reason...
|
||||||
window.desktop.onSetActiveView(this.setActiveView);
|
window.desktop.onSetActiveView(this.setActiveView);
|
||||||
|
|
||||||
@@ -507,7 +520,7 @@ class MainPage extends React.PureComponent<Props, State> {
|
|||||||
switch (tabStatus.status) {
|
switch (tabStatus.status) {
|
||||||
case Status.FAILED:
|
case Status.FAILED:
|
||||||
component = (
|
component = (
|
||||||
<ErrorView
|
<ConnectionErrorView
|
||||||
darkMode={this.props.darkMode}
|
darkMode={this.props.darkMode}
|
||||||
errorInfo={tabStatus.extra?.error}
|
errorInfo={tabStatus.extra?.error}
|
||||||
url={tabStatus.extra ? tabStatus.extra.url : ''}
|
url={tabStatus.extra ? tabStatus.extra.url : ''}
|
||||||
@@ -515,6 +528,16 @@ class MainPage extends React.PureComponent<Props, State> {
|
|||||||
handleLink={this.openServerExternally}
|
handleLink={this.openServerExternally}
|
||||||
/>);
|
/>);
|
||||||
break;
|
break;
|
||||||
|
case Status.INCOMPATIBLE:
|
||||||
|
component = (
|
||||||
|
<IncompatibleErrorView
|
||||||
|
darkMode={this.props.darkMode}
|
||||||
|
url={tabStatus.extra ? tabStatus.extra.url : ''}
|
||||||
|
appName={this.props.appName}
|
||||||
|
handleLink={this.openServerExternally}
|
||||||
|
handleUpgradeLink={() => window.desktop.openServerUpgradeLink()}
|
||||||
|
/>);
|
||||||
|
break;
|
||||||
case Status.LOADING:
|
case Status.LOADING:
|
||||||
case Status.RETRY:
|
case Status.RETRY:
|
||||||
case Status.DONE:
|
case Status.DONE:
|
||||||
|
@@ -107,6 +107,7 @@ export type BuildConfig = {
|
|||||||
defaultServers?: Server[];
|
defaultServers?: Server[];
|
||||||
helpLink: string;
|
helpLink: string;
|
||||||
academyLink: string;
|
academyLink: string;
|
||||||
|
upgradeLink: string;
|
||||||
enableServerManagement: boolean;
|
enableServerManagement: boolean;
|
||||||
enableAutoUpdater: boolean;
|
enableAutoUpdater: boolean;
|
||||||
managedResources: string[];
|
managedResources: string[];
|
||||||
|
@@ -38,6 +38,7 @@ declare global {
|
|||||||
doubleClickOnWindow: (windowName?: string) => void;
|
doubleClickOnWindow: (windowName?: string) => void;
|
||||||
focusCurrentView: () => void;
|
focusCurrentView: () => void;
|
||||||
openServerExternally: () => void;
|
openServerExternally: () => void;
|
||||||
|
openServerUpgradeLink: () => void;
|
||||||
closeDownloadsDropdown: () => void;
|
closeDownloadsDropdown: () => void;
|
||||||
closeDownloadsDropdownMenu: () => void;
|
closeDownloadsDropdownMenu: () => void;
|
||||||
openDownloadsDropdown: () => void;
|
openDownloadsDropdown: () => void;
|
||||||
@@ -72,6 +73,7 @@ declare global {
|
|||||||
onLoadRetry: (listener: (viewId: string, retry: Date, err: string, loadUrl: string) => void) => void;
|
onLoadRetry: (listener: (viewId: string, retry: Date, err: string, loadUrl: string) => void) => void;
|
||||||
onLoadSuccess: (listener: (viewId: string) => void) => void;
|
onLoadSuccess: (listener: (viewId: string) => void) => void;
|
||||||
onLoadFailed: (listener: (viewId: string, err: string, loadUrl: string) => void) => void;
|
onLoadFailed: (listener: (viewId: string, err: string, loadUrl: string) => void) => void;
|
||||||
|
onLoadIncompatibleServer: (listener: (viewId: string, loadUrl: string) => void) => void;
|
||||||
onSetActiveView: (listener: (serverId: string, viewId: string) => void) => void;
|
onSetActiveView: (listener: (serverId: string, viewId: string) => void) => void;
|
||||||
onMaximizeChange: (listener: (maximize: boolean) => void) => void;
|
onMaximizeChange: (listener: (maximize: boolean) => void) => void;
|
||||||
onEnterFullScreen: (listener: () => void) => void;
|
onEnterFullScreen: (listener: () => void) => void;
|
||||||
|
Reference in New Issue
Block a user