[MM-50485] Implement ServerManager, introduce id concept for identifying servers/views (#2654)
* Implement ServerManager, introduce id concept for identifying servers/views * REVERT ME (eventually): Move back to old logging * PR feedback * PR feedback * Revert "REVERT ME (eventually): Move back to old logging" This reverts commit 46954a5bbed4dd29e89ca9d5217bb75af2837bb3. * Merge'd * Use the correct method for Logger in ServerManager
This commit is contained in:
parent
862287edff
commit
53fb8c8fd3
80
package-lock.json
generated
80
package-lock.json
generated
|
@ -33,6 +33,7 @@
|
|||
"@types/react": "17.0.43",
|
||||
"@types/react-beautiful-dnd": "13.0.0",
|
||||
"@types/react-dom": "17.0.14",
|
||||
"@types/uuid": "^9.0.1",
|
||||
"@types/valid-url": "1.0.3",
|
||||
"@types/winreg": "1.2.31",
|
||||
"@typescript-eslint/eslint-plugin": "5.18.0",
|
||||
|
@ -102,6 +103,7 @@
|
|||
"style-loader": "3.3.1",
|
||||
"ts-prune": "0.10.3",
|
||||
"typescript": "4.6.3",
|
||||
"uuid": "9.0.0",
|
||||
"valid-url": "1.0.9",
|
||||
"webpack": "5.71.0",
|
||||
"webpack-cli": "4.10.0",
|
||||
|
@ -9563,6 +9565,12 @@
|
|||
"integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/uuid": {
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.1.tgz",
|
||||
"integrity": "sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/valid-url": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/valid-url/-/valid-url-1.0.3.tgz",
|
||||
|
@ -12061,6 +12069,16 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/bin-build/node_modules/uuid": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
|
||||
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
|
||||
"deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"uuid": "bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/binary-extensions": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
||||
|
@ -30811,6 +30829,16 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/tempfile/node_modules/uuid": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
|
||||
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
|
||||
"deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"uuid": "bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/terser": {
|
||||
"version": "4.8.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz",
|
||||
|
@ -32145,12 +32173,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
|
||||
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
|
||||
"integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"uuid": "bin/uuid"
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/uuid-browser": {
|
||||
|
@ -32858,6 +32886,16 @@
|
|||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-log/node_modules/uuid": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
|
||||
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
|
||||
"deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"uuid": "bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-merge": {
|
||||
"version": "5.8.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz",
|
||||
|
@ -40675,6 +40713,12 @@
|
|||
"integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/uuid": {
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.1.tgz",
|
||||
"integrity": "sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/valid-url": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/valid-url/-/valid-url-1.0.3.tgz",
|
||||
|
@ -42669,6 +42713,12 @@
|
|||
"requires": {
|
||||
"prepend-http": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"uuid": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
|
||||
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -57296,6 +57346,14 @@
|
|||
"requires": {
|
||||
"temp-dir": "^2.0.0",
|
||||
"uuid": "^3.3.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"uuid": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
|
||||
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"terser": {
|
||||
|
@ -58303,9 +58361,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"uuid": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
|
||||
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
|
||||
"integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==",
|
||||
"dev": true
|
||||
},
|
||||
"uuid-browser": {
|
||||
|
@ -58845,6 +58903,14 @@
|
|||
"requires": {
|
||||
"ansi-colors": "^3.0.0",
|
||||
"uuid": "^3.3.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"uuid": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
|
||||
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"webpack-merge": {
|
||||
|
|
|
@ -151,6 +151,7 @@
|
|||
"@types/react": "17.0.43",
|
||||
"@types/react-beautiful-dnd": "13.0.0",
|
||||
"@types/react-dom": "17.0.14",
|
||||
"@types/uuid": "^9.0.1",
|
||||
"@types/valid-url": "1.0.3",
|
||||
"@types/winreg": "1.2.31",
|
||||
"@typescript-eslint/eslint-plugin": "5.18.0",
|
||||
|
@ -220,6 +221,7 @@
|
|||
"style-loader": "3.3.1",
|
||||
"ts-prune": "0.10.3",
|
||||
"typescript": "4.6.3",
|
||||
"uuid": "9.0.0",
|
||||
"valid-url": "1.0.9",
|
||||
"webpack": "5.71.0",
|
||||
"webpack-cli": "4.10.0",
|
||||
|
|
|
@ -163,3 +163,6 @@ export const DOWNLOADS_DROPDOWN_MENU_CANCEL_DOWNLOAD = 'downloads-dropdown-menu-
|
|||
export const DOWNLOADS_DROPDOWN_MENU_CLEAR_FILE = 'downloads-dropdown-menu-clear-file';
|
||||
export const DOWNLOADS_DROPDOWN_MENU_OPEN_FILE = 'downloads-dropdown-menu-open-file';
|
||||
export const DOWNLOADS_DROPDOWN_MENU_SHOW_FILE_IN_FOLDER = 'downloads-dropdown-menu-show-file-in-folder';
|
||||
|
||||
export const SERVERS_URL_MODIFIED = 'servers-modified';
|
||||
export const SERVERS_UPDATE = 'servers-update';
|
||||
|
|
|
@ -6,7 +6,7 @@ import {EventEmitter} from 'events';
|
|||
import WindowsRegistry from 'winreg';
|
||||
import WindowsRegistryUTF8 from 'winreg-utf8';
|
||||
|
||||
import {RegistryConfig as RegistryConfigType, Team} from 'types/config';
|
||||
import {RegistryConfig as RegistryConfigType, FullTeam} from 'types/config';
|
||||
|
||||
import {Logger} from 'common/log';
|
||||
|
||||
|
@ -78,7 +78,7 @@ export default class RegistryConfig extends EventEmitter {
|
|||
*/
|
||||
async getServersListFromRegistry() {
|
||||
const defaultServers = await this.getRegistryEntry(`${BASE_REGISTRY_KEY_PATH}\\DefaultServerList`);
|
||||
return defaultServers.flat(2).reduce((servers: Team[], server, index) => {
|
||||
return defaultServers.flat(2).reduce((servers: FullTeam[], server, index) => {
|
||||
if (server) {
|
||||
servers.push({
|
||||
name: (server as WindowsRegistry.RegistryItem).name,
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
AnyConfig,
|
||||
BuildConfig,
|
||||
CombinedConfig,
|
||||
ConfigServer,
|
||||
Config as ConfigType,
|
||||
LocalConfiguration,
|
||||
RegistryConfig as RegistryConfigType,
|
||||
|
@ -203,6 +204,14 @@ export class Config extends EventEmitter {
|
|||
this.reload();
|
||||
}
|
||||
|
||||
setServers = (servers: ConfigServer[], lastActiveTeam?: number) => {
|
||||
log.debug('setServers', servers, lastActiveTeam);
|
||||
|
||||
this.localConfigData = Object.assign({}, this.localConfigData, {teams: servers, lastActiveTeam: lastActiveTeam ?? this.localConfigData?.lastActiveTeam});
|
||||
this.regenerateCombinedConfigData();
|
||||
this.saveLocalConfigData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to replace the existing config data with new config data
|
||||
*
|
||||
|
@ -277,7 +286,7 @@ export class Config extends EventEmitter {
|
|||
return this.combinedData?.darkMode ?? defaultPreferences.darkMode;
|
||||
}
|
||||
get localTeams() {
|
||||
return this.localConfigData?.teams ?? defaultPreferences.version;
|
||||
return this.localConfigData?.teams ?? defaultPreferences.teams;
|
||||
}
|
||||
get enableHardwareAcceleration() {
|
||||
return this.combinedData?.enableHardwareAcceleration ?? defaultPreferences.enableHardwareAcceleration;
|
||||
|
|
|
@ -15,6 +15,8 @@ export const setLoggingLevel = (level: string) => {
|
|||
log.transports.file.level = level as LevelOption;
|
||||
};
|
||||
|
||||
export const getLevel = () => log.transports.file.level as string;
|
||||
|
||||
export class Logger {
|
||||
private prefixes: string[];
|
||||
|
||||
|
|
|
@ -1,14 +1,30 @@
|
|||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {v4 as uuid} from 'uuid';
|
||||
|
||||
import {Team} from 'types/config';
|
||||
|
||||
import urlUtils from 'common/utils/url';
|
||||
|
||||
export class MattermostServer {
|
||||
id: string;
|
||||
name: string;
|
||||
url: URL;
|
||||
constructor(name: string, serverUrl: string) {
|
||||
this.name = name;
|
||||
this.url = urlUtils.parseURL(serverUrl)!;
|
||||
url!: URL;
|
||||
isPredefined: boolean;
|
||||
|
||||
constructor(server: Team, isPredefined = false) {
|
||||
this.id = uuid();
|
||||
this.name = server.name;
|
||||
this.updateURL(server.url);
|
||||
this.isPredefined = isPredefined;
|
||||
if (!this.url) {
|
||||
throw new Error('Invalid url for creating a server');
|
||||
}
|
||||
}
|
||||
|
||||
updateURL = (url: string) => {
|
||||
this.url = urlUtils.parseURL(url)!;
|
||||
if (!this.url) {
|
||||
throw new Error('Invalid url for creating a server');
|
||||
}
|
||||
|
|
181
src/common/servers/serverManager.test.js
Normal file
181
src/common/servers/serverManager.test.js
Normal file
|
@ -0,0 +1,181 @@
|
|||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {TAB_MESSAGING, TAB_FOCALBOARD, TAB_PLAYBOOKS} from 'common/tabs/TabView';
|
||||
import urlUtils, {equalUrlsIgnoringSubpath} from 'common/utils/url';
|
||||
import Utils from 'common/utils/util';
|
||||
|
||||
import {ServerManager} from './serverManager';
|
||||
|
||||
jest.mock('common/config', () => ({
|
||||
set: jest.fn(),
|
||||
}));
|
||||
jest.mock('common/utils/url', () => ({
|
||||
parseURL: jest.fn(),
|
||||
equalUrlsIgnoringSubpath: jest.fn(),
|
||||
}));
|
||||
jest.mock('common/utils/util', () => ({
|
||||
isVersionGreaterThanOrEqualTo: jest.fn(),
|
||||
}));
|
||||
jest.mock('main/server/serverInfo', () => ({
|
||||
ServerInfo: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('common/servers/serverManager', () => {
|
||||
describe('updateRemoteInfos', () => {
|
||||
const serverManager = new ServerManager();
|
||||
|
||||
beforeEach(() => {
|
||||
const server = {id: 'server-1', url: new URL('http://server-1.com'), name: 'server-1'};
|
||||
server.updateURL = (url) => {
|
||||
server.url = new URL(url);
|
||||
};
|
||||
serverManager.servers = new Map([['server-1', server]]);
|
||||
serverManager.tabs = new Map([
|
||||
['tab-1', {id: 'tab-1', name: TAB_MESSAGING, isOpen: true, server}],
|
||||
['tab-2', {id: 'tab-2', name: TAB_PLAYBOOKS, server}],
|
||||
['tab-3', {id: 'tab-3', name: TAB_FOCALBOARD, server}],
|
||||
]);
|
||||
serverManager.tabOrder = new Map([['server-1', ['tab-1', 'tab-2', 'tab-3']]]);
|
||||
serverManager.persistServers = jest.fn();
|
||||
Utils.isVersionGreaterThanOrEqualTo.mockImplementation((version) => version === '6.0.0');
|
||||
});
|
||||
|
||||
it('should not save when there is nothing to update', () => {
|
||||
serverManager.updateRemoteInfos(new Map([['server-1', {
|
||||
siteURL: 'http://server-1.com',
|
||||
serverVersion: '6.0.0',
|
||||
hasPlaybooks: false,
|
||||
hasFocalboard: false,
|
||||
}]]));
|
||||
|
||||
expect(serverManager.persistServers).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should open all tabs', async () => {
|
||||
serverManager.updateRemoteInfos(new Map([['server-1', {
|
||||
siteURL: 'http://server-1.com',
|
||||
serverVersion: '6.0.0',
|
||||
hasPlaybooks: true,
|
||||
hasFocalboard: true,
|
||||
}]]));
|
||||
|
||||
expect(serverManager.tabs.get('tab-2').isOpen).toBe(true);
|
||||
expect(serverManager.tabs.get('tab-3').isOpen).toBe(true);
|
||||
});
|
||||
|
||||
it('should open only playbooks', async () => {
|
||||
serverManager.updateRemoteInfos(new Map([['server-1', {
|
||||
siteURL: 'http://server-1.com',
|
||||
serverVersion: '6.0.0',
|
||||
hasPlaybooks: true,
|
||||
hasFocalboard: false,
|
||||
}]]));
|
||||
|
||||
expect(serverManager.tabs.get('tab-2').isOpen).toBe(true);
|
||||
expect(serverManager.tabs.get('tab-3').isOpen).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should open none when server version is too old', async () => {
|
||||
serverManager.updateRemoteInfos(new Map([['server-1', {
|
||||
siteURL: 'http://server-1.com',
|
||||
serverVersion: '5.0.0',
|
||||
hasPlaybooks: true,
|
||||
hasFocalboard: true,
|
||||
}]]));
|
||||
|
||||
expect(serverManager.tabs.get('tab-2').isOpen).toBeUndefined();
|
||||
expect(serverManager.tabs.get('tab-3').isOpen).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should update server URL using site URL', async () => {
|
||||
serverManager.updateRemoteInfos(new Map([['server-1', {
|
||||
siteURL: 'http://server-2.com',
|
||||
serverVersion: '6.0.0',
|
||||
hasPlaybooks: true,
|
||||
hasFocalboard: true,
|
||||
}]]));
|
||||
|
||||
expect(serverManager.servers.get('server-1').url.toString()).toBe('http://server-2.com/');
|
||||
});
|
||||
});
|
||||
|
||||
describe('lookupTabByURL', () => {
|
||||
const serverManager = new ServerManager();
|
||||
serverManager.getAllServers = () => [
|
||||
{id: 'server-1', url: new URL('http://server-1.com')},
|
||||
{id: 'server-2', url: new URL('http://server-2.com/subpath')},
|
||||
];
|
||||
serverManager.getOrderedTabsForServer = (serverId) => {
|
||||
if (serverId === 'server-1') {
|
||||
return [
|
||||
{id: 'tab-1', url: new URL('http://server-1.com')},
|
||||
{id: 'tab-1-type-1', url: new URL('http://server-1.com/type1')},
|
||||
{id: 'tab-1-type-2', url: new URL('http://server-1.com/type2')},
|
||||
];
|
||||
}
|
||||
if (serverId === 'server-2') {
|
||||
return [
|
||||
{id: 'tab-2', url: new URL('http://server-2.com/subpath')},
|
||||
{id: 'tab-2-type-1', url: new URL('http://server-2.com/subpath/type1')},
|
||||
{id: 'tab-2-type-2', url: new URL('http://server-2.com/subpath/type2')},
|
||||
];
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
urlUtils.parseURL.mockImplementation((url) => new URL(url));
|
||||
equalUrlsIgnoringSubpath.mockImplementation((url1, url2) => `${url1}`.startsWith(`${url2}`));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('should match the correct server - base URL', () => {
|
||||
const inputURL = new URL('http://server-1.com');
|
||||
expect(serverManager.lookupTabByURL(inputURL)).toStrictEqual({id: 'tab-1', url: new URL('http://server-1.com')});
|
||||
});
|
||||
|
||||
it('should match the correct server - base tab', () => {
|
||||
const inputURL = new URL('http://server-1.com/team');
|
||||
expect(serverManager.lookupTabByURL(inputURL)).toStrictEqual({id: 'tab-1', url: new URL('http://server-1.com')});
|
||||
});
|
||||
|
||||
it('should match the correct server - different tab', () => {
|
||||
const inputURL = new URL('http://server-1.com/type1/app');
|
||||
expect(serverManager.lookupTabByURL(inputURL)).toStrictEqual({id: 'tab-1-type-1', url: new URL('http://server-1.com/type1')});
|
||||
});
|
||||
|
||||
it('should return undefined for server with subpath and URL without', () => {
|
||||
const inputURL = new URL('http://server-2.com');
|
||||
expect(serverManager.lookupTabByURL(inputURL)).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should return undefined for server with subpath and URL with wrong subpath', () => {
|
||||
const inputURL = new URL('http://server-2.com/different/subpath');
|
||||
expect(serverManager.lookupTabByURL(inputURL)).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should match the correct server with a subpath - base URL', () => {
|
||||
const inputURL = new URL('http://server-2.com/subpath');
|
||||
expect(serverManager.lookupTabByURL(inputURL)).toStrictEqual({id: 'tab-2', url: new URL('http://server-2.com/subpath')});
|
||||
});
|
||||
|
||||
it('should match the correct server with a subpath - base tab', () => {
|
||||
const inputURL = new URL('http://server-2.com/subpath/team');
|
||||
expect(serverManager.lookupTabByURL(inputURL)).toStrictEqual({id: 'tab-2', url: new URL('http://server-2.com/subpath')});
|
||||
});
|
||||
|
||||
it('should match the correct server with a subpath - different tab', () => {
|
||||
const inputURL = new URL('http://server-2.com/subpath/type2/team');
|
||||
expect(serverManager.lookupTabByURL(inputURL)).toStrictEqual({id: 'tab-2-type-2', url: new URL('http://server-2.com/subpath/type2')});
|
||||
});
|
||||
|
||||
it('should return undefined for wrong server', () => {
|
||||
const inputURL = new URL('http://server-3.com');
|
||||
expect(serverManager.lookupTabByURL(inputURL)).toBe(undefined);
|
||||
});
|
||||
});
|
||||
});
|
461
src/common/servers/serverManager.ts
Normal file
461
src/common/servers/serverManager.ts
Normal file
|
@ -0,0 +1,461 @@
|
|||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import EventEmitter from 'events';
|
||||
|
||||
import {Team, ConfigServer, ConfigTab} from 'types/config';
|
||||
import {RemoteInfo} from 'types/server';
|
||||
|
||||
import Config from 'common/config';
|
||||
import {
|
||||
SERVERS_URL_MODIFIED,
|
||||
SERVERS_UPDATE,
|
||||
} from 'common/communication';
|
||||
import {Logger, getLevel} from 'common/log';
|
||||
import {MattermostServer} from 'common/servers/MattermostServer';
|
||||
import {TAB_FOCALBOARD, TAB_MESSAGING, TAB_PLAYBOOKS, TabView, getDefaultTabs} from 'common/tabs/TabView';
|
||||
import MessagingTabView from 'common/tabs/MessagingTabView';
|
||||
import FocalboardTabView from 'common/tabs/FocalboardTabView';
|
||||
import PlaybooksTabView from 'common/tabs/PlaybooksTabView';
|
||||
import urlUtils, {equalUrlsIgnoringSubpath} from 'common/utils/url';
|
||||
import Utils from 'common/utils/util';
|
||||
|
||||
const log = new Logger('ServerManager');
|
||||
|
||||
export class ServerManager extends EventEmitter {
|
||||
private servers: Map<string, MattermostServer>;
|
||||
private remoteInfo: Map<string, RemoteInfo>;
|
||||
private serverOrder: string[];
|
||||
private currentServerId?: string;
|
||||
|
||||
private tabs: Map<string, TabView>;
|
||||
private tabOrder: Map<string, string[]>;
|
||||
private lastActiveTab: Map<string, string>;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.servers = new Map();
|
||||
this.remoteInfo = new Map();
|
||||
this.serverOrder = [];
|
||||
this.tabs = new Map();
|
||||
this.tabOrder = new Map();
|
||||
this.lastActiveTab = new Map();
|
||||
}
|
||||
|
||||
getOrderedTabsForServer = (serverId: string) => {
|
||||
log.withPrefix(serverId).debug('getOrderedTabsForServer');
|
||||
|
||||
const tabOrder = this.tabOrder.get(serverId);
|
||||
if (!tabOrder) {
|
||||
return [];
|
||||
}
|
||||
return tabOrder.reduce((tabs, tabId) => {
|
||||
const tab = this.tabs.get(tabId);
|
||||
if (tab) {
|
||||
tabs.push(tab);
|
||||
}
|
||||
return tabs;
|
||||
}, [] as TabView[]);
|
||||
}
|
||||
|
||||
getOrderedServers = () => {
|
||||
log.debug('getOrderedServers');
|
||||
|
||||
return this.serverOrder.reduce((servers, srv) => {
|
||||
const server = this.servers.get(srv);
|
||||
if (server) {
|
||||
servers.push(server);
|
||||
}
|
||||
return servers;
|
||||
}, [] as MattermostServer[]);
|
||||
}
|
||||
|
||||
getCurrentServer = () => {
|
||||
log.debug('getCurrentServer');
|
||||
|
||||
if (!this.currentServerId) {
|
||||
throw new Error('No server set as current');
|
||||
}
|
||||
const server = this.servers.get(this.currentServerId);
|
||||
if (!server) {
|
||||
throw new Error('Current server does not exist');
|
||||
}
|
||||
return server;
|
||||
}
|
||||
|
||||
getLastActiveTabForServer = (serverId: string) => {
|
||||
log.withPrefix(serverId).debug('getLastActiveTabForServer');
|
||||
|
||||
const lastActiveTab = this.lastActiveTab.get(serverId);
|
||||
if (lastActiveTab) {
|
||||
const tab = this.tabs.get(lastActiveTab);
|
||||
if (tab && tab?.isOpen) {
|
||||
return tab;
|
||||
}
|
||||
}
|
||||
return this.getFirstOpenTabForServer(serverId);
|
||||
}
|
||||
|
||||
getServer = (id: string) => {
|
||||
return this.servers.get(id);
|
||||
}
|
||||
|
||||
getTab = (id: string) => {
|
||||
return this.tabs.get(id);
|
||||
}
|
||||
|
||||
getAllServers = () => {
|
||||
return [...this.servers.values()];
|
||||
}
|
||||
|
||||
hasServers = () => {
|
||||
return Boolean(this.servers.size);
|
||||
}
|
||||
|
||||
getRemoteInfo = (serverId: string) => {
|
||||
return this.remoteInfo.get(serverId);
|
||||
}
|
||||
|
||||
updateRemoteInfos = (remoteInfos: Map<string, RemoteInfo>) => {
|
||||
let hasUpdates = false;
|
||||
remoteInfos.forEach((remoteInfo, serverId) => {
|
||||
this.remoteInfo.set(serverId, remoteInfo);
|
||||
hasUpdates = this.updateServerURL(serverId) || hasUpdates;
|
||||
hasUpdates = this.openExtraTabs(serverId) || hasUpdates;
|
||||
});
|
||||
|
||||
if (hasUpdates) {
|
||||
this.persistServers();
|
||||
}
|
||||
}
|
||||
|
||||
lookupTabByURL = (inputURL: URL | string, ignoreScheme = false) => {
|
||||
log.silly('lookupTabByURL', `${inputURL}`, ignoreScheme);
|
||||
|
||||
const parsedURL = urlUtils.parseURL(inputURL);
|
||||
if (!parsedURL) {
|
||||
return undefined;
|
||||
}
|
||||
const server = this.getAllServers().find((server) => {
|
||||
return equalUrlsIgnoringSubpath(parsedURL, server.url, ignoreScheme) && parsedURL.pathname.match(new RegExp(`^${server.url.pathname}(.+)?(/(.+))?$`));
|
||||
});
|
||||
if (!server) {
|
||||
return undefined;
|
||||
}
|
||||
const tabs = this.getOrderedTabsForServer(server.id);
|
||||
|
||||
let selectedTab = tabs.find((tab) => tab && tab.name === TAB_MESSAGING);
|
||||
tabs.
|
||||
filter((tab) => tab && tab.name !== TAB_MESSAGING).
|
||||
forEach((tab) => {
|
||||
if (parsedURL.pathname.match(new RegExp(`^${tab.url.pathname}(/(.+))?`))) {
|
||||
selectedTab = tab;
|
||||
}
|
||||
});
|
||||
return selectedTab;
|
||||
}
|
||||
|
||||
updateServerOrder = (serverOrder: string[]) => {
|
||||
log.debug('updateServerOrder', serverOrder);
|
||||
|
||||
this.serverOrder = serverOrder;
|
||||
this.persistServers();
|
||||
}
|
||||
|
||||
updateTabOrder = (serverId: string, tabOrder: string[]) => {
|
||||
log.withPrefix(serverId).debug('updateTabOrder', tabOrder);
|
||||
|
||||
this.tabOrder.set(serverId, tabOrder);
|
||||
this.persistServers();
|
||||
}
|
||||
|
||||
addServer = (server: Team) => {
|
||||
const newServer = new MattermostServer(server, false);
|
||||
|
||||
if (this.servers.has(newServer.id)) {
|
||||
throw new Error('ID Collision detected. Cannot add server.');
|
||||
}
|
||||
this.servers.set(newServer.id, newServer);
|
||||
|
||||
this.serverOrder.push(newServer.id);
|
||||
const tabOrder: string[] = [];
|
||||
getDefaultTabs().forEach((tab) => {
|
||||
const newTab = this.getTabView(newServer, tab.name, tab.isOpen);
|
||||
this.tabs.set(newTab.id, newTab);
|
||||
tabOrder.push(newTab.id);
|
||||
});
|
||||
this.tabOrder.set(newServer.id, tabOrder);
|
||||
|
||||
// Emit this event whenever we update a server URL to ensure remote info is fetched
|
||||
this.emit(SERVERS_URL_MODIFIED, [newServer.id]);
|
||||
this.persistServers();
|
||||
return newServer;
|
||||
}
|
||||
|
||||
editServer = (serverId: string, server: Team) => {
|
||||
const existingServer = this.servers.get(serverId);
|
||||
if (!existingServer) {
|
||||
return;
|
||||
}
|
||||
|
||||
let urlModified;
|
||||
if (existingServer.url.toString() !== urlUtils.parseURL(server.url)?.toString()) {
|
||||
// Emit this event whenever we update a server URL to ensure remote info is fetched
|
||||
urlModified = () => this.emit(SERVERS_URL_MODIFIED, [serverId]);
|
||||
}
|
||||
existingServer.name = server.name;
|
||||
existingServer.updateURL(server.url);
|
||||
this.servers.set(serverId, existingServer);
|
||||
|
||||
this.tabOrder.get(serverId)?.forEach((tabId) => {
|
||||
const tab = this.tabs.get(tabId);
|
||||
if (tab) {
|
||||
tab.server = existingServer;
|
||||
this.tabs.set(tabId, tab);
|
||||
}
|
||||
});
|
||||
|
||||
urlModified?.();
|
||||
this.persistServers();
|
||||
}
|
||||
|
||||
removeServer = (serverId: string) => {
|
||||
this.tabOrder.get(serverId)?.forEach((tabId) => this.tabs.delete(tabId));
|
||||
this.tabOrder.delete(serverId);
|
||||
this.lastActiveTab.delete(serverId);
|
||||
|
||||
const index = this.serverOrder.findIndex((id) => id === serverId);
|
||||
this.serverOrder.splice(index, 1);
|
||||
this.remoteInfo.delete(serverId);
|
||||
this.servers.delete(serverId);
|
||||
|
||||
this.persistServers();
|
||||
}
|
||||
|
||||
setTabIsOpen = (tabId: string, isOpen: boolean) => {
|
||||
const tab = this.tabs.get(tabId);
|
||||
if (!tab) {
|
||||
return;
|
||||
}
|
||||
tab.isOpen = isOpen;
|
||||
|
||||
this.persistServers();
|
||||
}
|
||||
|
||||
updateLastActive = (tabId: string) => {
|
||||
const tab = this.tabs.get(tabId);
|
||||
if (!tab) {
|
||||
return;
|
||||
}
|
||||
this.lastActiveTab.set(tab.server.id, tabId);
|
||||
|
||||
this.currentServerId = tab.server.id;
|
||||
|
||||
const serverOrder = this.serverOrder.findIndex((srv) => srv === tab.server.id);
|
||||
if (serverOrder < 0) {
|
||||
throw new Error('Server order corrupt, ID not found.');
|
||||
}
|
||||
|
||||
this.persistServers(serverOrder);
|
||||
}
|
||||
|
||||
reloadFromConfig = () => {
|
||||
const serverOrder: string[] = [];
|
||||
Config.predefinedTeams.forEach((team) => {
|
||||
const id = this.initServer(team, true);
|
||||
serverOrder.push(id);
|
||||
});
|
||||
if (Config.enableServerManagement) {
|
||||
Config.localTeams.sort((a, b) => a.order - b.order).forEach((team) => {
|
||||
const id = this.initServer(team, false);
|
||||
serverOrder.push(id);
|
||||
});
|
||||
}
|
||||
this.filterOutDuplicateTeams();
|
||||
this.serverOrder = serverOrder;
|
||||
if (Config.lastActiveTeam) {
|
||||
this.currentServerId = this.serverOrder[Config.lastActiveTeam];
|
||||
} else {
|
||||
this.currentServerId = this.serverOrder[0];
|
||||
}
|
||||
}
|
||||
|
||||
private filterOutDuplicateTeams = () => {
|
||||
const servers = [...this.servers.keys()].map((key) => ({key, value: this.servers.get(key)!}));
|
||||
const uniqueServers = new Set();
|
||||
servers.forEach((server) => {
|
||||
if (uniqueServers.has(`${server.value.name}:${server.value.url}`)) {
|
||||
this.servers.delete(server.key);
|
||||
} else {
|
||||
uniqueServers.add(`${server.value.name}:${server.value.url}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private initServer = (team: ConfigServer, isPredefined: boolean) => {
|
||||
const server = new MattermostServer(team, isPredefined);
|
||||
this.servers.set(server.id, server);
|
||||
|
||||
log.withPrefix(server.id).debug('initialized server');
|
||||
|
||||
const tabOrder: string[] = [];
|
||||
team.tabs.sort((a, b) => a.order - b.order).forEach((tab) => {
|
||||
const tabView = this.getTabView(server, tab.name, tab.isOpen);
|
||||
log.withPrefix(tabView.id).debug('initialized tab');
|
||||
|
||||
this.tabs.set(tabView.id, tabView);
|
||||
tabOrder.push(tabView.id);
|
||||
});
|
||||
this.tabOrder.set(server.id, tabOrder);
|
||||
if (typeof team.lastActiveTab !== 'undefined') {
|
||||
this.lastActiveTab.set(server.id, tabOrder[team.lastActiveTab]);
|
||||
}
|
||||
return server.id;
|
||||
}
|
||||
|
||||
private getFirstOpenTabForServer = (serverId: string) => {
|
||||
const tabOrder = this.getOrderedTabsForServer(serverId);
|
||||
const openTabs = tabOrder.filter((tab) => tab.isOpen);
|
||||
const firstTab = openTabs[0];
|
||||
if (!firstTab) {
|
||||
throw new Error(`No tabs open for server id ${serverId}`);
|
||||
}
|
||||
return firstTab;
|
||||
}
|
||||
|
||||
private persistServers = async (lastActiveTeam?: number) => {
|
||||
this.emit(SERVERS_UPDATE);
|
||||
|
||||
const localServers = [...this.servers.values()].
|
||||
reduce((servers, srv) => {
|
||||
if (srv.isPredefined) {
|
||||
return servers;
|
||||
}
|
||||
servers.push(this.toConfigServer(srv));
|
||||
return servers;
|
||||
}, [] as ConfigServer[]);
|
||||
await Config.setServers(localServers, lastActiveTeam);
|
||||
}
|
||||
|
||||
private getLastActiveTab = (serverId: string) => {
|
||||
let lastActiveTab: number | undefined;
|
||||
if (this.lastActiveTab.has(serverId)) {
|
||||
const index = this.tabOrder.get(serverId)?.indexOf(this.lastActiveTab.get(serverId)!);
|
||||
if (typeof index !== 'undefined' && index >= 0) {
|
||||
lastActiveTab = index;
|
||||
}
|
||||
}
|
||||
return lastActiveTab;
|
||||
}
|
||||
|
||||
private toConfigServer = (server: MattermostServer): ConfigServer => {
|
||||
return {
|
||||
name: server.name,
|
||||
url: `${server.url}`,
|
||||
order: this.serverOrder.indexOf(server.id),
|
||||
lastActiveTab: this.getLastActiveTab(server.id),
|
||||
tabs: this.tabOrder.get(server.id)?.reduce((tabs, tabId, index) => {
|
||||
const tab = this.tabs.get(tabId);
|
||||
if (!tab) {
|
||||
return tabs;
|
||||
}
|
||||
tabs.push({
|
||||
name: tab?.type,
|
||||
order: index,
|
||||
isOpen: tab.isOpen,
|
||||
});
|
||||
return tabs;
|
||||
}, [] as ConfigTab[]) ?? [],
|
||||
};
|
||||
}
|
||||
|
||||
private getTabView = (srv: MattermostServer, tabName: string, isOpen?: boolean) => {
|
||||
switch (tabName) {
|
||||
case TAB_MESSAGING:
|
||||
return new MessagingTabView(srv, isOpen);
|
||||
case TAB_FOCALBOARD:
|
||||
return new FocalboardTabView(srv, isOpen);
|
||||
case TAB_PLAYBOOKS:
|
||||
return new PlaybooksTabView(srv, isOpen);
|
||||
default:
|
||||
throw new Error('Not implemeneted');
|
||||
}
|
||||
}
|
||||
|
||||
private updateServerURL = (serverId: string) => {
|
||||
const server = this.servers.get(serverId);
|
||||
const remoteInfo = this.remoteInfo.get(serverId);
|
||||
|
||||
if (!(server && remoteInfo)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (remoteInfo.siteURL && server.url.toString() !== new URL(remoteInfo.siteURL).toString()) {
|
||||
server.updateURL(remoteInfo.siteURL);
|
||||
this.servers.set(serverId, server);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private openExtraTabs = (serverId: string) => {
|
||||
const server = this.servers.get(serverId);
|
||||
const remoteInfo = this.remoteInfo.get(serverId);
|
||||
|
||||
if (!(server && remoteInfo)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(remoteInfo.serverVersion && Utils.isVersionGreaterThanOrEqualTo(remoteInfo.serverVersion, '6.0.0'))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let hasUpdates = false;
|
||||
const tabOrder = this.tabOrder.get(serverId);
|
||||
if (tabOrder) {
|
||||
tabOrder.forEach((tabId) => {
|
||||
const tab = this.tabs.get(tabId);
|
||||
if (tab) {
|
||||
if (tab.name === TAB_PLAYBOOKS && remoteInfo.hasPlaybooks && typeof tab.isOpen === 'undefined') {
|
||||
log.withPrefix(tab.id).verbose('opening Playbooks');
|
||||
tab.isOpen = true;
|
||||
this.tabs.set(tabId, tab);
|
||||
hasUpdates = true;
|
||||
}
|
||||
if (tab.name === TAB_FOCALBOARD && remoteInfo.hasFocalboard && typeof tab.isOpen === 'undefined') {
|
||||
log.withPrefix(tab.id).verbose('opening Boards');
|
||||
tab.isOpen = true;
|
||||
this.tabs.set(tabId, tab);
|
||||
hasUpdates = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return hasUpdates;
|
||||
}
|
||||
|
||||
private includeId = (id: string, ...prefixes: string[]) => {
|
||||
const shouldInclude = ['debug', 'silly'].includes(getLevel());
|
||||
return shouldInclude ? [id, ...prefixes] : prefixes;
|
||||
};
|
||||
|
||||
getServerLog = (serverId: string, ...additionalPrefixes: string[]) => {
|
||||
const server = this.getServer(serverId);
|
||||
if (!server) {
|
||||
return new Logger(serverId);
|
||||
}
|
||||
return new Logger(...additionalPrefixes, ...this.includeId(serverId, server.name));
|
||||
};
|
||||
|
||||
getViewLog = (viewId: string, ...additionalPrefixes: string[]) => {
|
||||
const view = this.getTab(viewId);
|
||||
if (!view) {
|
||||
return new Logger(viewId);
|
||||
}
|
||||
return new Logger(...additionalPrefixes, ...this.includeId(viewId, view.server.name, view.name));
|
||||
};
|
||||
}
|
||||
|
||||
const serverManager = new ServerManager();
|
||||
export default serverManager;
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {v4 as uuid} from 'uuid';
|
||||
import {Tuple as tuple} from '@bloomberg/record-tuple-polyfill';
|
||||
|
||||
import {MattermostServer} from 'common/servers/MattermostServer';
|
||||
|
@ -8,10 +9,14 @@ import {MattermostServer} from 'common/servers/MattermostServer';
|
|||
import {getTabViewName, TabType, TabView, TabTuple} from './TabView';
|
||||
|
||||
export default abstract class BaseTabView implements TabView {
|
||||
id: string;
|
||||
server: MattermostServer;
|
||||
isOpen?: boolean;
|
||||
|
||||
constructor(server: MattermostServer) {
|
||||
constructor(server: MattermostServer, isOpen?: boolean) {
|
||||
this.id = uuid();
|
||||
this.server = server;
|
||||
this.isOpen = isOpen;
|
||||
}
|
||||
get name(): string {
|
||||
return getTabViewName(this.server.name, this.type);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Team} from 'types/config';
|
||||
import {FullTeam} from 'types/config';
|
||||
|
||||
import {MattermostServer} from 'common/servers/MattermostServer';
|
||||
|
||||
|
@ -12,7 +12,9 @@ export type TabType = typeof TAB_MESSAGING | typeof TAB_FOCALBOARD | typeof TAB_
|
|||
export type TabTuple = [string, TabType];
|
||||
|
||||
export interface TabView {
|
||||
id: string;
|
||||
server: MattermostServer;
|
||||
isOpen?: boolean;
|
||||
|
||||
get name(): string;
|
||||
get type(): TabType;
|
||||
|
@ -21,27 +23,31 @@ export interface TabView {
|
|||
get urlTypeTuple(): TabTuple;
|
||||
}
|
||||
|
||||
export function getDefaultTeamWithTabsFromTeam(team: Team) {
|
||||
export function getDefaultTeamWithTabsFromTeam(team: FullTeam) {
|
||||
return {
|
||||
...team,
|
||||
tabs: [
|
||||
{
|
||||
name: TAB_MESSAGING,
|
||||
order: 0,
|
||||
isOpen: true,
|
||||
},
|
||||
{
|
||||
name: TAB_FOCALBOARD,
|
||||
order: 1,
|
||||
},
|
||||
{
|
||||
name: TAB_PLAYBOOKS,
|
||||
order: 2,
|
||||
},
|
||||
],
|
||||
tabs: getDefaultTabs(),
|
||||
};
|
||||
}
|
||||
|
||||
export function getDefaultTabs() {
|
||||
return [
|
||||
{
|
||||
name: TAB_MESSAGING,
|
||||
order: 0,
|
||||
isOpen: true,
|
||||
},
|
||||
{
|
||||
name: TAB_FOCALBOARD,
|
||||
order: 1,
|
||||
},
|
||||
{
|
||||
name: TAB_PLAYBOOKS,
|
||||
order: 2,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export function getTabDisplayName(tabType: TabType) {
|
||||
switch (tabType) {
|
||||
case TAB_MESSAGING:
|
||||
|
|
|
@ -29,6 +29,7 @@ jest.mock('common/log', () => {
|
|||
}),
|
||||
})),
|
||||
setLoggingLevel: jest.fn(),
|
||||
getLevel: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ export function updateServerInfos(teams: TeamWithTabs[]) {
|
|||
log.silly('app.utils.updateServerInfos');
|
||||
const serverInfos: Array<Promise<RemoteInfo | string | undefined>> = [];
|
||||
teams.forEach((team) => {
|
||||
const serverInfo = new ServerInfo(new MattermostServer(team.name, team.url));
|
||||
const serverInfo = new ServerInfo(new MattermostServer(team));
|
||||
serverInfos.push(serverInfo.promise);
|
||||
});
|
||||
Promise.all(serverInfos).then((data: Array<RemoteInfo | string | undefined>) => {
|
||||
|
|
|
@ -52,7 +52,7 @@ jest.mock('../utils', () => ({
|
|||
shouldHaveBackBar: jest.fn(),
|
||||
}));
|
||||
|
||||
const server = new MattermostServer('server_name', 'http://server-1.com');
|
||||
const server = new MattermostServer({name: 'server_name', url: 'http://server-1.com'});
|
||||
const tabView = new MessagingTabView(server);
|
||||
|
||||
describe('main/views/MattermostView', () => {
|
||||
|
|
|
@ -206,9 +206,9 @@ describe('main/views/viewManager', () => {
|
|||
urlTypeTuple: tuple(`http://${srv.name}.com/`, tabName),
|
||||
url: new URL(`http://${srv.name}.com`),
|
||||
}));
|
||||
MattermostServer.mockImplementation((name, url) => ({
|
||||
name,
|
||||
url: new URL(url),
|
||||
MattermostServer.mockImplementation((server) => ({
|
||||
name: server.name,
|
||||
url: new URL(server.url),
|
||||
}));
|
||||
const onceFn = jest.fn();
|
||||
const loadFn = jest.fn();
|
||||
|
@ -709,9 +709,9 @@ describe('main/views/viewManager', () => {
|
|||
|
||||
beforeEach(() => {
|
||||
Config.teams = servers.concat();
|
||||
MattermostServer.mockImplementation((name, url) => ({
|
||||
name,
|
||||
url: new URL(url),
|
||||
MattermostServer.mockImplementation((server) => ({
|
||||
name: server.name,
|
||||
url: new URL(server.url),
|
||||
}));
|
||||
equalUrlsIgnoringSubpath.mockImplementation((url1, url2) => `${url1}`.startsWith(`${url2}`));
|
||||
});
|
||||
|
|
|
@ -233,7 +233,7 @@ export class ViewManager {
|
|||
*/
|
||||
|
||||
private loadServer = (server: TeamWithTabs) => {
|
||||
const srv = new MattermostServer(server.name, server.url);
|
||||
const srv = new MattermostServer(server);
|
||||
const serverInfo = new ServerInfo(srv);
|
||||
server.tabs.forEach((tab) => this.loadView(srv, serverInfo, tab));
|
||||
}
|
||||
|
@ -422,7 +422,7 @@ export class ViewManager {
|
|||
map((t): [TeamWithTabs, Tab] => [x, t]));
|
||||
|
||||
for (const [team, tab] of sortedTabs) {
|
||||
const srv = new MattermostServer(team.name, team.url);
|
||||
const srv = new MattermostServer(team);
|
||||
const info = new ServerInfo(srv);
|
||||
const tabTuple = tuple(new URL(team.url).href, tab.name as TabType);
|
||||
const recycle = current.get(tabTuple);
|
||||
|
@ -599,8 +599,7 @@ export class ViewManager {
|
|||
if (!server) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const mmServer = new MattermostServer(server.name, server.url);
|
||||
const mmServer = new MattermostServer(server);
|
||||
let selectedTab = this.getServerView(mmServer, TAB_MESSAGING);
|
||||
server.tabs.
|
||||
filter((tab) => tab.name !== TAB_MESSAGING).
|
||||
|
|
|
@ -8,7 +8,7 @@ import {DragDropContext, Draggable, DraggingStyle, Droppable, DropResult, NotDra
|
|||
import {FormattedMessage, injectIntl, IntlShape} from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import {Tab} from 'types/config';
|
||||
import {ConfigTab} from 'types/config';
|
||||
|
||||
import {getTabViewName, TabType, canCloseTab, getTabDisplayName} from 'common/tabs/TabView';
|
||||
|
||||
|
@ -19,7 +19,7 @@ type Props = {
|
|||
isDarkMode: boolean;
|
||||
onSelect: (name: string, index: number) => void;
|
||||
onCloseTab: (name: string) => void;
|
||||
tabs: Tab[];
|
||||
tabs: ConfigTab[];
|
||||
sessionsExpired: Record<string, boolean>;
|
||||
unreadCounts: Record<string, boolean>;
|
||||
mentionCounts: Record<string, number>;
|
||||
|
|
|
@ -7,7 +7,7 @@ import {FormattedMessage} from 'react-intl';
|
|||
import classNames from 'classnames';
|
||||
import {DragDropContext, Draggable, DraggingStyle, Droppable, DropResult, NotDraggingStyle} from 'react-beautiful-dnd';
|
||||
|
||||
import {Team, TeamWithTabs, TeamWithTabsAndGpo} from 'types/config';
|
||||
import {FullTeam, TeamWithTabs, TeamWithTabsAndGpo} from 'types/config';
|
||||
|
||||
import {getTabViewName} from 'common/tabs/TabView';
|
||||
import {TAB_BAR_HEIGHT, THREE_DOT_MENU_WIDTH_MAC} from 'common/utils/constants';
|
||||
|
@ -83,7 +83,7 @@ class TeamDropdown extends React.PureComponent<Record<string, never>, State> {
|
|||
});
|
||||
}
|
||||
|
||||
selectServer = (team: Team) => {
|
||||
selectServer = (team: FullTeam) => {
|
||||
return () => {
|
||||
window.desktop.serverDropdown.switchServer(team.name);
|
||||
this.closeMenu();
|
||||
|
@ -106,7 +106,7 @@ class TeamDropdown extends React.PureComponent<Record<string, never>, State> {
|
|||
this.closeMenu();
|
||||
}
|
||||
|
||||
isActiveTeam = (team: Team) => {
|
||||
isActiveTeam = (team: FullTeam) => {
|
||||
return team.name === this.state.activeTeam;
|
||||
}
|
||||
|
||||
|
@ -139,7 +139,7 @@ class TeamDropdown extends React.PureComponent<Record<string, never>, State> {
|
|||
tabOrder.forEach((t, order) => {
|
||||
teams[t.index].order = order;
|
||||
});
|
||||
this.setState({teams, orderedTeams: teams.concat().sort((a: Team, b: Team) => a.order - b.order), isAnyDragging: false});
|
||||
this.setState({teams, orderedTeams: teams.concat().sort((a: FullTeam, b: FullTeam) => a.order - b.order), isAnyDragging: false});
|
||||
window.desktop.updateTeams(teams);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,26 +3,36 @@
|
|||
|
||||
export type Tab = {
|
||||
name: string;
|
||||
order: number;
|
||||
isOpen?: boolean;
|
||||
}
|
||||
|
||||
export type Team = {
|
||||
name: string;
|
||||
order: number;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export type FullTeam = Team & {
|
||||
order: number;
|
||||
lastActiveTab?: number;
|
||||
}
|
||||
|
||||
export type TeamWithIndex = Team & {index: number};
|
||||
export type TeamWithTabs = Team & {tabs: Tab[]};
|
||||
export type ConfigTab = Tab & {
|
||||
order: number;
|
||||
}
|
||||
|
||||
export type ConfigServer = FullTeam & {
|
||||
tabs: ConfigTab[];
|
||||
}
|
||||
|
||||
export type TeamWithIndex = FullTeam & {index: number};
|
||||
export type TeamWithTabs = ConfigServer & {tabs: Tab[]};
|
||||
export type TeamWithTabsAndGpo = TeamWithTabs & {isGpo?: boolean};
|
||||
|
||||
export type Config = ConfigV3;
|
||||
|
||||
export type ConfigV3 = {
|
||||
version: 3;
|
||||
teams: TeamWithTabs[];
|
||||
teams: ConfigServer[];
|
||||
showTrayIcon: boolean;
|
||||
trayIconTheme: string;
|
||||
minimizeToTray: boolean;
|
||||
|
@ -101,7 +111,7 @@ export type ConfigV0 = {version: 0; url: string};
|
|||
export type AnyConfig = ConfigV3 | ConfigV2 | ConfigV1 | ConfigV0;
|
||||
|
||||
export type BuildConfig = {
|
||||
defaultTeams?: Team[];
|
||||
defaultTeams?: FullTeam[];
|
||||
helpLink: string;
|
||||
enableServerManagement: boolean;
|
||||
enableAutoUpdater: boolean;
|
||||
|
@ -110,7 +120,7 @@ export type BuildConfig = {
|
|||
}
|
||||
|
||||
export type RegistryConfig = {
|
||||
teams: Team[];
|
||||
teams: FullTeam[];
|
||||
enableServerManagement: boolean;
|
||||
enableAutoUpdater: boolean;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue