[MM-14093] Rename 'team' to 'server' and 'tab' to 'view' in most cases, some additional cleanup (#2711)
* Rename MattermostTeam -> UniqueServer, MattermostTab -> UniqueView * Rename 'team' to 'server' * Some further cleanup * Rename weirdly named function * Rename 'tab' to 'view' in most instances * Fix i18n * PR feedback
This commit is contained in:
@@ -6,13 +6,13 @@
|
||||
import AppState from 'common/appState';
|
||||
import {LOAD_FAILED, TOGGLE_BACK_BUTTON, UPDATE_TARGET_URL} from 'common/communication';
|
||||
import {MattermostServer} from 'common/servers/MattermostServer';
|
||||
import MessagingTabView from 'common/tabs/MessagingTabView';
|
||||
import MessagingView from 'common/views/MessagingView';
|
||||
|
||||
import MainWindow from '../windows/mainWindow';
|
||||
import ContextMenu from '../contextMenu';
|
||||
import Utils from '../utils';
|
||||
|
||||
import {MattermostView} from './MattermostView';
|
||||
import {MattermostBrowserView} from './MattermostBrowserView';
|
||||
|
||||
jest.mock('electron', () => ({
|
||||
app: {
|
||||
@@ -59,12 +59,12 @@ jest.mock('../utils', () => ({
|
||||
}));
|
||||
|
||||
const server = new MattermostServer({name: 'server_name', url: 'http://server-1.com'});
|
||||
const tabView = new MessagingTabView(server, true);
|
||||
const view = new MessagingView(server, true);
|
||||
|
||||
describe('main/views/MattermostView', () => {
|
||||
describe('main/views/MattermostBrowserView', () => {
|
||||
describe('load', () => {
|
||||
const window = {on: jest.fn()};
|
||||
const mattermostView = new MattermostView(tabView, {}, {});
|
||||
const mattermostView = new MattermostBrowserView(view, {}, {});
|
||||
|
||||
beforeEach(() => {
|
||||
MainWindow.get.mockReturnValue(window);
|
||||
@@ -74,38 +74,38 @@ describe('main/views/MattermostView', () => {
|
||||
|
||||
it('should load provided URL when provided', async () => {
|
||||
const promise = Promise.resolve();
|
||||
mattermostView.view.webContents.loadURL.mockImplementation(() => promise);
|
||||
mattermostView.browserView.webContents.loadURL.mockImplementation(() => promise);
|
||||
mattermostView.load('http://server-2.com');
|
||||
await promise;
|
||||
expect(mattermostView.view.webContents.loadURL).toBeCalledWith('http://server-2.com/', expect.any(Object));
|
||||
expect(mattermostView.browserView.webContents.loadURL).toBeCalledWith('http://server-2.com/', expect.any(Object));
|
||||
expect(mattermostView.loadSuccess).toBeCalledWith('http://server-2.com/');
|
||||
});
|
||||
|
||||
it('should load server URL when not provided', async () => {
|
||||
const promise = Promise.resolve();
|
||||
mattermostView.view.webContents.loadURL.mockImplementation(() => promise);
|
||||
mattermostView.browserView.webContents.loadURL.mockImplementation(() => promise);
|
||||
mattermostView.load();
|
||||
await promise;
|
||||
expect(mattermostView.view.webContents.loadURL).toBeCalledWith('http://server-1.com/', expect.any(Object));
|
||||
expect(mattermostView.browserView.webContents.loadURL).toBeCalledWith('http://server-1.com/', expect.any(Object));
|
||||
expect(mattermostView.loadSuccess).toBeCalledWith('http://server-1.com/');
|
||||
});
|
||||
|
||||
it('should load server URL when bad url provided', async () => {
|
||||
const promise = Promise.resolve();
|
||||
mattermostView.view.webContents.loadURL.mockImplementation(() => promise);
|
||||
mattermostView.browserView.webContents.loadURL.mockImplementation(() => promise);
|
||||
mattermostView.load('a-bad<url');
|
||||
await promise;
|
||||
expect(mattermostView.view.webContents.loadURL).toBeCalledWith('http://server-1.com/', expect.any(Object));
|
||||
expect(mattermostView.browserView.webContents.loadURL).toBeCalledWith('http://server-1.com/', expect.any(Object));
|
||||
expect(mattermostView.loadSuccess).toBeCalledWith('http://server-1.com/');
|
||||
});
|
||||
|
||||
it('should call retry when failing to load', async () => {
|
||||
const error = new Error('test');
|
||||
const promise = Promise.reject(error);
|
||||
mattermostView.view.webContents.loadURL.mockImplementation(() => promise);
|
||||
mattermostView.browserView.webContents.loadURL.mockImplementation(() => promise);
|
||||
mattermostView.load('a-bad<url');
|
||||
await expect(promise).rejects.toThrow(error);
|
||||
expect(mattermostView.view.webContents.loadURL).toBeCalledWith('http://server-1.com/', expect.any(Object));
|
||||
expect(mattermostView.browserView.webContents.loadURL).toBeCalledWith('http://server-1.com/', expect.any(Object));
|
||||
expect(mattermostView.loadRetry).toBeCalledWith('http://server-1.com/', error);
|
||||
});
|
||||
|
||||
@@ -113,23 +113,23 @@ describe('main/views/MattermostView', () => {
|
||||
const error = new Error('test');
|
||||
error.code = 'ERR_CERT_ERROR';
|
||||
const promise = Promise.reject(error);
|
||||
mattermostView.view.webContents.loadURL.mockImplementation(() => promise);
|
||||
mattermostView.browserView.webContents.loadURL.mockImplementation(() => promise);
|
||||
mattermostView.load('a-bad<url');
|
||||
await expect(promise).rejects.toThrow(error);
|
||||
expect(mattermostView.view.webContents.loadURL).toBeCalledWith('http://server-1.com/', expect.any(Object));
|
||||
expect(mattermostView.browserView.webContents.loadURL).toBeCalledWith('http://server-1.com/', expect.any(Object));
|
||||
expect(mattermostView.loadRetry).not.toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('retry', () => {
|
||||
const window = {on: jest.fn()};
|
||||
const mattermostView = new MattermostView(tabView, {}, {});
|
||||
const mattermostView = new MattermostBrowserView(view, {}, {});
|
||||
const retryInBackgroundFn = jest.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
MainWindow.get.mockReturnValue(window);
|
||||
mattermostView.view.webContents.loadURL.mockImplementation(() => Promise.resolve());
|
||||
mattermostView.browserView.webContents.loadURL.mockImplementation(() => Promise.resolve());
|
||||
mattermostView.loadSuccess = jest.fn();
|
||||
mattermostView.loadRetry = jest.fn();
|
||||
mattermostView.emit = jest.fn();
|
||||
@@ -143,16 +143,16 @@ describe('main/views/MattermostView', () => {
|
||||
});
|
||||
|
||||
it('should do nothing when webcontents are destroyed', () => {
|
||||
const webContents = mattermostView.view.webContents;
|
||||
mattermostView.view.webContents = null;
|
||||
const webContents = mattermostView.browserView.webContents;
|
||||
mattermostView.browserView.webContents = null;
|
||||
mattermostView.retry('http://server-1.com')();
|
||||
expect(mattermostView.loadSuccess).not.toBeCalled();
|
||||
mattermostView.view.webContents = webContents;
|
||||
mattermostView.browserView.webContents = webContents;
|
||||
});
|
||||
|
||||
it('should call loadSuccess on successful load', async () => {
|
||||
const promise = Promise.resolve();
|
||||
mattermostView.view.webContents.loadURL.mockImplementation(() => promise);
|
||||
mattermostView.browserView.webContents.loadURL.mockImplementation(() => promise);
|
||||
mattermostView.retry('http://server-1.com')();
|
||||
await promise;
|
||||
expect(mattermostView.loadSuccess).toBeCalledWith('http://server-1.com');
|
||||
@@ -162,10 +162,10 @@ describe('main/views/MattermostView', () => {
|
||||
mattermostView.maxRetries = 10;
|
||||
const error = new Error('test');
|
||||
const promise = Promise.reject(error);
|
||||
mattermostView.view.webContents.loadURL.mockImplementation(() => promise);
|
||||
mattermostView.browserView.webContents.loadURL.mockImplementation(() => promise);
|
||||
mattermostView.retry('http://server-1.com')();
|
||||
await expect(promise).rejects.toThrow(error);
|
||||
expect(mattermostView.view.webContents.loadURL).toBeCalledWith('http://server-1.com', expect.any(Object));
|
||||
expect(mattermostView.browserView.webContents.loadURL).toBeCalledWith('http://server-1.com', expect.any(Object));
|
||||
expect(mattermostView.loadRetry).toBeCalledWith('http://server-1.com', error);
|
||||
});
|
||||
|
||||
@@ -173,12 +173,12 @@ describe('main/views/MattermostView', () => {
|
||||
mattermostView.maxRetries = 0;
|
||||
const error = new Error('test');
|
||||
const promise = Promise.reject(error);
|
||||
mattermostView.view.webContents.loadURL.mockImplementation(() => promise);
|
||||
mattermostView.browserView.webContents.loadURL.mockImplementation(() => promise);
|
||||
mattermostView.retry('http://server-1.com')();
|
||||
await expect(promise).rejects.toThrow(error);
|
||||
expect(mattermostView.view.webContents.loadURL).toBeCalledWith('http://server-1.com', expect.any(Object));
|
||||
expect(mattermostView.browserView.webContents.loadURL).toBeCalledWith('http://server-1.com', expect.any(Object));
|
||||
expect(mattermostView.loadRetry).not.toBeCalled();
|
||||
expect(MainWindow.sendToRenderer).toBeCalledWith(LOAD_FAILED, mattermostView.tab.id, expect.any(String), expect.any(String));
|
||||
expect(MainWindow.sendToRenderer).toBeCalledWith(LOAD_FAILED, mattermostView.view.id, expect.any(String), expect.any(String));
|
||||
expect(mattermostView.status).toBe(-1);
|
||||
jest.runAllTimers();
|
||||
expect(retryInBackgroundFn).toBeCalled();
|
||||
@@ -187,7 +187,7 @@ describe('main/views/MattermostView', () => {
|
||||
|
||||
describe('goToOffset', () => {
|
||||
const window = {on: jest.fn()};
|
||||
const mattermostView = new MattermostView(tabView, {}, {});
|
||||
const mattermostView = new MattermostBrowserView(view, {}, {});
|
||||
mattermostView.reload = jest.fn();
|
||||
|
||||
afterEach(() => {
|
||||
@@ -196,18 +196,18 @@ describe('main/views/MattermostView', () => {
|
||||
});
|
||||
|
||||
it('should only go to offset if it can', () => {
|
||||
mattermostView.view.webContents.canGoToOffset.mockReturnValue(false);
|
||||
mattermostView.browserView.webContents.canGoToOffset.mockReturnValue(false);
|
||||
mattermostView.goToOffset(1);
|
||||
expect(mattermostView.view.webContents.goToOffset).not.toBeCalled();
|
||||
expect(mattermostView.browserView.webContents.goToOffset).not.toBeCalled();
|
||||
|
||||
mattermostView.view.webContents.canGoToOffset.mockReturnValue(true);
|
||||
mattermostView.browserView.webContents.canGoToOffset.mockReturnValue(true);
|
||||
mattermostView.goToOffset(1);
|
||||
expect(mattermostView.view.webContents.goToOffset).toBeCalled();
|
||||
expect(mattermostView.browserView.webContents.goToOffset).toBeCalled();
|
||||
});
|
||||
|
||||
it('should call reload if an error occurs', () => {
|
||||
mattermostView.view.webContents.canGoToOffset.mockReturnValue(true);
|
||||
mattermostView.view.webContents.goToOffset.mockImplementation(() => {
|
||||
mattermostView.browserView.webContents.canGoToOffset.mockReturnValue(true);
|
||||
mattermostView.browserView.webContents.goToOffset.mockImplementation(() => {
|
||||
throw new Error('hi');
|
||||
});
|
||||
mattermostView.goToOffset(1);
|
||||
@@ -217,8 +217,8 @@ describe('main/views/MattermostView', () => {
|
||||
|
||||
describe('onLogin', () => {
|
||||
const window = {on: jest.fn()};
|
||||
const mattermostView = new MattermostView(tabView, {}, {});
|
||||
mattermostView.view.webContents.getURL = jest.fn();
|
||||
const mattermostView = new MattermostBrowserView(view, {}, {});
|
||||
mattermostView.browserView.webContents.getURL = jest.fn();
|
||||
mattermostView.reload = jest.fn();
|
||||
|
||||
afterEach(() => {
|
||||
@@ -227,19 +227,19 @@ describe('main/views/MattermostView', () => {
|
||||
});
|
||||
|
||||
it('should reload view when URL is not on subpath of original server URL', () => {
|
||||
mattermostView.view.webContents.getURL.mockReturnValue('http://server-2.com/subpath');
|
||||
mattermostView.browserView.webContents.getURL.mockReturnValue('http://server-2.com/subpath');
|
||||
mattermostView.onLogin(true);
|
||||
expect(mattermostView.reload).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not reload if URLs are matching', () => {
|
||||
mattermostView.view.webContents.getURL.mockReturnValue('http://server-1.com');
|
||||
mattermostView.browserView.webContents.getURL.mockReturnValue('http://server-1.com');
|
||||
mattermostView.onLogin(true);
|
||||
expect(mattermostView.reload).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not reload if URL is subpath of server URL', () => {
|
||||
mattermostView.view.webContents.getURL.mockReturnValue('http://server-1.com/subpath');
|
||||
mattermostView.browserView.webContents.getURL.mockReturnValue('http://server-1.com/subpath');
|
||||
mattermostView.onLogin(true);
|
||||
expect(mattermostView.reload).not.toHaveBeenCalled();
|
||||
});
|
||||
@@ -247,7 +247,7 @@ describe('main/views/MattermostView', () => {
|
||||
|
||||
describe('loadSuccess', () => {
|
||||
const window = {on: jest.fn()};
|
||||
const mattermostView = new MattermostView(tabView, {}, {});
|
||||
const mattermostView = new MattermostBrowserView(view, {}, {});
|
||||
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
@@ -275,7 +275,7 @@ describe('main/views/MattermostView', () => {
|
||||
|
||||
describe('show', () => {
|
||||
const window = {addBrowserView: jest.fn(), removeBrowserView: jest.fn(), on: jest.fn(), setTopBrowserView: jest.fn()};
|
||||
const mattermostView = new MattermostView(tabView, {}, {});
|
||||
const mattermostView = new MattermostBrowserView(view, {}, {});
|
||||
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
@@ -293,7 +293,7 @@ describe('main/views/MattermostView', () => {
|
||||
it('should add browser view to window and set bounds when request is true and view not currently visible', () => {
|
||||
mattermostView.isVisible = false;
|
||||
mattermostView.show();
|
||||
expect(window.addBrowserView).toBeCalledWith(mattermostView.view);
|
||||
expect(window.addBrowserView).toBeCalledWith(mattermostView.browserView);
|
||||
expect(mattermostView.setBounds).toBeCalled();
|
||||
expect(mattermostView.isVisible).toBe(true);
|
||||
});
|
||||
@@ -314,7 +314,7 @@ describe('main/views/MattermostView', () => {
|
||||
|
||||
describe('hide', () => {
|
||||
const window = {addBrowserView: jest.fn(), removeBrowserView: jest.fn(), on: jest.fn(), setTopBrowserView: jest.fn()};
|
||||
const mattermostView = new MattermostView(tabView, {}, {});
|
||||
const mattermostView = new MattermostBrowserView(view, {}, {});
|
||||
|
||||
beforeEach(() => {
|
||||
MainWindow.get.mockReturnValue(window);
|
||||
@@ -323,7 +323,7 @@ describe('main/views/MattermostView', () => {
|
||||
it('should remove browser view', () => {
|
||||
mattermostView.isVisible = true;
|
||||
mattermostView.hide();
|
||||
expect(window.removeBrowserView).toBeCalledWith(mattermostView.view);
|
||||
expect(window.removeBrowserView).toBeCalledWith(mattermostView.browserView);
|
||||
expect(mattermostView.isVisible).toBe(false);
|
||||
});
|
||||
|
||||
@@ -336,7 +336,7 @@ describe('main/views/MattermostView', () => {
|
||||
|
||||
describe('updateHistoryButton', () => {
|
||||
const window = {on: jest.fn()};
|
||||
const mattermostView = new MattermostView(tabView, {}, {});
|
||||
const mattermostView = new MattermostBrowserView(view, {}, {});
|
||||
|
||||
beforeEach(() => {
|
||||
MainWindow.get.mockReturnValue(window);
|
||||
@@ -345,7 +345,7 @@ describe('main/views/MattermostView', () => {
|
||||
it('should erase history and set isAtRoot when navigating to root URL', () => {
|
||||
mattermostView.atRoot = false;
|
||||
mattermostView.updateHistoryButton();
|
||||
expect(mattermostView.view.webContents.clearHistory).toHaveBeenCalled();
|
||||
expect(mattermostView.browserView.webContents.clearHistory).toHaveBeenCalled();
|
||||
expect(mattermostView.isAtRoot).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -362,22 +362,22 @@ describe('main/views/MattermostView', () => {
|
||||
});
|
||||
|
||||
it('should remove browser view from window', () => {
|
||||
const mattermostView = new MattermostView(tabView, {}, {});
|
||||
mattermostView.view.webContents.destroy = jest.fn();
|
||||
const mattermostView = new MattermostBrowserView(view, {}, {});
|
||||
mattermostView.browserView.webContents.destroy = jest.fn();
|
||||
mattermostView.destroy();
|
||||
expect(window.removeBrowserView).toBeCalledWith(mattermostView.view);
|
||||
expect(window.removeBrowserView).toBeCalledWith(mattermostView.browserView);
|
||||
});
|
||||
|
||||
it('should clear mentions', () => {
|
||||
const mattermostView = new MattermostView(tabView, {}, {});
|
||||
mattermostView.view.webContents.destroy = jest.fn();
|
||||
const mattermostView = new MattermostBrowserView(view, {}, {});
|
||||
mattermostView.browserView.webContents.destroy = jest.fn();
|
||||
mattermostView.destroy();
|
||||
expect(AppState.clear).toBeCalledWith(mattermostView.tab.id);
|
||||
expect(AppState.clear).toBeCalledWith(mattermostView.view.id);
|
||||
});
|
||||
|
||||
it('should clear outstanding timeouts', () => {
|
||||
const mattermostView = new MattermostView(tabView, {}, {});
|
||||
mattermostView.view.webContents.destroy = jest.fn();
|
||||
const mattermostView = new MattermostBrowserView(view, {}, {});
|
||||
mattermostView.browserView.webContents.destroy = jest.fn();
|
||||
const spy = jest.spyOn(global, 'clearTimeout');
|
||||
mattermostView.retryLoad = 999;
|
||||
mattermostView.removeLoading = 1000;
|
||||
@@ -388,7 +388,7 @@ describe('main/views/MattermostView', () => {
|
||||
|
||||
describe('handleInputEvents', () => {
|
||||
const window = {on: jest.fn()};
|
||||
const mattermostView = new MattermostView(tabView, {}, {});
|
||||
const mattermostView = new MattermostBrowserView(view, {}, {});
|
||||
|
||||
it('should open three dot menu on pressing Alt', () => {
|
||||
MainWindow.get.mockReturnValue(window);
|
||||
@@ -413,7 +413,7 @@ describe('main/views/MattermostView', () => {
|
||||
|
||||
describe('handleDidNavigate', () => {
|
||||
const window = {on: jest.fn()};
|
||||
const mattermostView = new MattermostView(tabView, {}, {});
|
||||
const mattermostView = new MattermostBrowserView(view, {}, {});
|
||||
|
||||
beforeEach(() => {
|
||||
MainWindow.get.mockReturnValue(window);
|
||||
@@ -435,7 +435,7 @@ describe('main/views/MattermostView', () => {
|
||||
|
||||
describe('handleUpdateTarget', () => {
|
||||
const window = {on: jest.fn()};
|
||||
const mattermostView = new MattermostView(tabView, {}, {});
|
||||
const mattermostView = new MattermostBrowserView(view, {}, {});
|
||||
|
||||
beforeEach(() => {
|
||||
MainWindow.get.mockReturnValue(window);
|
||||
@@ -466,16 +466,16 @@ describe('main/views/MattermostView', () => {
|
||||
});
|
||||
|
||||
describe('updateMentionsFromTitle', () => {
|
||||
const mattermostView = new MattermostView(tabView, {}, {});
|
||||
const mattermostView = new MattermostBrowserView(view, {}, {});
|
||||
|
||||
it('should parse mentions from title', () => {
|
||||
mattermostView.updateMentionsFromTitle('(7) Mattermost');
|
||||
expect(AppState.updateMentions).toHaveBeenCalledWith(mattermostView.tab.id, 7);
|
||||
expect(AppState.updateMentions).toHaveBeenCalledWith(mattermostView.view.id, 7);
|
||||
});
|
||||
|
||||
it('should parse unreads from title', () => {
|
||||
mattermostView.updateMentionsFromTitle('* Mattermost');
|
||||
expect(AppState.updateMentions).toHaveBeenCalledWith(mattermostView.tab.id, 0);
|
||||
expect(AppState.updateMentions).toHaveBeenCalledWith(mattermostView.view.id, 0);
|
||||
});
|
||||
});
|
||||
});
|
@@ -23,7 +23,7 @@ import {
|
||||
import ServerManager from 'common/servers/serverManager';
|
||||
import {Logger} from 'common/log';
|
||||
import {isInternalURL, parseURL} from 'common/utils/url';
|
||||
import {TabView} from 'common/tabs/TabView';
|
||||
import {MattermostView} from 'common/views/View';
|
||||
|
||||
import MainWindow from 'main/windows/mainWindow';
|
||||
|
||||
@@ -42,12 +42,12 @@ enum Status {
|
||||
const MENTIONS_GROUP = 2;
|
||||
const titleParser = /(\((\d+)\) )?(\* )?/g;
|
||||
|
||||
export class MattermostView extends EventEmitter {
|
||||
tab: TabView;
|
||||
export class MattermostBrowserView extends EventEmitter {
|
||||
view: MattermostView;
|
||||
isVisible: boolean;
|
||||
|
||||
private log: Logger;
|
||||
private view: BrowserView;
|
||||
private browserView: BrowserView;
|
||||
private loggedIn: boolean;
|
||||
private atRoot: boolean;
|
||||
private options: BrowserViewConstructorOptions;
|
||||
@@ -58,9 +58,9 @@ export class MattermostView extends EventEmitter {
|
||||
private maxRetries: number;
|
||||
private altPressStatus: boolean;
|
||||
|
||||
constructor(tab: TabView, options: BrowserViewConstructorOptions) {
|
||||
constructor(view: MattermostView, options: BrowserViewConstructorOptions) {
|
||||
super();
|
||||
this.tab = tab;
|
||||
this.view = view;
|
||||
|
||||
const preload = getLocalPreload('preload.js');
|
||||
this.options = Object.assign({}, options);
|
||||
@@ -75,24 +75,24 @@ export class MattermostView extends EventEmitter {
|
||||
this.isVisible = false;
|
||||
this.loggedIn = false;
|
||||
this.atRoot = true;
|
||||
this.view = new BrowserView(this.options);
|
||||
this.browserView = new BrowserView(this.options);
|
||||
this.resetLoadingStatus();
|
||||
|
||||
this.log = ServerManager.getViewLog(this.id, 'MattermostView');
|
||||
this.log = ServerManager.getViewLog(this.id, 'MattermostBrowserView');
|
||||
this.log.verbose('View created');
|
||||
|
||||
this.view.webContents.on('did-finish-load', this.handleDidFinishLoad);
|
||||
this.view.webContents.on('page-title-updated', this.handleTitleUpdate);
|
||||
this.view.webContents.on('page-favicon-updated', this.handleFaviconUpdate);
|
||||
this.view.webContents.on('update-target-url', this.handleUpdateTarget);
|
||||
this.view.webContents.on('did-navigate', this.handleDidNavigate);
|
||||
this.browserView.webContents.on('did-finish-load', this.handleDidFinishLoad);
|
||||
this.browserView.webContents.on('page-title-updated', this.handleTitleUpdate);
|
||||
this.browserView.webContents.on('page-favicon-updated', this.handleFaviconUpdate);
|
||||
this.browserView.webContents.on('update-target-url', this.handleUpdateTarget);
|
||||
this.browserView.webContents.on('did-navigate', this.handleDidNavigate);
|
||||
if (process.platform !== 'darwin') {
|
||||
this.view.webContents.on('before-input-event', this.handleInputEvents);
|
||||
this.browserView.webContents.on('before-input-event', this.handleInputEvents);
|
||||
}
|
||||
|
||||
WebContentsEventManager.addWebContentsEventListeners(this.view.webContents);
|
||||
WebContentsEventManager.addWebContentsEventListeners(this.browserView.webContents);
|
||||
|
||||
this.contextMenu = new ContextMenu({}, this.view);
|
||||
this.contextMenu = new ContextMenu({}, this.browserView);
|
||||
this.maxRetries = MAX_SERVER_RETRIES;
|
||||
|
||||
this.altPressStatus = false;
|
||||
@@ -105,7 +105,7 @@ export class MattermostView extends EventEmitter {
|
||||
}
|
||||
|
||||
get id() {
|
||||
return this.tab.id;
|
||||
return this.view.id;
|
||||
}
|
||||
get isAtRoot() {
|
||||
return this.atRoot;
|
||||
@@ -114,10 +114,10 @@ export class MattermostView extends EventEmitter {
|
||||
return this.loggedIn;
|
||||
}
|
||||
get currentURL() {
|
||||
return parseURL(this.view.webContents.getURL());
|
||||
return parseURL(this.browserView.webContents.getURL());
|
||||
}
|
||||
get webContentsId() {
|
||||
return this.view.webContents.id;
|
||||
return this.browserView.webContents.id;
|
||||
}
|
||||
|
||||
onLogin = (loggedIn: boolean) => {
|
||||
@@ -127,19 +127,19 @@ export class MattermostView extends EventEmitter {
|
||||
|
||||
this.loggedIn = loggedIn;
|
||||
|
||||
// If we're logging in from a different tab, force a reload
|
||||
// If we're logging in from a different view, force a reload
|
||||
if (loggedIn &&
|
||||
this.currentURL?.toString() !== this.tab.url.toString() &&
|
||||
!this.currentURL?.toString().startsWith(this.tab.url.toString())
|
||||
this.currentURL?.toString() !== this.view.url.toString() &&
|
||||
!this.currentURL?.toString().startsWith(this.view.url.toString())
|
||||
) {
|
||||
this.reload();
|
||||
}
|
||||
}
|
||||
|
||||
goToOffset = (offset: number) => {
|
||||
if (this.view.webContents.canGoToOffset(offset)) {
|
||||
if (this.browserView.webContents.canGoToOffset(offset)) {
|
||||
try {
|
||||
this.view.webContents.goToOffset(offset);
|
||||
this.browserView.webContents.goToOffset(offset);
|
||||
this.updateHistoryButton();
|
||||
} catch (error) {
|
||||
this.log.error(error);
|
||||
@@ -149,17 +149,17 @@ export class MattermostView extends EventEmitter {
|
||||
}
|
||||
|
||||
updateHistoryButton = () => {
|
||||
if (this.currentURL?.toString() === this.tab.url.toString()) {
|
||||
this.view.webContents.clearHistory();
|
||||
if (this.currentURL?.toString() === this.view.url.toString()) {
|
||||
this.browserView.webContents.clearHistory();
|
||||
this.atRoot = true;
|
||||
} else {
|
||||
this.atRoot = false;
|
||||
}
|
||||
this.view.webContents.send(BROWSER_HISTORY_BUTTON, this.view.webContents.canGoBack(), this.view.webContents.canGoForward());
|
||||
this.browserView.webContents.send(BROWSER_HISTORY_BUTTON, this.browserView.webContents.canGoBack(), this.browserView.webContents.canGoForward());
|
||||
}
|
||||
|
||||
load = (someURL?: URL | string) => {
|
||||
if (!this.tab) {
|
||||
if (!this.browserView) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -170,13 +170,13 @@ export class MattermostView extends EventEmitter {
|
||||
loadURL = parsedURL.toString();
|
||||
} else {
|
||||
this.log.error('Cannot parse provided url, using current server url', someURL);
|
||||
loadURL = this.tab.url.toString();
|
||||
loadURL = this.view.url.toString();
|
||||
}
|
||||
} else {
|
||||
loadURL = this.tab.url.toString();
|
||||
loadURL = this.view.url.toString();
|
||||
}
|
||||
this.log.verbose(`Loading ${loadURL}`);
|
||||
const loading = this.view.webContents.loadURL(loadURL, {userAgent: composeUserAgent()});
|
||||
const loading = this.browserView.webContents.loadURL(loadURL, {userAgent: composeUserAgent()});
|
||||
loading.then(this.loadSuccess(loadURL)).catch((err) => {
|
||||
if (err.code && err.code.startsWith('ERR_CERT')) {
|
||||
MainWindow.sendToRenderer(LOAD_FAILED, this.id, err.toString(), loadURL.toString());
|
||||
@@ -205,9 +205,9 @@ export class MattermostView extends EventEmitter {
|
||||
return;
|
||||
}
|
||||
this.isVisible = true;
|
||||
mainWindow.addBrowserView(this.view);
|
||||
mainWindow.setTopBrowserView(this.view);
|
||||
this.setBounds(getWindowBoundaries(mainWindow, shouldHaveBackBar(this.tab.url || '', this.currentURL)));
|
||||
mainWindow.addBrowserView(this.browserView);
|
||||
mainWindow.setTopBrowserView(this.browserView);
|
||||
this.setBounds(getWindowBoundaries(mainWindow, shouldHaveBackBar(this.view.url || '', this.currentURL)));
|
||||
if (this.status === Status.READY) {
|
||||
this.focus();
|
||||
}
|
||||
@@ -216,7 +216,7 @@ export class MattermostView extends EventEmitter {
|
||||
hide = () => {
|
||||
if (this.isVisible) {
|
||||
this.isVisible = false;
|
||||
MainWindow.get()?.removeBrowserView(this.view);
|
||||
MainWindow.get()?.removeBrowserView(this.browserView);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,27 +226,27 @@ export class MattermostView extends EventEmitter {
|
||||
}
|
||||
|
||||
getBounds = () => {
|
||||
return this.view.getBounds();
|
||||
return this.browserView.getBounds();
|
||||
}
|
||||
|
||||
openFind = () => {
|
||||
this.view.webContents.sendInputEvent({type: 'keyDown', keyCode: 'F', modifiers: [process.platform === 'darwin' ? 'cmd' : 'ctrl', 'shift']});
|
||||
this.browserView.webContents.sendInputEvent({type: 'keyDown', keyCode: 'F', modifiers: [process.platform === 'darwin' ? 'cmd' : 'ctrl', 'shift']});
|
||||
}
|
||||
|
||||
setBounds = (boundaries: Electron.Rectangle) => {
|
||||
this.view.setBounds(boundaries);
|
||||
this.browserView.setBounds(boundaries);
|
||||
}
|
||||
|
||||
destroy = () => {
|
||||
WebContentsEventManager.removeWebContentsListeners(this.webContentsId);
|
||||
AppState.clear(this.id);
|
||||
MainWindow.get()?.removeBrowserView(this.view);
|
||||
MainWindow.get()?.removeBrowserView(this.browserView);
|
||||
|
||||
// workaround to eliminate zombie processes
|
||||
// https://github.com/mattermost/desktop/pull/1519
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
this.view.webContents.destroy();
|
||||
this.browserView.webContents.destroy();
|
||||
|
||||
this.isVisible = false;
|
||||
if (this.retryLoad) {
|
||||
@@ -293,7 +293,7 @@ export class MattermostView extends EventEmitter {
|
||||
}
|
||||
|
||||
openDevTools = () => {
|
||||
this.view.webContents.openDevTools({mode: 'detach'});
|
||||
this.browserView.webContents.openDevTools({mode: 'detach'});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -301,16 +301,16 @@ export class MattermostView extends EventEmitter {
|
||||
*/
|
||||
|
||||
sendToRenderer = (channel: string, ...args: any[]) => {
|
||||
this.view.webContents.send(channel, ...args);
|
||||
this.browserView.webContents.send(channel, ...args);
|
||||
}
|
||||
|
||||
isDestroyed = () => {
|
||||
return this.view.webContents.isDestroyed();
|
||||
return this.browserView.webContents.isDestroyed();
|
||||
}
|
||||
|
||||
focus = () => {
|
||||
if (this.view.webContents) {
|
||||
this.view.webContents.focus();
|
||||
if (this.browserView.webContents) {
|
||||
this.browserView.webContents.focus();
|
||||
} else {
|
||||
this.log.warn('trying to focus the browserview, but it doesn\'t yet have webcontents.');
|
||||
}
|
||||
@@ -361,7 +361,7 @@ export class MattermostView extends EventEmitter {
|
||||
// if favicon is null, it will affect appState, but won't be memoized
|
||||
private findUnreadState = (favicon: string | null) => {
|
||||
try {
|
||||
this.view.webContents.send(IS_UNREAD, favicon, this.id);
|
||||
this.browserView.webContents.send(IS_UNREAD, favicon, this.id);
|
||||
} catch (err: any) {
|
||||
this.log.error('There was an error trying to request the unread state', err);
|
||||
}
|
||||
@@ -388,17 +388,17 @@ export class MattermostView extends EventEmitter {
|
||||
private retry = (loadURL: string) => {
|
||||
return () => {
|
||||
// window was closed while retrying
|
||||
if (!this.view || !this.view.webContents) {
|
||||
if (!this.browserView || !this.browserView.webContents) {
|
||||
return;
|
||||
}
|
||||
const loading = this.view.webContents.loadURL(loadURL, {userAgent: composeUserAgent()});
|
||||
const loading = this.browserView.webContents.loadURL(loadURL, {userAgent: composeUserAgent()});
|
||||
loading.then(this.loadSuccess(loadURL)).catch((err) => {
|
||||
if (this.maxRetries-- > 0) {
|
||||
this.loadRetry(loadURL, err);
|
||||
} else {
|
||||
MainWindow.sendToRenderer(LOAD_FAILED, this.id, err.toString(), loadURL.toString());
|
||||
this.emit(LOAD_FAILED, this.id, err.toString(), loadURL.toString());
|
||||
this.log.info(`Couldn't establish a connection with ${loadURL}, will continue to retry in the background`, err);
|
||||
this.log.info(`Couldn't esviewlish a connection with ${loadURL}, will continue to retry in the background`, err);
|
||||
this.status = Status.ERROR;
|
||||
this.retryLoad = setTimeout(this.retryInBackground(loadURL), RELOAD_INTERVAL);
|
||||
}
|
||||
@@ -409,10 +409,10 @@ export class MattermostView extends EventEmitter {
|
||||
private retryInBackground = (loadURL: string) => {
|
||||
return () => {
|
||||
// window was closed while retrying
|
||||
if (!this.view || !this.view.webContents) {
|
||||
if (!this.browserView || !this.browserView.webContents) {
|
||||
return;
|
||||
}
|
||||
const loading = this.view.webContents.loadURL(loadURL, {userAgent: composeUserAgent()});
|
||||
const loading = this.browserView.webContents.loadURL(loadURL, {userAgent: composeUserAgent()});
|
||||
loading.then(this.loadSuccess(loadURL)).catch(() => {
|
||||
this.retryLoad = setTimeout(this.retryInBackground(loadURL), RELOAD_INTERVAL);
|
||||
});
|
||||
@@ -431,7 +431,7 @@ export class MattermostView extends EventEmitter {
|
||||
MainWindow.sendToRenderer(LOAD_SUCCESS, this.id);
|
||||
this.maxRetries = MAX_SERVER_RETRIES;
|
||||
if (this.status === Status.LOADING) {
|
||||
this.updateMentionsFromTitle(this.view.webContents.getTitle());
|
||||
this.updateMentionsFromTitle(this.browserView.webContents.getTitle());
|
||||
this.findUnreadState(null);
|
||||
}
|
||||
this.status = Status.WAITING_MM;
|
||||
@@ -439,7 +439,7 @@ export class MattermostView extends EventEmitter {
|
||||
this.emit(LOAD_SUCCESS, this.id, loadURL);
|
||||
const mainWindow = MainWindow.get();
|
||||
if (mainWindow && this.currentURL) {
|
||||
this.setBounds(getWindowBoundaries(mainWindow, shouldHaveBackBar(this.tab.url || '', this.currentURL)));
|
||||
this.setBounds(getWindowBoundaries(mainWindow, shouldHaveBackBar(this.view.url || '', this.currentURL)));
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -453,13 +453,13 @@ export class MattermostView extends EventEmitter {
|
||||
|
||||
// wait for screen to truly finish loading before sending the message down
|
||||
const timeout = setInterval(() => {
|
||||
if (!this.view.webContents) {
|
||||
if (!this.browserView.webContents) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.view.webContents.isLoading()) {
|
||||
if (!this.browserView.webContents.isLoading()) {
|
||||
try {
|
||||
this.view.webContents.send(SET_VIEW_OPTIONS, this.id, this.tab.shouldNotify);
|
||||
this.browserView.webContents.send(SET_VIEW_OPTIONS, this.id, this.view.shouldNotify);
|
||||
clearTimeout(timeout);
|
||||
} catch (e) {
|
||||
this.log.error('failed to send view options to view');
|
||||
@@ -480,7 +480,7 @@ export class MattermostView extends EventEmitter {
|
||||
return;
|
||||
}
|
||||
|
||||
if (shouldHaveBackBar(this.tab.url || '', parsedURL)) {
|
||||
if (shouldHaveBackBar(this.view.url || '', parsedURL)) {
|
||||
this.setBounds(getWindowBoundaries(mainWindow, true));
|
||||
MainWindow.sendToRenderer(TOGGLE_BACK_BUTTON, true);
|
||||
this.log.debug('show back button');
|
||||
@@ -494,7 +494,7 @@ export class MattermostView extends EventEmitter {
|
||||
private handleUpdateTarget = (e: Event, url: string) => {
|
||||
this.log.silly('handleUpdateTarget', url);
|
||||
const parsedURL = parseURL(url);
|
||||
if (parsedURL && isInternalURL(parsedURL, this.tab.server.url)) {
|
||||
if (parsedURL && isInternalURL(parsedURL, this.view.server.url)) {
|
||||
this.emit(UPDATE_TARGET_URL);
|
||||
} else {
|
||||
this.emit(UPDATE_TARGET_URL, url);
|
||||
@@ -502,7 +502,7 @@ export class MattermostView extends EventEmitter {
|
||||
}
|
||||
|
||||
private handleServerWasModified = (serverIds: string) => {
|
||||
if (serverIds.includes(this.tab.server.id)) {
|
||||
if (serverIds.includes(this.view.server.id)) {
|
||||
this.reload();
|
||||
}
|
||||
}
|
@@ -7,7 +7,7 @@ import {TAB_BAR_HEIGHT, THREE_DOT_MENU_WIDTH, THREE_DOT_MENU_WIDTH_MAC, MENU_SHA
|
||||
|
||||
import MainWindow from 'main/windows/mainWindow';
|
||||
|
||||
import {TeamDropdownView} from './teamDropdownView';
|
||||
import {ServerDropdownView} from './serverDropdownView';
|
||||
|
||||
jest.mock('main/utils', () => ({
|
||||
getLocalPreload: (file) => file,
|
||||
@@ -38,36 +38,36 @@ jest.mock('common/servers/serverManager', () => ({
|
||||
getOrderedServers: jest.fn().mockReturnValue([]),
|
||||
}));
|
||||
|
||||
describe('main/views/teamDropdownView', () => {
|
||||
describe('main/views/serverDropdownView', () => {
|
||||
describe('getBounds', () => {
|
||||
beforeEach(() => {
|
||||
MainWindow.getBounds.mockReturnValue({width: 500, height: 400, x: 0, y: 0});
|
||||
});
|
||||
|
||||
const teamDropdownView = new TeamDropdownView();
|
||||
const serverDropdownView = new ServerDropdownView();
|
||||
if (process.platform === 'darwin') {
|
||||
it('should account for three dot menu, tab bar and shadow', () => {
|
||||
expect(teamDropdownView.getBounds(400, 300)).toStrictEqual({x: THREE_DOT_MENU_WIDTH_MAC - MENU_SHADOW_WIDTH, y: TAB_BAR_HEIGHT - MENU_SHADOW_WIDTH, width: 400, height: 300});
|
||||
expect(serverDropdownView.getBounds(400, 300)).toStrictEqual({x: THREE_DOT_MENU_WIDTH_MAC - MENU_SHADOW_WIDTH, y: TAB_BAR_HEIGHT - MENU_SHADOW_WIDTH, width: 400, height: 300});
|
||||
});
|
||||
} else {
|
||||
it('should account for three dot menu, tab bar and shadow', () => {
|
||||
expect(teamDropdownView.getBounds(400, 300)).toStrictEqual({x: THREE_DOT_MENU_WIDTH - MENU_SHADOW_WIDTH, y: TAB_BAR_HEIGHT - MENU_SHADOW_WIDTH, width: 400, height: 300});
|
||||
expect(serverDropdownView.getBounds(400, 300)).toStrictEqual({x: THREE_DOT_MENU_WIDTH - MENU_SHADOW_WIDTH, y: TAB_BAR_HEIGHT - MENU_SHADOW_WIDTH, width: 400, height: 300});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it('should change the view bounds based on open/closed state', () => {
|
||||
const teamDropdownView = new TeamDropdownView();
|
||||
teamDropdownView.view = {
|
||||
const serverDropdownView = new ServerDropdownView();
|
||||
serverDropdownView.view = {
|
||||
setBounds: jest.fn(),
|
||||
webContents: {
|
||||
focus: jest.fn(),
|
||||
},
|
||||
};
|
||||
teamDropdownView.bounds = {width: 400, height: 300};
|
||||
teamDropdownView.handleOpen();
|
||||
expect(teamDropdownView.view.setBounds).toBeCalledWith(teamDropdownView.bounds);
|
||||
teamDropdownView.handleClose();
|
||||
expect(teamDropdownView.view.setBounds).toBeCalledWith({width: 0, height: 0, x: expect.any(Number), y: expect.any(Number)});
|
||||
serverDropdownView.bounds = {width: 400, height: 300};
|
||||
serverDropdownView.handleOpen();
|
||||
expect(serverDropdownView.view.setBounds).toBeCalledWith(serverDropdownView.bounds);
|
||||
serverDropdownView.handleClose();
|
||||
expect(serverDropdownView.view.setBounds).toBeCalledWith({width: 0, height: 0, x: expect.any(Number), y: expect.any(Number)});
|
||||
});
|
||||
});
|
@@ -3,16 +3,16 @@
|
||||
|
||||
import {BrowserView, ipcMain, IpcMainEvent} from 'electron';
|
||||
|
||||
import {MattermostTeam} from 'types/config';
|
||||
import {UniqueServer} from 'types/config';
|
||||
|
||||
import AppState from 'common/appState';
|
||||
import {
|
||||
CLOSE_TEAMS_DROPDOWN,
|
||||
CLOSE_SERVERS_DROPDOWN,
|
||||
EMIT_CONFIGURATION,
|
||||
OPEN_TEAMS_DROPDOWN,
|
||||
UPDATE_TEAMS_DROPDOWN,
|
||||
OPEN_SERVERS_DROPDOWN,
|
||||
UPDATE_SERVERS_DROPDOWN,
|
||||
UPDATE_APPSTATE,
|
||||
REQUEST_TEAMS_DROPDOWN_INFO,
|
||||
REQUEST_SERVERS_DROPDOWN_INFO,
|
||||
RECEIVE_DROPDOWN_MENU_SIZE,
|
||||
SERVERS_UPDATE,
|
||||
MAIN_WINDOW_CREATED,
|
||||
@@ -27,12 +27,12 @@ import {getLocalPreload, getLocalURLString} from 'main/utils';
|
||||
|
||||
import MainWindow from '../windows/mainWindow';
|
||||
|
||||
const log = new Logger('TeamDropdownView');
|
||||
const log = new Logger('ServerDropdownView');
|
||||
|
||||
export class TeamDropdownView {
|
||||
export class ServerDropdownView {
|
||||
private view?: BrowserView;
|
||||
private teams: MattermostTeam[];
|
||||
private hasGPOTeams: boolean;
|
||||
private servers: UniqueServer[];
|
||||
private hasGPOServers: boolean;
|
||||
private isOpen: boolean;
|
||||
private bounds: Electron.Rectangle;
|
||||
|
||||
@@ -43,8 +43,8 @@ export class TeamDropdownView {
|
||||
private windowBounds?: Electron.Rectangle;
|
||||
|
||||
constructor() {
|
||||
this.teams = [];
|
||||
this.hasGPOTeams = false;
|
||||
this.servers = [];
|
||||
this.hasGPOServers = false;
|
||||
this.isOpen = false;
|
||||
this.bounds = this.getBounds(0, 0);
|
||||
|
||||
@@ -55,12 +55,12 @@ export class TeamDropdownView {
|
||||
MainWindow.on(MAIN_WINDOW_CREATED, this.init);
|
||||
MainWindow.on(MAIN_WINDOW_RESIZED, this.updateWindowBounds);
|
||||
|
||||
ipcMain.on(OPEN_TEAMS_DROPDOWN, this.handleOpen);
|
||||
ipcMain.on(CLOSE_TEAMS_DROPDOWN, this.handleClose);
|
||||
ipcMain.on(OPEN_SERVERS_DROPDOWN, this.handleOpen);
|
||||
ipcMain.on(CLOSE_SERVERS_DROPDOWN, this.handleClose);
|
||||
ipcMain.on(RECEIVE_DROPDOWN_MENU_SIZE, this.handleReceivedMenuSize);
|
||||
|
||||
ipcMain.on(EMIT_CONFIGURATION, this.updateDropdown);
|
||||
ipcMain.on(REQUEST_TEAMS_DROPDOWN_INFO, this.updateDropdown);
|
||||
ipcMain.on(REQUEST_SERVERS_DROPDOWN_INFO, this.updateDropdown);
|
||||
|
||||
AppState.on(UPDATE_APPSTATE, this.updateMentions);
|
||||
ServerManager.on(SERVERS_UPDATE, this.updateServers);
|
||||
@@ -84,7 +84,7 @@ export class TeamDropdownView {
|
||||
}});
|
||||
this.view.webContents.loadURL(getLocalURLString('dropdown.html'));
|
||||
|
||||
this.setOrderedTeams();
|
||||
this.setOrderedServers();
|
||||
this.windowBounds = MainWindow.getBounds();
|
||||
this.updateDropdown();
|
||||
MainWindow.get()?.addBrowserView(this.view);
|
||||
@@ -94,13 +94,13 @@ export class TeamDropdownView {
|
||||
log.silly('updateDropdown');
|
||||
|
||||
this.view?.webContents.send(
|
||||
UPDATE_TEAMS_DROPDOWN,
|
||||
this.teams,
|
||||
UPDATE_SERVERS_DROPDOWN,
|
||||
this.servers,
|
||||
Config.darkMode,
|
||||
this.windowBounds,
|
||||
ServerManager.hasServers() ? ServerManager.getCurrentServer().id : undefined,
|
||||
Config.enableServerManagement,
|
||||
this.hasGPOTeams,
|
||||
this.hasGPOServers,
|
||||
this.expired,
|
||||
this.mentions,
|
||||
this.unreads,
|
||||
@@ -108,7 +108,7 @@ export class TeamDropdownView {
|
||||
}
|
||||
|
||||
private updateServers = () => {
|
||||
this.setOrderedTeams();
|
||||
this.setOrderedServers();
|
||||
this.updateDropdown();
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ export class TeamDropdownView {
|
||||
this.view.setBounds(this.bounds);
|
||||
MainWindow.get()?.setTopBrowserView(this.view);
|
||||
this.view.webContents.focus();
|
||||
MainWindow.sendToRenderer(OPEN_TEAMS_DROPDOWN);
|
||||
MainWindow.sendToRenderer(OPEN_SERVERS_DROPDOWN);
|
||||
this.isOpen = true;
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ export class TeamDropdownView {
|
||||
log.debug('handleClose');
|
||||
|
||||
this.view?.setBounds(this.getBounds(0, 0));
|
||||
MainWindow.sendToRenderer(CLOSE_TEAMS_DROPDOWN);
|
||||
MainWindow.sendToRenderer(CLOSE_SERVERS_DROPDOWN);
|
||||
this.isOpen = false;
|
||||
}
|
||||
|
||||
@@ -174,7 +174,7 @@ export class TeamDropdownView {
|
||||
private reduceNotifications = <T>(inputMap: Map<string, T>, items: Map<string, T>, modifier: (base?: T, value?: T) => T) => {
|
||||
inputMap.clear();
|
||||
return [...items.keys()].reduce((map, key) => {
|
||||
const view = ServerManager.getTab(key);
|
||||
const view = ServerManager.getView(key);
|
||||
if (!view) {
|
||||
return map;
|
||||
}
|
||||
@@ -183,11 +183,11 @@ export class TeamDropdownView {
|
||||
}, inputMap);
|
||||
}
|
||||
|
||||
private setOrderedTeams = () => {
|
||||
this.teams = ServerManager.getOrderedServers().map((team) => team.toMattermostTeam());
|
||||
this.hasGPOTeams = this.teams.some((srv) => srv.isPredefined);
|
||||
private setOrderedServers = () => {
|
||||
this.servers = ServerManager.getOrderedServers().map((server) => server.toUniqueServer());
|
||||
this.hasGPOServers = this.servers.some((srv) => srv.isPredefined);
|
||||
}
|
||||
}
|
||||
|
||||
const teamDropdownView = new TeamDropdownView();
|
||||
export default teamDropdownView;
|
||||
const serverDropdownView = new ServerDropdownView();
|
||||
export default serverDropdownView;
|
@@ -7,13 +7,13 @@
|
||||
import {dialog} from 'electron';
|
||||
|
||||
import {BROWSER_HISTORY_PUSH, LOAD_SUCCESS, SET_ACTIVE_VIEW} from 'common/communication';
|
||||
import {TAB_MESSAGING} from 'common/tabs/TabView';
|
||||
import {TAB_MESSAGING} from 'common/views/View';
|
||||
import ServerManager from 'common/servers/serverManager';
|
||||
import urlUtils from 'common/utils/url';
|
||||
|
||||
import MainWindow from 'main/windows/mainWindow';
|
||||
|
||||
import {MattermostView} from './MattermostView';
|
||||
import {MattermostBrowserView} from './MattermostBrowserView';
|
||||
import {ViewManager} from './viewManager';
|
||||
import LoadingScreen from './loadingScreen';
|
||||
|
||||
@@ -30,12 +30,9 @@ jest.mock('electron', () => ({
|
||||
handle: jest.fn(),
|
||||
},
|
||||
}));
|
||||
jest.mock('common/config', () => ({
|
||||
teams: [],
|
||||
}));
|
||||
jest.mock('common/tabs/TabView', () => ({
|
||||
getTabViewName: jest.fn((a, b) => `${a}-${b}`),
|
||||
TAB_MESSAGING: 'tab',
|
||||
jest.mock('common/views/View', () => ({
|
||||
getViewName: jest.fn((a, b) => `${a}-${b}`),
|
||||
TAB_MESSAGING: 'view',
|
||||
}));
|
||||
|
||||
jest.mock('common/servers/MattermostServer', () => ({
|
||||
@@ -79,7 +76,7 @@ jest.mock('common/servers/serverManager', () => ({
|
||||
getLastActiveServer: jest.fn(),
|
||||
getLastActiveTabForServer: jest.fn(),
|
||||
updateLastActive: jest.fn(),
|
||||
lookupTabByURL: jest.fn(),
|
||||
lookupViewByURL: jest.fn(),
|
||||
getRemoteInfo: jest.fn(),
|
||||
on: jest.fn(),
|
||||
getServerLog: () => ({
|
||||
@@ -100,8 +97,8 @@ jest.mock('common/servers/serverManager', () => ({
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('./MattermostView', () => ({
|
||||
MattermostView: jest.fn(),
|
||||
jest.mock('./MattermostBrowserView', () => ({
|
||||
MattermostBrowserView: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('./modalManager', () => ({
|
||||
@@ -121,12 +118,12 @@ describe('main/views/viewManager', () => {
|
||||
beforeEach(() => {
|
||||
viewManager.showById = jest.fn();
|
||||
MainWindow.get.mockReturnValue({});
|
||||
MattermostView.mockImplementation((tab) => ({
|
||||
MattermostBrowserView.mockImplementation((view) => ({
|
||||
on: jest.fn(),
|
||||
load: loadFn,
|
||||
once: onceFn,
|
||||
destroy: destroyFn,
|
||||
id: tab.id,
|
||||
id: view.id,
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -136,21 +133,21 @@ describe('main/views/viewManager', () => {
|
||||
viewManager.views = new Map();
|
||||
});
|
||||
|
||||
it('should add closed tabs to closedViews', () => {
|
||||
viewManager.loadView({id: 'server1'}, {id: 'tab1', isOpen: false});
|
||||
expect(viewManager.closedViews.has('tab1')).toBe(true);
|
||||
it('should add closed views to closedViews', () => {
|
||||
viewManager.loadView({id: 'server1'}, {id: 'view1', isOpen: false});
|
||||
expect(viewManager.closedViews.has('view1')).toBe(true);
|
||||
});
|
||||
|
||||
it('should remove from remove from closedViews when the tab is open', () => {
|
||||
viewManager.closedViews.set('tab1', {});
|
||||
expect(viewManager.closedViews.has('tab1')).toBe(true);
|
||||
viewManager.loadView({id: 'server1'}, {id: 'tab1', isOpen: true});
|
||||
expect(viewManager.closedViews.has('tab1')).toBe(false);
|
||||
it('should remove from remove from closedViews when the view is open', () => {
|
||||
viewManager.closedViews.set('view1', {});
|
||||
expect(viewManager.closedViews.has('view1')).toBe(true);
|
||||
viewManager.loadView({id: 'server1'}, {id: 'view1', isOpen: true});
|
||||
expect(viewManager.closedViews.has('view1')).toBe(false);
|
||||
});
|
||||
|
||||
it('should add view to views map and add listeners', () => {
|
||||
viewManager.loadView({id: 'server1'}, {id: 'tab1', isOpen: true}, 'http://server-1.com/subpath');
|
||||
expect(viewManager.views.has('tab1')).toBe(true);
|
||||
viewManager.loadView({id: 'server1'}, {id: 'view1', isOpen: true}, 'http://server-1.com/subpath');
|
||||
expect(viewManager.views.has('view1')).toBe(true);
|
||||
expect(onceFn).toHaveBeenCalledWith(LOAD_SUCCESS, viewManager.activateView);
|
||||
expect(loadFn).toHaveBeenCalledWith('http://server-1.com/subpath');
|
||||
});
|
||||
@@ -173,14 +170,14 @@ describe('main/views/viewManager', () => {
|
||||
const onceFn = jest.fn();
|
||||
const loadFn = jest.fn();
|
||||
const destroyFn = jest.fn();
|
||||
MattermostView.mockImplementation((tab) => ({
|
||||
MattermostBrowserView.mockImplementation((view) => ({
|
||||
on: jest.fn(),
|
||||
load: loadFn,
|
||||
once: onceFn,
|
||||
destroy: destroyFn,
|
||||
id: tab.id,
|
||||
id: view.id,
|
||||
updateServerInfo: jest.fn(),
|
||||
tab,
|
||||
view,
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -193,45 +190,45 @@ describe('main/views/viewManager', () => {
|
||||
|
||||
it('should recycle existing views', () => {
|
||||
const makeSpy = jest.spyOn(viewManager, 'makeView');
|
||||
const view = new MattermostView({
|
||||
id: 'tab1',
|
||||
const view = new MattermostBrowserView({
|
||||
id: 'view1',
|
||||
server: {
|
||||
id: 'server1',
|
||||
},
|
||||
});
|
||||
viewManager.views.set('tab1', view);
|
||||
viewManager.views.set('view1', view);
|
||||
ServerManager.getAllServers.mockReturnValue([{
|
||||
id: 'server1',
|
||||
url: new URL('http://server1.com'),
|
||||
}]);
|
||||
ServerManager.getOrderedTabsForServer.mockReturnValue([
|
||||
{
|
||||
id: 'tab1',
|
||||
id: 'view1',
|
||||
isOpen: true,
|
||||
},
|
||||
]);
|
||||
viewManager.handleReloadConfiguration();
|
||||
expect(viewManager.views.get('tab1')).toBe(view);
|
||||
expect(viewManager.views.get('view1')).toBe(view);
|
||||
expect(makeSpy).not.toHaveBeenCalled();
|
||||
makeSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('should close tabs that arent open', () => {
|
||||
it('should close views that arent open', () => {
|
||||
ServerManager.getAllServers.mockReturnValue([{
|
||||
id: 'server1',
|
||||
url: new URL('http://server1.com'),
|
||||
}]);
|
||||
ServerManager.getOrderedTabsForServer.mockReturnValue([
|
||||
{
|
||||
id: 'tab1',
|
||||
id: 'view1',
|
||||
isOpen: false,
|
||||
},
|
||||
]);
|
||||
viewManager.handleReloadConfiguration();
|
||||
expect(viewManager.closedViews.has('tab1')).toBe(true);
|
||||
expect(viewManager.closedViews.has('view1')).toBe(true);
|
||||
});
|
||||
|
||||
it('should create new views for new tabs', () => {
|
||||
it('should create new views for new views', () => {
|
||||
const makeSpy = jest.spyOn(viewManager, 'makeView');
|
||||
ServerManager.getAllServers.mockReturnValue([{
|
||||
id: 'server1',
|
||||
@@ -240,10 +237,10 @@ describe('main/views/viewManager', () => {
|
||||
}]);
|
||||
ServerManager.getOrderedTabsForServer.mockReturnValue([
|
||||
{
|
||||
id: 'tab1',
|
||||
name: 'tab1',
|
||||
id: 'view1',
|
||||
name: 'view1',
|
||||
isOpen: true,
|
||||
url: new URL('http://server1.com/tab'),
|
||||
url: new URL('http://server1.com/view'),
|
||||
},
|
||||
]);
|
||||
viewManager.handleReloadConfiguration();
|
||||
@@ -254,10 +251,10 @@ describe('main/views/viewManager', () => {
|
||||
url: new URL('http://server1.com'),
|
||||
},
|
||||
{
|
||||
id: 'tab1',
|
||||
name: 'tab1',
|
||||
id: 'view1',
|
||||
name: 'view1',
|
||||
isOpen: true,
|
||||
url: new URL('http://server1.com/tab'),
|
||||
url: new URL('http://server1.com/view'),
|
||||
},
|
||||
);
|
||||
makeSpy.mockRestore();
|
||||
@@ -265,27 +262,27 @@ describe('main/views/viewManager', () => {
|
||||
|
||||
it('should set focus to current view on reload', () => {
|
||||
const view = {
|
||||
id: 'tab1',
|
||||
tab: {
|
||||
id: 'view1',
|
||||
view: {
|
||||
server: {
|
||||
id: 'server-1',
|
||||
},
|
||||
id: 'tab1',
|
||||
id: 'view1',
|
||||
url: new URL('http://server1.com'),
|
||||
},
|
||||
destroy: jest.fn(),
|
||||
updateServerInfo: jest.fn(),
|
||||
focus: jest.fn(),
|
||||
};
|
||||
viewManager.currentView = 'tab1';
|
||||
viewManager.views.set('tab1', view);
|
||||
viewManager.currentView = 'view1';
|
||||
viewManager.views.set('view1', view);
|
||||
ServerManager.getAllServers.mockReturnValue([{
|
||||
id: 'server1',
|
||||
url: new URL('http://server1.com'),
|
||||
}]);
|
||||
ServerManager.getOrderedTabsForServer.mockReturnValue([
|
||||
{
|
||||
id: 'tab1',
|
||||
id: 'view1',
|
||||
isOpen: true,
|
||||
},
|
||||
]);
|
||||
@@ -295,23 +292,23 @@ describe('main/views/viewManager', () => {
|
||||
|
||||
it('should show initial if currentView has been removed', () => {
|
||||
const view = {
|
||||
id: 'tab1',
|
||||
tab: {
|
||||
id: 'tab1',
|
||||
id: 'view1',
|
||||
view: {
|
||||
id: 'view1',
|
||||
url: new URL('http://server1.com'),
|
||||
},
|
||||
destroy: jest.fn(),
|
||||
updateServerInfo: jest.fn(),
|
||||
};
|
||||
viewManager.currentView = 'tab1';
|
||||
viewManager.views.set('tab1', view);
|
||||
viewManager.currentView = 'view1';
|
||||
viewManager.views.set('view1', view);
|
||||
ServerManager.getAllServers.mockReturnValue([{
|
||||
id: 'server2',
|
||||
url: new URL('http://server2.com'),
|
||||
}]);
|
||||
ServerManager.getOrderedTabsForServer.mockReturnValue([
|
||||
{
|
||||
id: 'tab1',
|
||||
id: 'view1',
|
||||
isOpen: false,
|
||||
},
|
||||
]);
|
||||
@@ -321,21 +318,21 @@ describe('main/views/viewManager', () => {
|
||||
|
||||
it('should remove unused views', () => {
|
||||
const view = {
|
||||
name: 'tab1',
|
||||
tab: {
|
||||
name: 'tab1',
|
||||
name: 'view1',
|
||||
view: {
|
||||
name: 'view1',
|
||||
url: new URL('http://server1.com'),
|
||||
},
|
||||
destroy: jest.fn(),
|
||||
};
|
||||
viewManager.views.set('tab1', view);
|
||||
viewManager.views.set('view1', view);
|
||||
ServerManager.getAllServers.mockReturnValue([{
|
||||
id: 'server2',
|
||||
url: new URL('http://server2.com'),
|
||||
}]);
|
||||
ServerManager.getOrderedTabsForServer.mockReturnValue([
|
||||
{
|
||||
id: 'tab1',
|
||||
id: 'view1',
|
||||
isOpen: false,
|
||||
},
|
||||
]);
|
||||
@@ -360,11 +357,11 @@ describe('main/views/viewManager', () => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('should show last active tab and server', () => {
|
||||
it('should show last active view and server', () => {
|
||||
ServerManager.getLastActiveServer.mockReturnValue({id: 'server-1'});
|
||||
ServerManager.getLastActiveTabForServer.mockReturnValue({id: 'tab-1'});
|
||||
ServerManager.getLastActiveTabForServer.mockReturnValue({id: 'view-1'});
|
||||
viewManager.showInitial();
|
||||
expect(viewManager.showById).toHaveBeenCalledWith('tab-1');
|
||||
expect(viewManager.showById).toHaveBeenCalledWith('view-1');
|
||||
});
|
||||
|
||||
it('should open new server modal when no servers exist', () => {
|
||||
@@ -385,7 +382,7 @@ describe('main/views/viewManager', () => {
|
||||
order: 0,
|
||||
tabs: [
|
||||
{
|
||||
name: 'tab-messaging',
|
||||
name: 'view-messaging',
|
||||
order: 0,
|
||||
isOpen: true,
|
||||
},
|
||||
@@ -403,9 +400,9 @@ describe('main/views/viewManager', () => {
|
||||
},
|
||||
];
|
||||
const view1 = {
|
||||
id: 'server-1_tab-messaging',
|
||||
id: 'server-1_view-messaging',
|
||||
isLoggedIn: true,
|
||||
tab: {
|
||||
view: {
|
||||
type: TAB_MESSAGING,
|
||||
server: {
|
||||
url: 'http://server-1.com',
|
||||
@@ -416,21 +413,21 @@ describe('main/views/viewManager', () => {
|
||||
const view2 = {
|
||||
...view1,
|
||||
id: 'server-1_other_type_1',
|
||||
tab: {
|
||||
...view1.tab,
|
||||
view: {
|
||||
...view1.view,
|
||||
type: 'other_type_1',
|
||||
},
|
||||
};
|
||||
const view3 = {
|
||||
...view1,
|
||||
id: 'server-1_other_type_2',
|
||||
tab: {
|
||||
...view1.tab,
|
||||
view: {
|
||||
...view1.view,
|
||||
type: 'other_type_2',
|
||||
},
|
||||
};
|
||||
const views = new Map([
|
||||
['server-1_tab-messaging', view1],
|
||||
['server-1_view-messaging', view1],
|
||||
['server-1_other_type_1', view2],
|
||||
]);
|
||||
const closedViews = new Map([
|
||||
@@ -438,7 +435,7 @@ describe('main/views/viewManager', () => {
|
||||
]);
|
||||
viewManager.getView = (viewId) => views.get(viewId);
|
||||
viewManager.isViewClosed = (viewId) => closedViews.has(viewId);
|
||||
viewManager.openClosedTab = jest.fn();
|
||||
viewManager.openClosedView = jest.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
ServerManager.getAllServers.mockReturnValue(servers);
|
||||
@@ -451,24 +448,24 @@ describe('main/views/viewManager', () => {
|
||||
});
|
||||
|
||||
it('should open closed view if pushing to it', () => {
|
||||
viewManager.openClosedTab.mockImplementation((name) => {
|
||||
viewManager.openClosedView.mockImplementation((name) => {
|
||||
const view = closedViews.get(name);
|
||||
closedViews.delete(name);
|
||||
views.set(name, view);
|
||||
});
|
||||
ServerManager.lookupTabByURL.mockReturnValue({id: 'server-1_other_type_2'});
|
||||
viewManager.handleBrowserHistoryPush(null, 'server-1_tab-messaging', '/other_type_2/subpath');
|
||||
expect(viewManager.openClosedTab).toBeCalledWith('server-1_other_type_2', 'http://server-1.com/other_type_2/subpath');
|
||||
ServerManager.lookupViewByURL.mockReturnValue({id: 'server-1_other_type_2'});
|
||||
viewManager.handleBrowserHistoryPush(null, 'server-1_view-messaging', '/other_type_2/subpath');
|
||||
expect(viewManager.openClosedView).toBeCalledWith('server-1_other_type_2', 'http://server-1.com/other_type_2/subpath');
|
||||
});
|
||||
|
||||
it('should open redirect view if different from current view', () => {
|
||||
ServerManager.lookupTabByURL.mockReturnValue({id: 'server-1_other_type_1'});
|
||||
viewManager.handleBrowserHistoryPush(null, 'server-1_tab-messaging', '/other_type_1/subpath');
|
||||
ServerManager.lookupViewByURL.mockReturnValue({id: 'server-1_other_type_1'});
|
||||
viewManager.handleBrowserHistoryPush(null, 'server-1_view-messaging', '/other_type_1/subpath');
|
||||
expect(viewManager.showById).toBeCalledWith('server-1_other_type_1');
|
||||
});
|
||||
|
||||
it('should ignore redirects to "/" to Messages from other tabs', () => {
|
||||
ServerManager.lookupTabByURL.mockReturnValue({id: 'server-1_tab-messaging'});
|
||||
it('should ignore redirects to "/" to Messages from other views', () => {
|
||||
ServerManager.lookupViewByURL.mockReturnValue({id: 'server-1_view-messaging'});
|
||||
viewManager.handleBrowserHistoryPush(null, 'server-1_other_type_1', '/');
|
||||
expect(view1.sendToRenderer).not.toBeCalled();
|
||||
});
|
||||
@@ -487,11 +484,11 @@ describe('main/views/viewManager', () => {
|
||||
send: jest.fn(),
|
||||
},
|
||||
},
|
||||
tab: {
|
||||
view: {
|
||||
server: {
|
||||
name: 'server-1',
|
||||
},
|
||||
type: 'tab-1',
|
||||
type: 'view-1',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -510,9 +507,9 @@ describe('main/views/viewManager', () => {
|
||||
...baseView,
|
||||
isVisible: true,
|
||||
};
|
||||
viewManager.views.set('server1-tab1', view);
|
||||
viewManager.views.set('server1-view1', view);
|
||||
|
||||
viewManager.showById('server1-tab1');
|
||||
viewManager.showById('server1-view1');
|
||||
expect(viewManager.currentView).toBeUndefined();
|
||||
expect(view.isReady).not.toBeCalled();
|
||||
expect(view.show).not.toBeCalled();
|
||||
@@ -584,7 +581,7 @@ describe('main/views/viewManager', () => {
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
viewManager.openClosedTab = jest.fn();
|
||||
viewManager.openClosedView = jest.fn();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -594,7 +591,7 @@ describe('main/views/viewManager', () => {
|
||||
});
|
||||
|
||||
it('should load URL into matching view', () => {
|
||||
ServerManager.lookupTabByURL.mockImplementation(() => ({id: 'view1', url: new URL('http://server-1.com/')}));
|
||||
ServerManager.lookupViewByURL.mockImplementation(() => ({id: 'view1', url: new URL('http://server-1.com/')}));
|
||||
const view = {...baseView};
|
||||
viewManager.views.set('view1', view);
|
||||
viewManager.handleDeepLink('mattermost://server-1.com/deep/link?thing=yes');
|
||||
@@ -602,11 +599,11 @@ describe('main/views/viewManager', () => {
|
||||
});
|
||||
|
||||
it('should send the URL to the view if its already loaded on a 6.0 server', () => {
|
||||
ServerManager.lookupTabByURL.mockImplementation(() => ({id: 'view1', url: new URL('http://server-1.com/')}));
|
||||
ServerManager.lookupViewByURL.mockImplementation(() => ({id: 'view1', url: new URL('http://server-1.com/')}));
|
||||
ServerManager.getRemoteInfo.mockReturnValue({serverVersion: '6.0.0'});
|
||||
const view = {
|
||||
...baseView,
|
||||
tab: {
|
||||
view: {
|
||||
server: {
|
||||
url: new URL('http://server-1.com'),
|
||||
},
|
||||
@@ -619,7 +616,7 @@ describe('main/views/viewManager', () => {
|
||||
});
|
||||
|
||||
it('should throw error if view is missing', () => {
|
||||
ServerManager.lookupTabByURL.mockImplementation(() => ({id: 'view1', url: new URL('http://server-1.com/')}));
|
||||
ServerManager.lookupViewByURL.mockImplementation(() => ({id: 'view1', url: new URL('http://server-1.com/')}));
|
||||
const view = {...baseView};
|
||||
viewManager.handleDeepLink('mattermost://server-1.com/deep/link?thing=yes');
|
||||
expect(view.load).not.toHaveBeenCalled();
|
||||
@@ -632,11 +629,11 @@ describe('main/views/viewManager', () => {
|
||||
expect(dialog.showErrorBox).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should reopen closed tab if called upon', () => {
|
||||
ServerManager.lookupTabByURL.mockImplementation(() => ({id: 'view1', url: new URL('http://server-1.com/')}));
|
||||
it('should reopen closed view if called upon', () => {
|
||||
ServerManager.lookupViewByURL.mockImplementation(() => ({id: 'view1', url: new URL('http://server-1.com/')}));
|
||||
viewManager.closedViews.set('view1', {});
|
||||
viewManager.handleDeepLink('mattermost://server-1.com/deep/link?thing=yes');
|
||||
expect(viewManager.openClosedTab).toHaveBeenCalledWith('view1', 'http://server-1.com/deep/link?thing=yes');
|
||||
expect(viewManager.openClosedView).toHaveBeenCalledWith('view1', 'http://server-1.com/deep/link?thing=yes');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -11,7 +11,7 @@ import {
|
||||
LOAD_FAILED,
|
||||
LOADSCREEN_END,
|
||||
SET_ACTIVE_VIEW,
|
||||
OPEN_TAB,
|
||||
OPEN_VIEW,
|
||||
BROWSER_HISTORY_PUSH,
|
||||
UPDATE_URL_VIEW_WIDTH,
|
||||
SERVERS_UPDATE,
|
||||
@@ -33,7 +33,7 @@ import {Logger} from 'common/log';
|
||||
import Utils from 'common/utils/util';
|
||||
import {MattermostServer} from 'common/servers/MattermostServer';
|
||||
import ServerManager from 'common/servers/serverManager';
|
||||
import {TabView, TAB_MESSAGING} from 'common/tabs/TabView';
|
||||
import {MattermostView, TAB_MESSAGING} from 'common/views/View';
|
||||
import {parseURL} from 'common/utils/url';
|
||||
|
||||
import {localizeMessage} from 'main/i18nManager';
|
||||
@@ -41,7 +41,7 @@ import MainWindow from 'main/windows/mainWindow';
|
||||
|
||||
import {getLocalURLString, getLocalPreload, getAdjustedWindowBoundaries, shouldHaveBackBar} from '../utils';
|
||||
|
||||
import {MattermostView} from './MattermostView';
|
||||
import {MattermostBrowserView} from './MattermostBrowserView';
|
||||
import modalManager from './modalManager';
|
||||
import LoadingScreen from './loadingScreen';
|
||||
|
||||
@@ -50,14 +50,14 @@ const URL_VIEW_DURATION = 10 * SECOND;
|
||||
const URL_VIEW_HEIGHT = 20;
|
||||
|
||||
export class ViewManager {
|
||||
private closedViews: Map<string, {srv: MattermostServer; tab: TabView}>;
|
||||
private views: Map<string, MattermostView>;
|
||||
private closedViews: Map<string, {srv: MattermostServer; view: MattermostView}>;
|
||||
private views: Map<string, MattermostBrowserView>;
|
||||
private currentView?: string;
|
||||
|
||||
private urlViewCancel?: () => void;
|
||||
|
||||
constructor() {
|
||||
this.views = new Map(); // keep in mind that this doesn't need to hold server order, only tabs on the renderer need that.
|
||||
this.views = new Map(); // keep in mind that this doesn't need to hold server order, only views on the renderer need that.
|
||||
this.closedViews = new Map();
|
||||
|
||||
MainWindow.on(MAIN_WINDOW_CREATED, this.init);
|
||||
@@ -102,23 +102,23 @@ export class ViewManager {
|
||||
return this.closedViews.has(viewId);
|
||||
}
|
||||
|
||||
showById = (tabId: string) => {
|
||||
this.getViewLogger(tabId).debug('showById', tabId);
|
||||
showById = (viewId: string) => {
|
||||
this.getViewLogger(viewId).debug('showById', viewId);
|
||||
|
||||
const newView = this.views.get(tabId);
|
||||
const newView = this.views.get(viewId);
|
||||
if (newView) {
|
||||
if (newView.isVisible) {
|
||||
return;
|
||||
}
|
||||
let hidePrevious;
|
||||
if (this.currentView && this.currentView !== tabId) {
|
||||
if (this.currentView && this.currentView !== viewId) {
|
||||
const previous = this.getCurrentView();
|
||||
if (previous) {
|
||||
hidePrevious = () => previous.hide();
|
||||
}
|
||||
}
|
||||
|
||||
this.currentView = tabId;
|
||||
this.currentView = viewId;
|
||||
if (!newView.isErrored()) {
|
||||
newView.show();
|
||||
if (newView.needsLoadingScreen()) {
|
||||
@@ -126,10 +126,10 @@ export class ViewManager {
|
||||
}
|
||||
}
|
||||
hidePrevious?.();
|
||||
MainWindow.get()?.webContents.send(SET_ACTIVE_VIEW, newView.tab.server.id, newView.tab.id);
|
||||
ServerManager.updateLastActive(newView.tab.id);
|
||||
MainWindow.get()?.webContents.send(SET_ACTIVE_VIEW, newView.view.server.id, newView.view.id);
|
||||
ServerManager.updateLastActive(newView.view.id);
|
||||
} else {
|
||||
this.getViewLogger(tabId).warn(`Couldn't find a view with name: ${tabId}`);
|
||||
this.getViewLogger(viewId).warn(`Couldn't find a view with name: ${viewId}`);
|
||||
}
|
||||
modalManager.showModal();
|
||||
}
|
||||
@@ -175,28 +175,28 @@ export class ViewManager {
|
||||
handleDeepLink = (url: string | URL) => {
|
||||
if (url) {
|
||||
const parsedURL = parseURL(url)!;
|
||||
const tabView = ServerManager.lookupTabByURL(parsedURL, true);
|
||||
if (tabView) {
|
||||
const urlWithSchema = `${tabView.url.origin}${parsedURL.pathname}${parsedURL.search}`;
|
||||
if (this.closedViews.has(tabView.id)) {
|
||||
this.openClosedTab(tabView.id, urlWithSchema);
|
||||
const view = ServerManager.lookupViewByURL(parsedURL, true);
|
||||
if (view) {
|
||||
const urlWithSchema = `${view.url.origin}${parsedURL.pathname}${parsedURL.search}`;
|
||||
if (this.closedViews.has(view.id)) {
|
||||
this.openClosedView(view.id, urlWithSchema);
|
||||
} else {
|
||||
const view = this.views.get(tabView.id);
|
||||
if (!view) {
|
||||
log.error(`Couldn't find a view matching the id ${tabView.id}`);
|
||||
const browserView = this.views.get(view.id);
|
||||
if (!browserView) {
|
||||
log.error(`Couldn't find a view matching the id ${view.id}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (view.isReady() && ServerManager.getRemoteInfo(view.tab.server.id)?.serverVersion && Utils.isVersionGreaterThanOrEqualTo(ServerManager.getRemoteInfo(view.tab.server.id)?.serverVersion ?? '', '6.0.0')) {
|
||||
const pathName = `/${urlWithSchema.replace(view.tab.server.url.toString(), '')}`;
|
||||
view.sendToRenderer(BROWSER_HISTORY_PUSH, pathName);
|
||||
this.deeplinkSuccess(view.id);
|
||||
if (browserView.isReady() && ServerManager.getRemoteInfo(browserView.view.server.id)?.serverVersion && Utils.isVersionGreaterThanOrEqualTo(ServerManager.getRemoteInfo(browserView.view.server.id)?.serverVersion ?? '', '6.0.0')) {
|
||||
const pathName = `/${urlWithSchema.replace(browserView.view.server.url.toString(), '')}`;
|
||||
browserView.sendToRenderer(BROWSER_HISTORY_PUSH, pathName);
|
||||
this.deeplinkSuccess(browserView.id);
|
||||
} else {
|
||||
// attempting to change parsedURL protocol results in it not being modified.
|
||||
view.resetLoadingStatus();
|
||||
view.load(urlWithSchema);
|
||||
view.once(LOAD_SUCCESS, this.deeplinkSuccess);
|
||||
view.once(LOAD_FAILED, this.deeplinkFailed);
|
||||
browserView.resetLoadingStatus();
|
||||
browserView.load(urlWithSchema);
|
||||
browserView.once(LOAD_SUCCESS, this.deeplinkSuccess);
|
||||
browserView.once(LOAD_FAILED, this.deeplinkFailed);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -225,35 +225,35 @@ export class ViewManager {
|
||||
*/
|
||||
|
||||
private loadServer = (server: MattermostServer) => {
|
||||
const tabs = ServerManager.getOrderedTabsForServer(server.id);
|
||||
tabs.forEach((tab) => this.loadView(server, tab));
|
||||
const views = ServerManager.getOrderedTabsForServer(server.id);
|
||||
views.forEach((view) => this.loadView(server, view));
|
||||
}
|
||||
|
||||
private loadView = (srv: MattermostServer, tab: TabView, url?: string) => {
|
||||
if (!tab.isOpen) {
|
||||
this.closedViews.set(tab.id, {srv, tab});
|
||||
private loadView = (srv: MattermostServer, view: MattermostView, url?: string) => {
|
||||
if (!view.isOpen) {
|
||||
this.closedViews.set(view.id, {srv, view});
|
||||
return;
|
||||
}
|
||||
const view = this.makeView(srv, tab, url);
|
||||
this.addView(view);
|
||||
const browserView = this.makeView(srv, view, url);
|
||||
this.addView(browserView);
|
||||
}
|
||||
|
||||
private makeView = (srv: MattermostServer, tab: TabView, url?: string): MattermostView => {
|
||||
private makeView = (srv: MattermostServer, view: MattermostView, url?: string): MattermostBrowserView => {
|
||||
const mainWindow = MainWindow.get();
|
||||
if (!mainWindow) {
|
||||
throw new Error('Cannot create view, no main window present');
|
||||
}
|
||||
|
||||
const view = new MattermostView(tab, {webPreferences: {spellcheck: Config.useSpellChecker}});
|
||||
view.once(LOAD_SUCCESS, this.activateView);
|
||||
view.on(LOADSCREEN_END, this.finishLoading);
|
||||
view.on(LOAD_FAILED, this.failLoading);
|
||||
view.on(UPDATE_TARGET_URL, this.showURLView);
|
||||
view.load(url);
|
||||
return view;
|
||||
const browserView = new MattermostBrowserView(view, {webPreferences: {spellcheck: Config.useSpellChecker}});
|
||||
browserView.once(LOAD_SUCCESS, this.activateView);
|
||||
browserView.on(LOADSCREEN_END, this.finishLoading);
|
||||
browserView.on(LOAD_FAILED, this.failLoading);
|
||||
browserView.on(UPDATE_TARGET_URL, this.showURLView);
|
||||
browserView.load(url);
|
||||
return browserView;
|
||||
}
|
||||
|
||||
private addView = (view: MattermostView): void => {
|
||||
private addView = (view: MattermostBrowserView): void => {
|
||||
this.views.set(view.id, view);
|
||||
if (this.closedViews.has(view.id)) {
|
||||
this.closedViews.delete(view.id);
|
||||
@@ -265,8 +265,8 @@ export class ViewManager {
|
||||
|
||||
if (ServerManager.hasServers()) {
|
||||
const lastActiveServer = ServerManager.getCurrentServer();
|
||||
const lastActiveTab = ServerManager.getLastActiveTabForServer(lastActiveServer.id);
|
||||
this.showById(lastActiveTab.id);
|
||||
const lastActiveView = ServerManager.getLastActiveTabForServer(lastActiveServer.id);
|
||||
this.showById(lastActiveView.id);
|
||||
} else {
|
||||
MainWindow.get()?.webContents.send(SET_ACTIVE_VIEW);
|
||||
}
|
||||
@@ -382,33 +382,33 @@ export class ViewManager {
|
||||
*/
|
||||
|
||||
/** Called when a new configuration is received
|
||||
* Servers or tabs have been added or edited. We need to
|
||||
* close, open, or reload tabs, taking care to reuse tabs and
|
||||
* preserve focus on the currently selected tab. */
|
||||
* Servers or views have been added or edited. We need to
|
||||
* close, open, or reload views, taking care to reuse views and
|
||||
* preserve focus on the currently selected view. */
|
||||
private handleReloadConfiguration = () => {
|
||||
log.debug('handleReloadConfiguration');
|
||||
|
||||
const currentTabId: string | undefined = this.views.get(this.currentView as string)?.tab.id;
|
||||
const currentViewId: string | undefined = this.views.get(this.currentView as string)?.view.id;
|
||||
|
||||
const current: Map<string, MattermostView> = new Map();
|
||||
const current: Map<string, MattermostBrowserView> = new Map();
|
||||
for (const view of this.views.values()) {
|
||||
current.set(view.tab.id, view);
|
||||
current.set(view.view.id, view);
|
||||
}
|
||||
|
||||
const views: Map<string, MattermostView> = new Map();
|
||||
const closed: Map<string, {srv: MattermostServer; tab: TabView}> = new Map();
|
||||
const views: Map<string, MattermostBrowserView> = new Map();
|
||||
const closed: Map<string, {srv: MattermostServer; view: MattermostView}> = new Map();
|
||||
|
||||
const sortedTabs = ServerManager.getAllServers().flatMap((x) => ServerManager.getOrderedTabsForServer(x.id).
|
||||
map((t): [MattermostServer, TabView] => [x, t]));
|
||||
const sortedViews = ServerManager.getAllServers().flatMap((x) => ServerManager.getOrderedTabsForServer(x.id).
|
||||
map((t): [MattermostServer, MattermostView] => [x, t]));
|
||||
|
||||
for (const [srv, tab] of sortedTabs) {
|
||||
const recycle = current.get(tab.id);
|
||||
if (!tab.isOpen) {
|
||||
closed.set(tab.id, {srv, tab});
|
||||
for (const [srv, view] of sortedViews) {
|
||||
const recycle = current.get(view.id);
|
||||
if (!view.isOpen) {
|
||||
closed.set(view.id, {srv, view});
|
||||
} else if (recycle) {
|
||||
views.set(tab.id, recycle);
|
||||
views.set(view.id, recycle);
|
||||
} else {
|
||||
views.set(tab.id, this.makeView(srv, tab));
|
||||
views.set(view.id, this.makeView(srv, view));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -428,10 +428,10 @@ export class ViewManager {
|
||||
|
||||
// commit closed
|
||||
for (const x of closed.values()) {
|
||||
this.closedViews.set(x.tab.id, {srv: x.srv, tab: x.tab});
|
||||
this.closedViews.set(x.view.id, {srv: x.srv, view: x.view});
|
||||
}
|
||||
|
||||
if ((currentTabId && closed.has(currentTabId)) || (this.currentView && this.closedViews.has(this.currentView))) {
|
||||
if ((currentViewId && closed.has(currentViewId)) || (this.currentView && this.closedViews.has(this.currentView))) {
|
||||
if (ServerManager.hasServers()) {
|
||||
this.currentView = undefined;
|
||||
this.showInitial();
|
||||
@@ -440,13 +440,13 @@ export class ViewManager {
|
||||
}
|
||||
}
|
||||
|
||||
// show the focused tab (or initial)
|
||||
if (currentTabId && views.has(currentTabId)) {
|
||||
const view = views.get(currentTabId);
|
||||
// show the focused view (or initial)
|
||||
if (currentViewId && views.has(currentViewId)) {
|
||||
const view = views.get(currentViewId);
|
||||
if (view && view.id !== this.currentView) {
|
||||
this.currentView = view.id;
|
||||
this.showById(view.id);
|
||||
MainWindow.get()?.webContents.send(SET_ACTIVE_VIEW, view.tab.server.id, view.tab.id);
|
||||
MainWindow.get()?.webContents.send(SET_ACTIVE_VIEW, view.view.server.id, view.view.id);
|
||||
} else {
|
||||
this.focusCurrentView();
|
||||
}
|
||||
@@ -475,17 +475,17 @@ export class ViewManager {
|
||||
return;
|
||||
}
|
||||
let cleanedPathName = pathName;
|
||||
if (currentView.tab.server.url.pathname !== '/' && pathName.startsWith(currentView.tab.server.url.pathname)) {
|
||||
cleanedPathName = pathName.replace(currentView.tab.server.url.pathname, '');
|
||||
if (currentView.view.server.url.pathname !== '/' && pathName.startsWith(currentView.view.server.url.pathname)) {
|
||||
cleanedPathName = pathName.replace(currentView.view.server.url.pathname, '');
|
||||
}
|
||||
const redirectedviewId = ServerManager.lookupTabByURL(`${currentView.tab.server.url.toString().replace(/\/$/, '')}${cleanedPathName}`)?.id || viewId;
|
||||
const redirectedviewId = ServerManager.lookupViewByURL(`${currentView.view.server.url.toString().replace(/\/$/, '')}${cleanedPathName}`)?.id || viewId;
|
||||
if (this.isViewClosed(redirectedviewId)) {
|
||||
// If it's a closed view, just open it and stop
|
||||
this.openClosedTab(redirectedviewId, `${currentView.tab.server.url}${cleanedPathName}`);
|
||||
this.openClosedView(redirectedviewId, `${currentView.view.server.url}${cleanedPathName}`);
|
||||
return;
|
||||
}
|
||||
let redirectedView = this.getView(redirectedviewId) || currentView;
|
||||
if (redirectedView !== currentView && redirectedView?.tab.server.id === ServerManager.getCurrentServer().id && redirectedView?.isLoggedIn) {
|
||||
if (redirectedView !== currentView && redirectedView?.view.server.id === ServerManager.getCurrentServer().id && redirectedView?.isLoggedIn) {
|
||||
log.info('redirecting to a new view', redirectedView?.id || viewId);
|
||||
this.showById(redirectedView?.id || viewId);
|
||||
} else {
|
||||
@@ -493,7 +493,7 @@ export class ViewManager {
|
||||
}
|
||||
|
||||
// Special case check for Channels to not force a redirect to "/", causing a refresh
|
||||
if (!(redirectedView !== currentView && redirectedView?.tab.type === TAB_MESSAGING && cleanedPathName === '/')) {
|
||||
if (!(redirectedView !== currentView && redirectedView?.view.type === TAB_MESSAGING && cleanedPathName === '/')) {
|
||||
redirectedView?.sendToRenderer(BROWSER_HISTORY_PUSH, cleanedPathName);
|
||||
if (redirectedView) {
|
||||
this.handleBrowserHistoryButton(e, redirectedView.id);
|
||||
@@ -547,7 +547,7 @@ export class ViewManager {
|
||||
|
||||
const currentView = this.getCurrentView();
|
||||
if (currentView && currentView.currentURL) {
|
||||
const adjustedBounds = getAdjustedWindowBoundaries(newBounds.width, newBounds.height, shouldHaveBackBar(currentView.tab.url, currentView.currentURL));
|
||||
const adjustedBounds = getAdjustedWindowBoundaries(newBounds.width, newBounds.height, shouldHaveBackBar(currentView.view.url, currentView.currentURL));
|
||||
currentView.setBounds(adjustedBounds);
|
||||
}
|
||||
}
|
||||
@@ -556,21 +556,21 @@ export class ViewManager {
|
||||
* Helper functions
|
||||
*/
|
||||
|
||||
private openClosedTab = (id: string, url?: string) => {
|
||||
private openClosedView = (id: string, url?: string) => {
|
||||
if (!this.closedViews.has(id)) {
|
||||
return;
|
||||
}
|
||||
const {srv, tab} = this.closedViews.get(id)!;
|
||||
tab.isOpen = true;
|
||||
this.loadView(srv, tab, url);
|
||||
const {srv, view} = this.closedViews.get(id)!;
|
||||
view.isOpen = true;
|
||||
this.loadView(srv, view, url);
|
||||
this.showById(id);
|
||||
const view = this.views.get(id)!;
|
||||
view.isVisible = true;
|
||||
view.on(LOAD_SUCCESS, () => {
|
||||
view.isVisible = false;
|
||||
const browserView = this.views.get(id)!;
|
||||
browserView.isVisible = true;
|
||||
browserView.on(LOAD_SUCCESS, () => {
|
||||
browserView.isVisible = false;
|
||||
this.showById(id);
|
||||
});
|
||||
ipcMain.emit(OPEN_TAB, null, tab.id);
|
||||
ipcMain.emit(OPEN_VIEW, null, view.id);
|
||||
}
|
||||
|
||||
private getViewLogger = (viewId: string) => {
|
||||
@@ -585,8 +585,8 @@ export class ViewManager {
|
||||
return {
|
||||
id: view.id,
|
||||
webContentsId: view.webContentsId,
|
||||
serverName: view.tab.server.name,
|
||||
tabType: view.tab.type,
|
||||
serverName: view.view.server.name,
|
||||
viewType: view.view.type,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@@ -81,7 +81,7 @@ export class WebContentsEventManager {
|
||||
return CallsWidgetWindow.getURL();
|
||||
}
|
||||
|
||||
return ViewManager.getViewByWebContentsId(webContentsId)?.tab.server.url;
|
||||
return ViewManager.getViewByWebContentsId(webContentsId)?.view.server.url;
|
||||
}
|
||||
|
||||
private generateWillNavigate = (webContentsId: number) => {
|
||||
@@ -274,7 +274,7 @@ export class WebContentsEventManager {
|
||||
return {action: 'deny'};
|
||||
}
|
||||
|
||||
const otherServerURL = ServerManager.lookupTabByURL(parsedURL);
|
||||
const otherServerURL = ServerManager.lookupViewByURL(parsedURL);
|
||||
if (otherServerURL && isTeamUrl(otherServerURL.server.url, parsedURL, true)) {
|
||||
ViewManager.handleDeepLink(parsedURL);
|
||||
return {action: 'deny'};
|
||||
|
Reference in New Issue
Block a user