[MM-50485] Migrate app to ServerManager, remove view names and replace with IDs (#2672)
* Migrate app to ServerManager, remove view names and replace with IDs * Fixed a test * Fixed a bug when adding the initial server * Merge'd * Bug fixes and PR feedback
This commit is contained in:
@@ -18,7 +18,6 @@ export const GET_LOCAL_CONFIGURATION = 'get-local-configuration';
|
||||
export const RELOAD_CONFIGURATION = 'reload-config';
|
||||
export const EMIT_CONFIGURATION = 'emit-configuration';
|
||||
|
||||
export const UPDATE_TEAMS = 'update-teams';
|
||||
export const DARK_MODE_CHANGE = 'dark_mode_change';
|
||||
export const GET_DARK_MODE = 'get-dark-mode';
|
||||
export const USER_ACTIVITY_UPDATE = 'user-activity-update';
|
||||
@@ -38,7 +37,6 @@ export const DOUBLE_CLICK_ON_WINDOW = 'double_click';
|
||||
export const SHOW_NEW_SERVER_MODAL = 'show_new_server_modal';
|
||||
export const SHOW_EDIT_SERVER_MODAL = 'show-edit-server-modal';
|
||||
export const SHOW_REMOVE_SERVER_MODAL = 'show-remove-server-modal';
|
||||
export const MAIN_WINDOW_SHOWN = 'main-window-shown';
|
||||
|
||||
export const RETRIEVE_MODAL_INFO = 'retrieve-modal-info';
|
||||
export const MODAL_CANCEL = 'modal-cancel';
|
||||
@@ -106,8 +104,7 @@ export const APP_LOGGED_OUT = 'app-logged-out';
|
||||
|
||||
export const GET_AVAILABLE_SPELL_CHECKER_LANGUAGES = 'get-available-spell-checker-languages';
|
||||
|
||||
export const GET_VIEW_NAME = 'get-view-name';
|
||||
export const GET_VIEW_WEBCONTENTS_ID = 'get-view-webcontents-id';
|
||||
export const GET_VIEW_INFO_FOR_TEST = 'get-view-info-for-test';
|
||||
|
||||
export const RESIZE_MODAL = 'resize-modal';
|
||||
export const GET_MODAL_UNCLOSEABLE = 'get-modal-uncloseable';
|
||||
@@ -119,7 +116,6 @@ export const UPDATE_URL_VIEW_WIDTH = 'update-url-view-width';
|
||||
export const RELOAD_CURRENT_VIEW = 'reload-current-view';
|
||||
|
||||
export const PING_DOMAIN = 'ping-domain';
|
||||
export const PING_DOMAIN_RESPONSE = 'ping-domain-response';
|
||||
|
||||
export const GET_LANGUAGE_INFORMATION = 'get-language-information';
|
||||
export const GET_AVAILABLE_LANGUAGES = 'get-available-languages';
|
||||
@@ -166,3 +162,8 @@ export const DOWNLOADS_DROPDOWN_MENU_SHOW_FILE_IN_FOLDER = 'downloads-dropdown-m
|
||||
|
||||
export const SERVERS_URL_MODIFIED = 'servers-modified';
|
||||
export const SERVERS_UPDATE = 'servers-update';
|
||||
export const UPDATE_SERVER_ORDER = 'update-server-order';
|
||||
export const UPDATE_TAB_ORDER = 'update-tab-order';
|
||||
export const GET_LAST_ACTIVE = 'get-last-active';
|
||||
export const GET_ORDERED_SERVERS = 'get-ordered-servers';
|
||||
export const GET_ORDERED_TABS_FOR_SERVER = 'get-ordered-tabs-for-server';
|
||||
|
@@ -314,67 +314,66 @@ describe('common/config', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: Re-enable when we migrate to ServerManager fully
|
||||
// describe('regenerateCombinedConfigData', () => {
|
||||
// it('should combine config from all sources', () => {
|
||||
// const config = new Config();
|
||||
// config.reload = jest.fn();
|
||||
// config.init(configPath, appName, appPath);
|
||||
// config.useNativeWindow = false;
|
||||
// config.defaultConfigData = {defaultSetting: 'default', otherDefaultSetting: 'default'};
|
||||
// config.localConfigData = {otherDefaultSetting: 'local', localSetting: 'local', otherLocalSetting: 'local'};
|
||||
// config.buildConfigData = {otherLocalSetting: 'build', buildSetting: 'build', otherBuildSetting: 'build'};
|
||||
// config.registryConfigData = {otherBuildSetting: 'registry', registrySetting: 'registry'};
|
||||
describe('regenerateCombinedConfigData', () => {
|
||||
it('should combine config from all sources', () => {
|
||||
const config = new Config();
|
||||
config.reload = jest.fn();
|
||||
config.init(configPath, appName, appPath);
|
||||
config.useNativeWindow = false;
|
||||
config.defaultConfigData = {defaultSetting: 'default', otherDefaultSetting: 'default'};
|
||||
config.localConfigData = {otherDefaultSetting: 'local', localSetting: 'local', otherLocalSetting: 'local'};
|
||||
config.buildConfigData = {otherLocalSetting: 'build', buildSetting: 'build', otherBuildSetting: 'build'};
|
||||
config.registryConfigData = {otherBuildSetting: 'registry', registrySetting: 'registry'};
|
||||
|
||||
// config.regenerateCombinedConfigData();
|
||||
// config.combinedData.darkMode = false;
|
||||
// expect(config.combinedData).toStrictEqual({
|
||||
// appName: 'app-name',
|
||||
// useNativeWindow: false,
|
||||
// darkMode: false,
|
||||
// otherBuildSetting: 'registry',
|
||||
// registrySetting: 'registry',
|
||||
// otherLocalSetting: 'build',
|
||||
// buildSetting: 'build',
|
||||
// otherDefaultSetting: 'local',
|
||||
// localSetting: 'local',
|
||||
// defaultSetting: 'default',
|
||||
// });
|
||||
// });
|
||||
config.regenerateCombinedConfigData();
|
||||
config.combinedData.darkMode = false;
|
||||
expect(config.combinedData).toStrictEqual({
|
||||
appName: 'app-name',
|
||||
useNativeWindow: false,
|
||||
darkMode: false,
|
||||
otherBuildSetting: 'registry',
|
||||
registrySetting: 'registry',
|
||||
otherLocalSetting: 'build',
|
||||
buildSetting: 'build',
|
||||
otherDefaultSetting: 'local',
|
||||
localSetting: 'local',
|
||||
defaultSetting: 'default',
|
||||
});
|
||||
});
|
||||
|
||||
// it('should not include any teams in the combined config', () => {
|
||||
// const config = new Config();
|
||||
// config.reload = jest.fn();
|
||||
// config.init(configPath, appName, appPath);
|
||||
// config.defaultConfigData = {};
|
||||
// config.localConfigData = {};
|
||||
// config.buildConfigData = {enableServerManagement: true};
|
||||
// config.registryConfigData = {};
|
||||
// config.predefinedTeams.push(team, team);
|
||||
// config.useNativeWindow = false;
|
||||
// config.localConfigData = {teams: [
|
||||
// team,
|
||||
// {
|
||||
// ...team,
|
||||
// name: 'local-team-2',
|
||||
// url: 'http://local-team-2.com',
|
||||
// },
|
||||
// {
|
||||
// ...team,
|
||||
// name: 'local-team-1',
|
||||
// order: 1,
|
||||
// url: 'http://local-team-1.com',
|
||||
// },
|
||||
// ]};
|
||||
it('should not include any teams in the combined config', () => {
|
||||
const config = new Config();
|
||||
config.reload = jest.fn();
|
||||
config.init(configPath, appName, appPath);
|
||||
config.defaultConfigData = {};
|
||||
config.localConfigData = {};
|
||||
config.buildConfigData = {enableServerManagement: true};
|
||||
config.registryConfigData = {};
|
||||
config.predefinedTeams.push(team, team);
|
||||
config.useNativeWindow = false;
|
||||
config.localConfigData = {teams: [
|
||||
team,
|
||||
{
|
||||
...team,
|
||||
name: 'local-team-2',
|
||||
url: 'http://local-team-2.com',
|
||||
},
|
||||
{
|
||||
...team,
|
||||
name: 'local-team-1',
|
||||
order: 1,
|
||||
url: 'http://local-team-1.com',
|
||||
},
|
||||
]};
|
||||
|
||||
// config.regenerateCombinedConfigData();
|
||||
// config.combinedData.darkMode = false;
|
||||
// expect(config.combinedData).toStrictEqual({
|
||||
// appName: 'app-name',
|
||||
// useNativeWindow: false,
|
||||
// darkMode: false,
|
||||
// enableServerManagement: true,
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
config.regenerateCombinedConfigData();
|
||||
config.combinedData.darkMode = false;
|
||||
expect(config.combinedData).toStrictEqual({
|
||||
appName: 'app-name',
|
||||
useNativeWindow: false,
|
||||
darkMode: false,
|
||||
enableServerManagement: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -169,9 +169,6 @@ export class Config extends EventEmitter {
|
||||
get version() {
|
||||
return this.combinedData?.version ?? defaultPreferences.version;
|
||||
}
|
||||
get teams() {
|
||||
return this.combinedData?.teams ?? defaultPreferences.teams;
|
||||
}
|
||||
get darkMode() {
|
||||
return this.combinedData?.darkMode ?? defaultPreferences.darkMode;
|
||||
}
|
||||
@@ -380,71 +377,14 @@ export class Config extends EventEmitter {
|
||||
);
|
||||
|
||||
// We don't want to include the servers in the combined config, they should only be accesible via the ServerManager
|
||||
//delete (this.combinedData as any).teams;
|
||||
delete (this.combinedData as any).teams;
|
||||
delete (this.combinedData as any).defaultTeams;
|
||||
|
||||
if (this.combinedData) {
|
||||
// TODO: This can be removed after we fully migrate to ServerManager
|
||||
let combinedTeams: ConfigServer[] = [];
|
||||
combinedTeams.push(...this.predefinedTeams);
|
||||
if (this.localConfigData && this.enableServerManagement) {
|
||||
combinedTeams.push(...this.localConfigData.teams || []);
|
||||
}
|
||||
combinedTeams = this.filterOutDuplicateTeams(combinedTeams);
|
||||
combinedTeams = this.sortUnorderedTeams(combinedTeams);
|
||||
this.combinedData.teams = combinedTeams;
|
||||
|
||||
this.combinedData.appName = this.appName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the provided list of teams with duplicates filtered out
|
||||
* TODO: This can be removed after we fully migrate to ServerManager
|
||||
* @param {array} teams array of teams to check for duplicates
|
||||
*/
|
||||
private filterOutDuplicateTeams = (teams: ConfigServer[]) => {
|
||||
let newTeams = teams;
|
||||
const uniqueURLs = new Set();
|
||||
newTeams = newTeams.filter((team) => {
|
||||
return uniqueURLs.has(`${team.name}:${team.url}`) ? false : uniqueURLs.add(`${team.name}:${team.url}`);
|
||||
});
|
||||
return newTeams;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a default sort order to the team list, if no order is specified.
|
||||
* @param {array} teams to sort
|
||||
* TODO: This can be removed after we fully migrate to ServerManager
|
||||
*/
|
||||
private sortUnorderedTeams = (teams: ConfigServer[]) => {
|
||||
// We want to preserve the array order of teams in the config, otherwise a lot of bugs will occur
|
||||
const mappedTeams = teams.map((team, index) => ({team, originalOrder: index}));
|
||||
|
||||
// Make a best pass at interpreting sort order. If an order is not specified, assume it is 0.
|
||||
//
|
||||
const newTeams = mappedTeams.sort((x, y) => {
|
||||
if (!x.team.order) {
|
||||
x.team.order = 0;
|
||||
}
|
||||
if (!y.team.order) {
|
||||
y.team.order = 0;
|
||||
}
|
||||
|
||||
// once we ensured `order` exists, we can sort numerically
|
||||
return x.team.order - y.team.order;
|
||||
});
|
||||
|
||||
// Now re-number all items from 0 to (max), ensuring user's sort order is preserved. The
|
||||
// new tabbed interface requires an item with order:0 in order to raise the first tab.
|
||||
//
|
||||
newTeams.forEach((mappedTeam, i) => {
|
||||
mappedTeam.team.order = i;
|
||||
});
|
||||
|
||||
return newTeams.sort((x, y) => x.originalOrder - y.originalOrder).map((mappedTeam) => mappedTeam.team);
|
||||
}
|
||||
|
||||
// helper functions
|
||||
private writeFile = (filePath: string, configData: Partial<ConfigType>, callback?: fs.NoParamCallback) => {
|
||||
if (!this.defaultConfigData) {
|
||||
|
@@ -3,7 +3,7 @@
|
||||
|
||||
import {v4 as uuid} from 'uuid';
|
||||
|
||||
import {Team} from 'types/config';
|
||||
import {MattermostTeam, Team} from 'types/config';
|
||||
|
||||
import urlUtils from 'common/utils/url';
|
||||
|
||||
@@ -13,14 +13,13 @@ export class MattermostServer {
|
||||
url!: URL;
|
||||
isPredefined: boolean;
|
||||
|
||||
constructor(server: Team, isPredefined = false) {
|
||||
constructor(server: Team, isPredefined: boolean) {
|
||||
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) => {
|
||||
@@ -29,4 +28,13 @@ export class MattermostServer {
|
||||
throw new Error('Invalid url for creating a server');
|
||||
}
|
||||
}
|
||||
|
||||
toMattermostTeam = (): MattermostTeam => {
|
||||
return {
|
||||
name: this.name,
|
||||
url: this.url.toString(),
|
||||
id: this.id,
|
||||
isPredefined: this.isPredefined,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@@ -32,9 +32,9 @@ describe('common/servers/serverManager', () => {
|
||||
};
|
||||
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}],
|
||||
['tab-1', {id: 'tab-1', type: TAB_MESSAGING, isOpen: true, server}],
|
||||
['tab-2', {id: 'tab-2', type: TAB_PLAYBOOKS, server}],
|
||||
['tab-3', {id: 'tab-3', type: TAB_FOCALBOARD, server}],
|
||||
]);
|
||||
serverManager.tabOrder = new Map([['server-1', ['tab-1', 'tab-2', 'tab-3']]]);
|
||||
serverManager.persistServers = jest.fn();
|
||||
|
@@ -145,9 +145,9 @@ export class ServerManager extends EventEmitter {
|
||||
}
|
||||
const tabs = this.getOrderedTabsForServer(server.id);
|
||||
|
||||
let selectedTab = tabs.find((tab) => tab && tab.name === TAB_MESSAGING);
|
||||
let selectedTab = tabs.find((tab) => tab && tab.type === TAB_MESSAGING);
|
||||
tabs.
|
||||
filter((tab) => tab && tab.name !== TAB_MESSAGING).
|
||||
filter((tab) => tab && tab.type !== TAB_MESSAGING).
|
||||
forEach((tab) => {
|
||||
if (parsedURL.pathname.match(new RegExp(`^${tab.url.pathname}(/(.+))?`))) {
|
||||
selectedTab = tab;
|
||||
@@ -187,6 +187,10 @@ export class ServerManager extends EventEmitter {
|
||||
});
|
||||
this.tabOrder.set(newServer.id, tabOrder);
|
||||
|
||||
if (!this.currentServerId) {
|
||||
this.currentServerId = newServer.id;
|
||||
}
|
||||
|
||||
// Emit this event whenever we update a server URL to ensure remote info is fetched
|
||||
this.emit(SERVERS_URL_MODIFIED, [newServer.id]);
|
||||
this.persistServers();
|
||||
@@ -230,6 +234,10 @@ export class ServerManager extends EventEmitter {
|
||||
this.remoteInfo.delete(serverId);
|
||||
this.servers.delete(serverId);
|
||||
|
||||
if (this.currentServerId === serverId && this.hasServers()) {
|
||||
this.currentServerId = this.serverOrder[0];
|
||||
}
|
||||
|
||||
this.persistServers();
|
||||
}
|
||||
|
||||
@@ -274,7 +282,7 @@ export class ServerManager extends EventEmitter {
|
||||
}
|
||||
this.filterOutDuplicateTeams();
|
||||
this.serverOrder = serverOrder;
|
||||
if (Config.lastActiveTeam) {
|
||||
if (Config.lastActiveTeam && this.serverOrder[Config.lastActiveTeam]) {
|
||||
this.currentServerId = this.serverOrder[Config.lastActiveTeam];
|
||||
} else {
|
||||
this.currentServerId = this.serverOrder[0];
|
||||
@@ -417,13 +425,13 @@ export class ServerManager extends EventEmitter {
|
||||
tabOrder.forEach((tabId) => {
|
||||
const tab = this.tabs.get(tabId);
|
||||
if (tab) {
|
||||
if (tab.name === TAB_PLAYBOOKS && remoteInfo.hasPlaybooks && typeof tab.isOpen === 'undefined') {
|
||||
if (tab.type === 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') {
|
||||
if (tab.type === TAB_FOCALBOARD && remoteInfo.hasFocalboard && typeof tab.isOpen === 'undefined') {
|
||||
log.withPrefix(tab.id).verbose('opening Boards');
|
||||
tab.isOpen = true;
|
||||
this.tabs.set(tabId, tab);
|
||||
@@ -453,7 +461,7 @@ export class ServerManager extends EventEmitter {
|
||||
if (!view) {
|
||||
return new Logger(viewId);
|
||||
}
|
||||
return new Logger(...additionalPrefixes, ...this.includeId(viewId, view.server.name, view.name));
|
||||
return new Logger(...additionalPrefixes, ...this.includeId(viewId, view.server.name, view.type));
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -3,9 +3,11 @@
|
||||
|
||||
import {v4 as uuid} from 'uuid';
|
||||
|
||||
import {MattermostTab} from 'types/config';
|
||||
|
||||
import {MattermostServer} from 'common/servers/MattermostServer';
|
||||
|
||||
import {getTabViewName, TabType, TabView} from './TabView';
|
||||
import {TabType, TabView} from './TabView';
|
||||
|
||||
export default abstract class BaseTabView implements TabView {
|
||||
id: string;
|
||||
@@ -17,9 +19,6 @@ export default abstract class BaseTabView implements TabView {
|
||||
this.server = server;
|
||||
this.isOpen = isOpen;
|
||||
}
|
||||
get name(): string {
|
||||
return getTabViewName(this.server.name, this.type);
|
||||
}
|
||||
get url(): URL {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
@@ -29,4 +28,12 @@ export default abstract class BaseTabView implements TabView {
|
||||
get shouldNotify(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
toMattermostTab = (): MattermostTab => {
|
||||
return {
|
||||
id: this.id,
|
||||
name: this.type,
|
||||
isOpen: this.isOpen,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@@ -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 {MattermostTab, Team} from 'types/config';
|
||||
|
||||
import {MattermostServer} from 'common/servers/MattermostServer';
|
||||
|
||||
@@ -15,10 +15,11 @@ export interface TabView {
|
||||
server: MattermostServer;
|
||||
isOpen?: boolean;
|
||||
|
||||
get name(): string;
|
||||
get type(): TabType;
|
||||
get url(): URL;
|
||||
get shouldNotify(): boolean;
|
||||
|
||||
toMattermostTab(): MattermostTab;
|
||||
}
|
||||
|
||||
export function getDefaultConfigTeamFromTeam(team: Team & {order: number; lastActiveTab?: number}) {
|
||||
@@ -59,10 +60,6 @@ export function getTabDisplayName(tabType: TabType) {
|
||||
}
|
||||
}
|
||||
|
||||
export function getTabViewName(serverName: string, tabType: string) {
|
||||
return `${serverName}___${tabType}`;
|
||||
}
|
||||
|
||||
export function canCloseTab(tabType: TabType) {
|
||||
return tabType !== TAB_MESSAGING;
|
||||
}
|
||||
|
Reference in New Issue
Block a user