[MM-59483] Remove legacy preload and custom login code (#3174)
* Remove legacy preload script code * Remove custom login code * FIx i18n
This commit is contained in:
@@ -85,8 +85,6 @@
|
|||||||
"main.menus.app.view.developerModeDisableContextMenu": "Disable Context Menu",
|
"main.menus.app.view.developerModeDisableContextMenu": "Disable Context Menu",
|
||||||
"main.menus.app.view.developerModeDisableNotificationStorage": "Disable Notification Storage",
|
"main.menus.app.view.developerModeDisableNotificationStorage": "Disable Notification Storage",
|
||||||
"main.menus.app.view.developerModeDisableUserActivityMonitor": "Disable User Activity Monitor",
|
"main.menus.app.view.developerModeDisableUserActivityMonitor": "Disable User Activity Monitor",
|
||||||
"main.menus.app.view.developerModeForceLegacyAPI": "Force Legacy API",
|
|
||||||
"main.menus.app.view.developerModeForceNewAPI": "Force New API",
|
|
||||||
"main.menus.app.view.devToolsAppWrapper": "Developer Tools for Application Wrapper",
|
"main.menus.app.view.devToolsAppWrapper": "Developer Tools for Application Wrapper",
|
||||||
"main.menus.app.view.devToolsCurrentCallWidget": "Developer Tools for Call Widget",
|
"main.menus.app.view.devToolsCurrentCallWidget": "Developer Tools for Call Widget",
|
||||||
"main.menus.app.view.devToolsCurrentServer": "Developer Tools for Current Server",
|
"main.menus.app.view.devToolsCurrentServer": "Developer Tools for Current Server",
|
||||||
@@ -164,7 +162,6 @@
|
|||||||
"renderer.components.errorView.troubleshooting.browserView.canReachFromBrowserWindow": "You can reach <link>{url}</link> from a browser window.",
|
"renderer.components.errorView.troubleshooting.browserView.canReachFromBrowserWindow": "You can reach <link>{url}</link> from a browser window.",
|
||||||
"renderer.components.errorView.troubleshooting.computerIsConnected": "Your computer is connected to the internet.",
|
"renderer.components.errorView.troubleshooting.computerIsConnected": "Your computer is connected to the internet.",
|
||||||
"renderer.components.errorView.troubleshooting.urlIsCorrect.appNameIsCorrect": "The {appName} URL <link>{url}</link> is correct",
|
"renderer.components.errorView.troubleshooting.urlIsCorrect.appNameIsCorrect": "The {appName} URL <link>{url}</link> is correct",
|
||||||
"renderer.components.extraBar.back": "Back",
|
|
||||||
"renderer.components.input.required": "This field is required",
|
"renderer.components.input.required": "This field is required",
|
||||||
"renderer.components.mainPage.contextMenu.ariaLabel": "Context menu",
|
"renderer.components.mainPage.contextMenu.ariaLabel": "Context menu",
|
||||||
"renderer.components.mainPage.titleBar": "{appName}",
|
"renderer.components.mainPage.titleBar": "{appName}",
|
||||||
|
@@ -55,15 +55,11 @@ export const PLAY_SOUND = 'play_sound';
|
|||||||
export const GET_DOWNLOAD_LOCATION = 'get_download_location';
|
export const GET_DOWNLOAD_LOCATION = 'get_download_location';
|
||||||
|
|
||||||
export const UPDATE_MENTIONS = 'update_mentions';
|
export const UPDATE_MENTIONS = 'update_mentions';
|
||||||
export const IS_UNREAD = 'is_unread';
|
|
||||||
export const UNREAD_RESULT = 'unread_result';
|
|
||||||
export const UNREADS_AND_MENTIONS = 'unreads-and-mentions';
|
export const UNREADS_AND_MENTIONS = 'unreads-and-mentions';
|
||||||
export const SESSION_EXPIRED = 'session_expired';
|
export const SESSION_EXPIRED = 'session_expired';
|
||||||
|
|
||||||
export const REACT_APP_INITIALIZED = 'react-app-initialized';
|
export const REACT_APP_INITIALIZED = 'react-app-initialized';
|
||||||
|
|
||||||
export const TOGGLE_BACK_BUTTON = 'toggle-back-button';
|
|
||||||
|
|
||||||
export const SHOW_SETTINGS_WINDOW = 'show-settings-window';
|
export const SHOW_SETTINGS_WINDOW = 'show-settings-window';
|
||||||
|
|
||||||
export const LOADING_SCREEN_ANIMATION_FINISHED = 'loading-screen-animation-finished';
|
export const LOADING_SCREEN_ANIMATION_FINISHED = 'loading-screen-animation-finished';
|
||||||
@@ -93,8 +89,6 @@ export const CHECK_FOR_UPDATES = 'check-for-updates';
|
|||||||
export const NO_UPDATE_AVAILABLE = 'no-update-available';
|
export const NO_UPDATE_AVAILABLE = 'no-update-available';
|
||||||
|
|
||||||
export const BROWSER_HISTORY_PUSH = 'browser-history-push';
|
export const BROWSER_HISTORY_PUSH = 'browser-history-push';
|
||||||
export const APP_LOGGED_IN = 'app-logged-in';
|
|
||||||
export const APP_LOGGED_OUT = 'app-logged-out';
|
|
||||||
export const TAB_LOGIN_CHANGED = 'tab-login-changed';
|
export const TAB_LOGIN_CHANGED = 'tab-login-changed';
|
||||||
|
|
||||||
export const GET_AVAILABLE_SPELL_CHECKER_LANGUAGES = 'get-available-spell-checker-languages';
|
export const GET_AVAILABLE_SPELL_CHECKER_LANGUAGES = 'get-available-spell-checker-languages';
|
||||||
@@ -118,13 +112,11 @@ export const VIEW_FINISHED_RESIZING = 'view-finished-resizing';
|
|||||||
|
|
||||||
// Calls
|
// Calls
|
||||||
export const GET_DESKTOP_SOURCES = 'get-desktop-sources';
|
export const GET_DESKTOP_SOURCES = 'get-desktop-sources';
|
||||||
export const DESKTOP_SOURCES_RESULT = 'desktop-sources-result';
|
|
||||||
export const DESKTOP_SOURCES_MODAL_REQUEST = 'desktop-sources-modal-request';
|
export const DESKTOP_SOURCES_MODAL_REQUEST = 'desktop-sources-modal-request';
|
||||||
export const CALLS_JOIN_CALL = 'calls-join-call';
|
export const CALLS_JOIN_CALL = 'calls-join-call';
|
||||||
export const CALLS_LEAVE_CALL = 'calls-leave-call';
|
export const CALLS_LEAVE_CALL = 'calls-leave-call';
|
||||||
export const CALLS_WIDGET_RESIZE = 'calls-widget-resize';
|
export const CALLS_WIDGET_RESIZE = 'calls-widget-resize';
|
||||||
export const CALLS_WIDGET_SHARE_SCREEN = 'calls-widget-share-screen';
|
export const CALLS_WIDGET_SHARE_SCREEN = 'calls-widget-share-screen';
|
||||||
export const CALLS_WIDGET_CHANNEL_LINK_CLICK = 'calls-widget-channel-link-click';
|
|
||||||
export const CALLS_LINK_CLICK = 'calls-link-click';
|
export const CALLS_LINK_CLICK = 'calls-link-click';
|
||||||
export const CALLS_JOINED_CALL = 'calls-joined-call';
|
export const CALLS_JOINED_CALL = 'calls-joined-call';
|
||||||
export const CALLS_POPOUT_FOCUS = 'calls-popout-focus';
|
export const CALLS_POPOUT_FOCUS = 'calls-popout-focus';
|
||||||
@@ -189,14 +181,10 @@ export const OPEN_WINDOWS_CAMERA_PREFERENCES = 'open-windows-camera-preferences'
|
|||||||
export const OPEN_WINDOWS_MICROPHONE_PREFERENCES = 'open-windows-microphone-preferences';
|
export const OPEN_WINDOWS_MICROPHONE_PREFERENCES = 'open-windows-microphone-preferences';
|
||||||
export const GET_MEDIA_ACCESS_STATUS = 'get-media-access-status';
|
export const GET_MEDIA_ACCESS_STATUS = 'get-media-access-status';
|
||||||
|
|
||||||
// Legacy code remove signal
|
|
||||||
export const LEGACY_OFF = 'legacy-off';
|
|
||||||
|
|
||||||
export const GET_NONCE = 'get-nonce';
|
export const GET_NONCE = 'get-nonce';
|
||||||
|
|
||||||
export const DEVELOPER_MODE_UPDATED = 'developer-mode-updated';
|
export const DEVELOPER_MODE_UPDATED = 'developer-mode-updated';
|
||||||
export const IS_DEVELOPER_MODE_ENABLED = 'is-developer-mode-enabled';
|
export const IS_DEVELOPER_MODE_ENABLED = 'is-developer-mode-enabled';
|
||||||
export const GET_DEVELOPER_MODE_SETTING = 'get-developer-mode-setting';
|
|
||||||
|
|
||||||
export const METRICS_SEND = 'metrics-send';
|
export const METRICS_SEND = 'metrics-send';
|
||||||
export const METRICS_RECEIVE = 'metrics-receive';
|
export const METRICS_RECEIVE = 'metrics-receive';
|
||||||
|
@@ -15,7 +15,6 @@ export const MAX_LOADING_SCREEN_SECONDS = 4 * SECOND;
|
|||||||
|
|
||||||
export const TAB_BAR_HEIGHT = 40;
|
export const TAB_BAR_HEIGHT = 40;
|
||||||
export const TAB_BAR_PADDING = 4;
|
export const TAB_BAR_PADDING = 4;
|
||||||
export const BACK_BAR_HEIGHT = 36;
|
|
||||||
export const THREE_DOT_MENU_WIDTH = 40;
|
export const THREE_DOT_MENU_WIDTH = 40;
|
||||||
export const THREE_DOT_MENU_WIDTH_MAC = 80;
|
export const THREE_DOT_MENU_WIDTH_MAC = 80;
|
||||||
export const MENU_SHADOW_WIDTH = 24;
|
export const MENU_SHADOW_WIDTH = 24;
|
||||||
@@ -55,20 +54,6 @@ export const URLValidationStatus = {
|
|||||||
URLUpdated: 'URL_UPDATED',
|
URLUpdated: 'URL_UPDATED',
|
||||||
};
|
};
|
||||||
|
|
||||||
// supported custom login paths (oath, saml)
|
|
||||||
export const customLoginRegexPaths = [
|
|
||||||
/^\/oauth\/authorize$/i,
|
|
||||||
/^\/oauth\/deauthorize$/i,
|
|
||||||
/^\/oauth\/access_token$/i,
|
|
||||||
/^\/oauth\/[A-Za-z0-9]+\/complete$/i,
|
|
||||||
/^\/oauth\/[A-Za-z0-9]+\/login$/i,
|
|
||||||
/^\/oauth\/[A-Za-z0-9]+\/signup$/i,
|
|
||||||
/^\/api\/v3\/oauth\/[A-Za-z0-9]+\/complete$/i,
|
|
||||||
/^\/signup\/[A-Za-z0-9]+\/complete$/i,
|
|
||||||
/^\/login\/[A-Za-z0-9]+\/complete$/i,
|
|
||||||
/^\/login\/sso\/saml$/i,
|
|
||||||
];
|
|
||||||
|
|
||||||
export const nonTeamUrlPaths = [
|
export const nonTeamUrlPaths = [
|
||||||
'plugins',
|
'plugins',
|
||||||
'signup',
|
'signup',
|
||||||
|
@@ -9,7 +9,6 @@ import {
|
|||||||
isValidURI,
|
isValidURI,
|
||||||
parseURL,
|
parseURL,
|
||||||
isInternalURL,
|
isInternalURL,
|
||||||
isCustomLoginURL,
|
|
||||||
isCallsPopOutURL,
|
isCallsPopOutURL,
|
||||||
isTrustedURL,
|
isTrustedURL,
|
||||||
} from 'common/utils/url';
|
} from 'common/utils/url';
|
||||||
@@ -204,45 +203,6 @@ describe('common/utils/url', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('isCustomLoginURL', () => {
|
|
||||||
it('should match correct URL', () => {
|
|
||||||
expect(isCustomLoginURL(
|
|
||||||
new URL('http://server.com/oauth/authorize'),
|
|
||||||
new URL('http://server.com'),
|
|
||||||
)).toBe(true);
|
|
||||||
});
|
|
||||||
it('should not match incorrect URL', () => {
|
|
||||||
expect(isCustomLoginURL(
|
|
||||||
new URL('http://server.com/oauth/notauthorize'),
|
|
||||||
new URL('http://server.com'),
|
|
||||||
)).toBe(false);
|
|
||||||
});
|
|
||||||
it('should not match base URL', () => {
|
|
||||||
expect(isCustomLoginURL(
|
|
||||||
new URL('http://server.com/'),
|
|
||||||
new URL('http://server.com'),
|
|
||||||
)).toBe(false);
|
|
||||||
});
|
|
||||||
it('should match with subpath', () => {
|
|
||||||
expect(isCustomLoginURL(
|
|
||||||
new URL('http://server.com/subpath/oauth/authorize'),
|
|
||||||
new URL('http://server.com/subpath'),
|
|
||||||
)).toBe(true);
|
|
||||||
});
|
|
||||||
it('should not match with different subpath', () => {
|
|
||||||
expect(isCustomLoginURL(
|
|
||||||
new URL('http://server.com/subpath/oauth/authorize'),
|
|
||||||
new URL('http://server.com/different/subpath'),
|
|
||||||
)).toBe(false);
|
|
||||||
});
|
|
||||||
it('should not match with oauth subpath', () => {
|
|
||||||
expect(isCustomLoginURL(
|
|
||||||
new URL('http://server.com/oauth/authorize'),
|
|
||||||
new URL('http://server.com/oauth/authorize'),
|
|
||||||
)).toBe(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('isCallsPopOutURL', () => {
|
describe('isCallsPopOutURL', () => {
|
||||||
it('should match correct URL', () => {
|
it('should match correct URL', () => {
|
||||||
expect(isCallsPopOutURL(
|
expect(isCallsPopOutURL(
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
import {isHttpsUri, isHttpUri, isUri} from 'valid-url';
|
import {isHttpsUri, isHttpUri, isUri} from 'valid-url';
|
||||||
|
|
||||||
import buildConfig from 'common/config/buildConfig';
|
import buildConfig from 'common/config/buildConfig';
|
||||||
import {customLoginRegexPaths, nonTeamUrlPaths, CALLS_PLUGIN_ID} from 'common/utils/constants';
|
import {nonTeamUrlPaths, CALLS_PLUGIN_ID} from 'common/utils/constants';
|
||||||
|
|
||||||
export const getFormattedPathName = (pn: string) => (pn.endsWith('/') ? pn : `${pn}/`);
|
export const getFormattedPathName = (pn: string) => (pn.endsWith('/') ? pn : `${pn}/`);
|
||||||
export const parseURL = (inputURL: string | URL) => {
|
export const parseURL = (inputURL: string | URL) => {
|
||||||
@@ -77,22 +77,6 @@ export const isTeamUrl = (serverURL: URL, inputURL: URL, withApi?: boolean) => {
|
|||||||
}
|
}
|
||||||
return !(paths.some((testPath) => isUrlType(testPath, serverURL, inputURL)));
|
return !(paths.some((testPath) => isUrlType(testPath, serverURL, inputURL)));
|
||||||
};
|
};
|
||||||
export const isCustomLoginURL = (inputURL: URL, serverURL: URL) => {
|
|
||||||
if (!isTrustedURL(inputURL, serverURL)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const subpath = serverURL.pathname;
|
|
||||||
const urlPath = inputURL.pathname;
|
|
||||||
const replacement = subpath.endsWith('/') ? '/' : '';
|
|
||||||
const replacedPath = urlPath.replace(subpath, replacement);
|
|
||||||
for (const regexPath of customLoginRegexPaths) {
|
|
||||||
if (replacedPath.match(regexPath)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const isCallsPopOutURL = (serverURL: URL, inputURL: URL, callID: string) => {
|
export const isCallsPopOutURL = (serverURL: URL, inputURL: URL, callID: string) => {
|
||||||
const matches = inputURL.pathname.match(new RegExp(`^${escapeRegExp(getFormattedPathName(serverURL.pathname))}([A-Za-z0-9-_]+)/`, 'i'));
|
const matches = inputURL.pathname.match(new RegExp(`^${escapeRegExp(getFormattedPathName(serverURL.pathname))}([A-Za-z0-9-_]+)/`, 'i'));
|
||||||
|
@@ -14,9 +14,6 @@ jest.mock('common/utils/url', () => {
|
|||||||
isTrustedURL: (url) => {
|
isTrustedURL: (url) => {
|
||||||
return url.toString() === 'http://trustedurl.com/';
|
return url.toString() === 'http://trustedurl.com/';
|
||||||
},
|
},
|
||||||
isCustomLoginURL: (url) => {
|
|
||||||
return url.toString() === 'http://customloginurl.com/';
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -73,13 +70,6 @@ describe('main/authManager', () => {
|
|||||||
expect(authManager.popPermissionModal).not.toBeCalled();
|
expect(authManager.popPermissionModal).not.toBeCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should popLoginModal when isCustomLoginURL', () => {
|
|
||||||
ViewManager.getViewByWebContentsId.mockReturnValue({view: {server: {url: new URL('http://customloginurl.com/')}}});
|
|
||||||
authManager.handleAppLogin({preventDefault: jest.fn()}, {id: 1}, {url: 'http://customloginurl.com/'}, null, jest.fn());
|
|
||||||
expect(authManager.popLoginModal).toBeCalled();
|
|
||||||
expect(authManager.popPermissionModal).not.toBeCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should popLoginModal when has permission', () => {
|
it('should popLoginModal when has permission', () => {
|
||||||
ViewManager.getViewByWebContentsId.mockReturnValue({view: {server: {url: new URL('http://haspermissionurl.com/')}}});
|
ViewManager.getViewByWebContentsId.mockReturnValue({view: {server: {url: new URL('http://haspermissionurl.com/')}}});
|
||||||
authManager.handleAppLogin({preventDefault: jest.fn()}, {id: 1}, {url: 'http://haspermissionurl.com/'}, null, jest.fn());
|
authManager.handleAppLogin({preventDefault: jest.fn()}, {id: 1}, {url: 'http://haspermissionurl.com/'}, null, jest.fn());
|
||||||
|
@@ -4,7 +4,7 @@ import type {AuthenticationResponseDetails, AuthInfo, WebContents, Event} from '
|
|||||||
|
|
||||||
import {Logger} from 'common/log';
|
import {Logger} from 'common/log';
|
||||||
import {BASIC_AUTH_PERMISSION} from 'common/permissions';
|
import {BASIC_AUTH_PERMISSION} from 'common/permissions';
|
||||||
import {isCustomLoginURL, isTrustedURL, parseURL} from 'common/utils/url';
|
import {isTrustedURL, parseURL} from 'common/utils/url';
|
||||||
import TrustedOriginsStore from 'main/trustedOrigins';
|
import TrustedOriginsStore from 'main/trustedOrigins';
|
||||||
import {getLocalPreload} from 'main/utils';
|
import {getLocalPreload} from 'main/utils';
|
||||||
import modalManager from 'main/views/modalManager';
|
import modalManager from 'main/views/modalManager';
|
||||||
@@ -45,7 +45,7 @@ export class AuthManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.loginCallbackMap.set(request.url, callback); // if callback is undefined set it to null instead so we know we have set it up with no value
|
this.loginCallbackMap.set(request.url, callback); // if callback is undefined set it to null instead so we know we have set it up with no value
|
||||||
if (isTrustedURL(parsedURL, serverURL) || isCustomLoginURL(parsedURL, serverURL) || TrustedOriginsStore.checkPermission(parsedURL, BASIC_AUTH_PERMISSION)) {
|
if (isTrustedURL(parsedURL, serverURL) || TrustedOriginsStore.checkPermission(parsedURL, BASIC_AUTH_PERMISSION)) {
|
||||||
this.popLoginModal(request, authInfo);
|
this.popLoginModal(request, authInfo);
|
||||||
} else {
|
} else {
|
||||||
this.popPermissionModal(request, authInfo, BASIC_AUTH_PERMISSION);
|
this.popPermissionModal(request, authInfo, BASIC_AUTH_PERMISSION);
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
import {ipcMain} from 'electron';
|
import {ipcMain} from 'electron';
|
||||||
import {EventEmitter} from 'events';
|
import {EventEmitter} from 'events';
|
||||||
|
|
||||||
import {DEVELOPER_MODE_UPDATED, IS_DEVELOPER_MODE_ENABLED, UPDATE_PATHS, GET_DEVELOPER_MODE_SETTING} from 'common/communication';
|
import {DEVELOPER_MODE_UPDATED, IS_DEVELOPER_MODE_ENABLED, UPDATE_PATHS} from 'common/communication';
|
||||||
import JsonFileManager from 'common/JsonFileManager';
|
import JsonFileManager from 'common/JsonFileManager';
|
||||||
import {developerModeJson} from 'main/constants';
|
import {developerModeJson} from 'main/constants';
|
||||||
|
|
||||||
@@ -18,7 +18,6 @@ export class DeveloperMode extends EventEmitter {
|
|||||||
this.json = new JsonFileManager(file);
|
this.json = new JsonFileManager(file);
|
||||||
|
|
||||||
ipcMain.handle(IS_DEVELOPER_MODE_ENABLED, this.enabled);
|
ipcMain.handle(IS_DEVELOPER_MODE_ENABLED, this.enabled);
|
||||||
ipcMain.handle(GET_DEVELOPER_MODE_SETTING, (_, setting) => this.get(setting));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enabled = () => process.env.MM_DESKTOP_DEVELOPER_MODE === 'true';
|
enabled = () => process.env.MM_DESKTOP_DEVELOPER_MODE === 'true';
|
||||||
|
@@ -214,22 +214,6 @@ export function createTemplate(config: Config, updateManager: UpdateManager) {
|
|||||||
DeveloperMode.toggle('disableContextMenu');
|
DeveloperMode.toggle('disableContextMenu');
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: localizeMessage('main.menus.app.view.developerModeForceLegacyAPI', 'Force Legacy API'),
|
|
||||||
type: 'checkbox' as const,
|
|
||||||
checked: DeveloperMode.get('forceLegacyAPI'),
|
|
||||||
click() {
|
|
||||||
DeveloperMode.toggle('forceLegacyAPI');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: localizeMessage('main.menus.app.view.developerModeForceNewAPI', 'Force New API'),
|
|
||||||
type: 'checkbox' as const,
|
|
||||||
checked: DeveloperMode.get('forceNewAPI'),
|
|
||||||
click() {
|
|
||||||
DeveloperMode.toggle('forceNewAPI');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -8,16 +8,11 @@ import type {DesktopAPI} from '@mattermost/desktop-api';
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
NOTIFY_MENTION,
|
NOTIFY_MENTION,
|
||||||
IS_UNREAD,
|
|
||||||
UNREAD_RESULT,
|
|
||||||
SESSION_EXPIRED,
|
SESSION_EXPIRED,
|
||||||
REACT_APP_INITIALIZED,
|
REACT_APP_INITIALIZED,
|
||||||
USER_ACTIVITY_UPDATE,
|
USER_ACTIVITY_UPDATE,
|
||||||
BROWSER_HISTORY_PUSH,
|
BROWSER_HISTORY_PUSH,
|
||||||
APP_LOGGED_IN,
|
|
||||||
APP_LOGGED_OUT,
|
|
||||||
GET_VIEW_INFO_FOR_TEST,
|
GET_VIEW_INFO_FOR_TEST,
|
||||||
DESKTOP_SOURCES_RESULT,
|
|
||||||
VIEW_FINISHED_RESIZING,
|
VIEW_FINISHED_RESIZING,
|
||||||
CALLS_JOIN_CALL,
|
CALLS_JOIN_CALL,
|
||||||
CALLS_JOINED_CALL,
|
CALLS_JOINED_CALL,
|
||||||
@@ -33,7 +28,6 @@ import {
|
|||||||
BROWSER_HISTORY_STATUS_UPDATED,
|
BROWSER_HISTORY_STATUS_UPDATED,
|
||||||
NOTIFICATION_CLICKED,
|
NOTIFICATION_CLICKED,
|
||||||
CALLS_WIDGET_RESIZE,
|
CALLS_WIDGET_RESIZE,
|
||||||
CALLS_WIDGET_CHANNEL_LINK_CLICK,
|
|
||||||
CALLS_LINK_CLICK,
|
CALLS_LINK_CLICK,
|
||||||
CALLS_POPOUT_FOCUS,
|
CALLS_POPOUT_FOCUS,
|
||||||
CALLS_WIDGET_OPEN_THREAD,
|
CALLS_WIDGET_OPEN_THREAD,
|
||||||
@@ -41,9 +35,7 @@ import {
|
|||||||
CALLS_WIDGET_OPEN_USER_SETTINGS,
|
CALLS_WIDGET_OPEN_USER_SETTINGS,
|
||||||
GET_DESKTOP_SOURCES,
|
GET_DESKTOP_SOURCES,
|
||||||
UNREADS_AND_MENTIONS,
|
UNREADS_AND_MENTIONS,
|
||||||
LEGACY_OFF,
|
|
||||||
TAB_LOGIN_CHANGED,
|
TAB_LOGIN_CHANGED,
|
||||||
GET_DEVELOPER_MODE_SETTING,
|
|
||||||
METRICS_SEND,
|
METRICS_SEND,
|
||||||
METRICS_REQUEST,
|
METRICS_REQUEST,
|
||||||
METRICS_RECEIVE,
|
METRICS_RECEIVE,
|
||||||
@@ -51,35 +43,20 @@ import {
|
|||||||
|
|
||||||
import type {ExternalAPI} from 'types/externalAPI';
|
import type {ExternalAPI} from 'types/externalAPI';
|
||||||
|
|
||||||
let legacyEnabled = false;
|
const createListener: ExternalAPI['createListener'] = (channel: string, listener: (...args: never[]) => void) => {
|
||||||
let legacyOff: () => void;
|
|
||||||
|
|
||||||
ipcRenderer.invoke(GET_DEVELOPER_MODE_SETTING, 'forceLegacyAPI').then((force) => {
|
|
||||||
if (force) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const createListener: ExternalAPI['createListener'] = (channel: string, listener: (...args: never[]) => void) => {
|
|
||||||
const listenerWithEvent = (_: IpcRendererEvent, ...args: unknown[]) =>
|
const listenerWithEvent = (_: IpcRendererEvent, ...args: unknown[]) =>
|
||||||
listener(...args as never[]);
|
listener(...args as never[]);
|
||||||
ipcRenderer.on(channel, listenerWithEvent);
|
ipcRenderer.on(channel, listenerWithEvent);
|
||||||
return () => {
|
return () => {
|
||||||
ipcRenderer.off(channel, listenerWithEvent);
|
ipcRenderer.off(channel, listenerWithEvent);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const desktopAPI: DesktopAPI = {
|
const desktopAPI: DesktopAPI = {
|
||||||
|
|
||||||
// Initialization
|
// Initialization
|
||||||
isDev: () => ipcRenderer.invoke(GET_IS_DEV_MODE),
|
isDev: () => ipcRenderer.invoke(GET_IS_DEV_MODE),
|
||||||
getAppInfo: () => {
|
getAppInfo: () => ipcRenderer.invoke(GET_APP_INFO),
|
||||||
// Using this signal as the sign to disable the legacy code, since it is run before the app is rendered
|
|
||||||
if (legacyEnabled) {
|
|
||||||
legacyOff?.();
|
|
||||||
}
|
|
||||||
|
|
||||||
return ipcRenderer.invoke(GET_APP_INFO);
|
|
||||||
},
|
|
||||||
reactAppInitialized: () => ipcRenderer.send(REACT_APP_INITIALIZED),
|
reactAppInitialized: () => ipcRenderer.send(REACT_APP_INITIALIZED),
|
||||||
|
|
||||||
// Session
|
// Session
|
||||||
@@ -138,9 +115,8 @@ ipcRenderer.invoke(GET_DEVELOPER_MODE_SETTING, 'forceLegacyAPI').then((force) =>
|
|||||||
|
|
||||||
// Utility
|
// Utility
|
||||||
unregister: (channel) => ipcRenderer.removeAllListeners(channel),
|
unregister: (channel) => ipcRenderer.removeAllListeners(channel),
|
||||||
};
|
};
|
||||||
contextBridge.exposeInMainWorld('desktopAPI', desktopAPI);
|
contextBridge.exposeInMainWorld('desktopAPI', desktopAPI);
|
||||||
});
|
|
||||||
|
|
||||||
ipcRenderer.on(METRICS_REQUEST, async (_, name, serverId) => {
|
ipcRenderer.on(METRICS_REQUEST, async (_, name, serverId) => {
|
||||||
const memory = await process.getProcessMemoryInfo();
|
const memory = await process.getProcessMemoryInfo();
|
||||||
@@ -203,319 +179,3 @@ const CLEAR_CACHE_INTERVAL = 6 * 60 * 60 * 1000; // 6 hours
|
|||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
webFrame.clearCache();
|
webFrame.clearCache();
|
||||||
}, CLEAR_CACHE_INTERVAL);
|
}, CLEAR_CACHE_INTERVAL);
|
||||||
|
|
||||||
ipcRenderer.invoke(GET_DEVELOPER_MODE_SETTING, 'forceNewAPI').then((force) => {
|
|
||||||
if (force) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/****************************************************************************
|
|
||||||
* LEGACY CODE BELOW
|
|
||||||
* All of this code is deprecated and should be removed eventually
|
|
||||||
* Current it is there to support older versions of the web app
|
|
||||||
****************************************************************************
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Legacy helper functions
|
|
||||||
*/
|
|
||||||
|
|
||||||
const onLoad = () => {
|
|
||||||
if (document.getElementById('root') === null) {
|
|
||||||
console.warn('The guest is not assumed as mattermost-webapp');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
watchReactAppUntilInitialized(() => {
|
|
||||||
console.warn('Legacy preload initialized');
|
|
||||||
ipcRenderer.send(REACT_APP_INITIALIZED);
|
|
||||||
ipcRenderer.invoke(REQUEST_BROWSER_HISTORY_STATUS).then(sendHistoryButtonReturn);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const onStorageChanged = (e: StorageEvent) => {
|
|
||||||
if (e.key === '__login__' && e.storageArea === localStorage && e.newValue) {
|
|
||||||
ipcRenderer.send(APP_LOGGED_IN);
|
|
||||||
}
|
|
||||||
if (e.key === '__logout__' && e.storageArea === localStorage && e.newValue) {
|
|
||||||
ipcRenderer.send(APP_LOGGED_OUT);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const isReactAppInitialized = () => {
|
|
||||||
const initializedRoot =
|
|
||||||
document.querySelector('#root.channel-view') || // React 16 webapp
|
|
||||||
document.querySelector('#root .signup-team__container') || // React 16 login
|
|
||||||
document.querySelector('div[data-reactroot]'); // Older React apps
|
|
||||||
if (initializedRoot === null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return initializedRoot.children.length !== 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
const watchReactAppUntilInitialized = (callback: () => void) => {
|
|
||||||
let count = 0;
|
|
||||||
const interval = 500;
|
|
||||||
const timeout = 30000;
|
|
||||||
const timer = setInterval(() => {
|
|
||||||
count += interval;
|
|
||||||
if (isReactAppInitialized() || count >= timeout) { // assumed as webapp has been initialized.
|
|
||||||
clearTimeout(timer);
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
}, interval);
|
|
||||||
};
|
|
||||||
|
|
||||||
const checkUnread = () => {
|
|
||||||
if (isReactAppInitialized()) {
|
|
||||||
findUnread();
|
|
||||||
} else {
|
|
||||||
watchReactAppUntilInitialized(() => {
|
|
||||||
findUnread();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const findUnread = () => {
|
|
||||||
const classes = ['team-container unread', 'SidebarChannel unread', 'sidebar-item unread-title'];
|
|
||||||
const isUnread = classes.some((classPair) => {
|
|
||||||
const result = document.getElementsByClassName(classPair);
|
|
||||||
return result && result.length > 0;
|
|
||||||
});
|
|
||||||
ipcRenderer.send(UNREAD_RESULT, isUnread);
|
|
||||||
};
|
|
||||||
|
|
||||||
let sessionExpired: boolean;
|
|
||||||
const getUnreadCount = () => {
|
|
||||||
// LHS not found => Log out => Count should be 0, but session may be expired.
|
|
||||||
let isExpired;
|
|
||||||
if (document.getElementById('sidebar-left') === null) {
|
|
||||||
const extraParam = (new URLSearchParams(window.location.search)).get('extra');
|
|
||||||
isExpired = extraParam === 'expired';
|
|
||||||
} else {
|
|
||||||
isExpired = false;
|
|
||||||
}
|
|
||||||
if (isExpired !== sessionExpired) {
|
|
||||||
sessionExpired = isExpired;
|
|
||||||
ipcRenderer.send(SESSION_EXPIRED, sessionExpired);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Legacy message passing code - can be running alongside the new API stuff
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Disabling no-explicit-any for this legacy code
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
window.addEventListener('message', ({origin, data = {}}: {origin?: string; data?: {type?: string; message?: any}} = {}) => {
|
|
||||||
const {type, message = {}} = data;
|
|
||||||
if (origin !== window.location.origin) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
switch (type) {
|
|
||||||
case 'webapp-ready':
|
|
||||||
case 'get-app-version': {
|
|
||||||
// register with the webapp to enable custom integration functionality
|
|
||||||
ipcRenderer.invoke(GET_APP_INFO).then((info) => {
|
|
||||||
console.log(`registering ${info.name} v${info.version} with the server`);
|
|
||||||
window.postMessage(
|
|
||||||
{
|
|
||||||
type: 'register-desktop',
|
|
||||||
message: info,
|
|
||||||
},
|
|
||||||
window.location.origin || '*',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'dispatch-notification': {
|
|
||||||
const {title, body, channel, teamId, url, silent, data: messageData} = message;
|
|
||||||
channels.set(channel.id, channel);
|
|
||||||
ipcRenderer.invoke(NOTIFY_MENTION, title, body, channel.id, teamId, url, silent, messageData.soundName);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case BROWSER_HISTORY_PUSH: {
|
|
||||||
const {path} = message as {path: string};
|
|
||||||
ipcRenderer.send(BROWSER_HISTORY_PUSH, path);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'history-button': {
|
|
||||||
ipcRenderer.invoke(REQUEST_BROWSER_HISTORY_STATUS).then(sendHistoryButtonReturn);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CALLS_LINK_CLICK: {
|
|
||||||
ipcRenderer.send(CALLS_LINK_CLICK, message.link);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case GET_DESKTOP_SOURCES: {
|
|
||||||
ipcRenderer.invoke(GET_DESKTOP_SOURCES, message).then(sendDesktopSourcesResult);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CALLS_WIDGET_SHARE_SCREEN: {
|
|
||||||
ipcRenderer.send(CALLS_WIDGET_SHARE_SCREEN, message.sourceID, message.withAudio);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CALLS_JOIN_CALL: {
|
|
||||||
ipcRenderer.invoke(CALLS_JOIN_CALL, message).then(sendCallsJoinedCall);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CALLS_JOINED_CALL: {
|
|
||||||
ipcRenderer.send(CALLS_JOINED_CALL, message.callID, message.sessionID);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CALLS_JOIN_REQUEST: {
|
|
||||||
ipcRenderer.send(CALLS_JOIN_REQUEST, message.callID);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CALLS_WIDGET_RESIZE: {
|
|
||||||
ipcRenderer.send(CALLS_WIDGET_RESIZE, message.width, message.height);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CALLS_ERROR: {
|
|
||||||
ipcRenderer.send(CALLS_ERROR, message.err, message.callID, message.errMsg);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CALLS_WIDGET_CHANNEL_LINK_CLICK:
|
|
||||||
case CALLS_LEAVE_CALL:
|
|
||||||
case DESKTOP_SOURCES_MODAL_REQUEST:
|
|
||||||
case CALLS_POPOUT_FOCUS: {
|
|
||||||
ipcRenderer.send(type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Legacy support to hold the full channel object so that it can be used for the click event
|
|
||||||
const channels: Map<string, {id: string}> = new Map();
|
|
||||||
ipcRenderer.on(NOTIFICATION_CLICKED, (event, channelId, teamId, url) => {
|
|
||||||
const channel = channels.get(channelId) ?? {id: channelId};
|
|
||||||
channels.delete(channelId);
|
|
||||||
window.postMessage(
|
|
||||||
{
|
|
||||||
type: NOTIFICATION_CLICKED,
|
|
||||||
message: {
|
|
||||||
channel,
|
|
||||||
teamId,
|
|
||||||
url,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
window.location.origin,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcRenderer.on(BROWSER_HISTORY_PUSH, (event, pathName) => {
|
|
||||||
window.postMessage(
|
|
||||||
{
|
|
||||||
type: 'browser-history-push-return',
|
|
||||||
message: {
|
|
||||||
pathName,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
window.location.origin,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const sendHistoryButtonReturn = (status: {canGoBack: boolean; canGoForward: boolean}) => {
|
|
||||||
window.postMessage(
|
|
||||||
{
|
|
||||||
type: 'history-button-return',
|
|
||||||
message: {
|
|
||||||
enableBack: status.canGoBack,
|
|
||||||
enableForward: status.canGoForward,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
window.location.origin,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
ipcRenderer.on(BROWSER_HISTORY_STATUS_UPDATED, (event, canGoBack, canGoForward) => sendHistoryButtonReturn({canGoBack, canGoForward}));
|
|
||||||
|
|
||||||
const sendDesktopSourcesResult = (sources: Array<{
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
thumbnailURL: string;
|
|
||||||
}>) => {
|
|
||||||
window.postMessage(
|
|
||||||
{
|
|
||||||
type: DESKTOP_SOURCES_RESULT,
|
|
||||||
message: sources,
|
|
||||||
},
|
|
||||||
window.location.origin,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const sendCallsJoinedCall = (message: {callID: string; sessionID: string}) => {
|
|
||||||
window.postMessage(
|
|
||||||
{
|
|
||||||
type: CALLS_JOINED_CALL,
|
|
||||||
message,
|
|
||||||
},
|
|
||||||
window.location.origin,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
ipcRenderer.on(CALLS_JOIN_REQUEST, (_, callID) => {
|
|
||||||
window.postMessage(
|
|
||||||
{
|
|
||||||
type: CALLS_JOIN_REQUEST,
|
|
||||||
message: {callID},
|
|
||||||
},
|
|
||||||
window.location.origin,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcRenderer.on(DESKTOP_SOURCES_MODAL_REQUEST, () => {
|
|
||||||
window.postMessage(
|
|
||||||
{
|
|
||||||
type: DESKTOP_SOURCES_MODAL_REQUEST,
|
|
||||||
},
|
|
||||||
window.location.origin,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcRenderer.on(CALLS_WIDGET_SHARE_SCREEN, (_, sourceID, withAudio) => {
|
|
||||||
window.postMessage(
|
|
||||||
{
|
|
||||||
type: CALLS_WIDGET_SHARE_SCREEN,
|
|
||||||
message: {sourceID, withAudio},
|
|
||||||
},
|
|
||||||
window.location.origin,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcRenderer.on(CALLS_ERROR, (_, err, callID, errMsg) => {
|
|
||||||
window.postMessage(
|
|
||||||
{
|
|
||||||
type: CALLS_ERROR,
|
|
||||||
message: {err, callID, errMsg},
|
|
||||||
},
|
|
||||||
window.location.origin,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// push user activity updates to the webapp
|
|
||||||
ipcRenderer.on(USER_ACTIVITY_UPDATE, (event, userIsActive, isSystemEvent) => {
|
|
||||||
if (window.location.origin !== 'null') {
|
|
||||||
window.postMessage({type: USER_ACTIVITY_UPDATE, message: {userIsActive, manual: isSystemEvent}}, window.location.origin);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Legacy functionality that needs to be disabled with the new API
|
|
||||||
*/
|
|
||||||
|
|
||||||
legacyEnabled = true;
|
|
||||||
ipcRenderer.on(IS_UNREAD, checkUnread);
|
|
||||||
const unreadInterval = setInterval(getUnreadCount, 1000);
|
|
||||||
window.addEventListener('storage', onStorageChanged);
|
|
||||||
window.addEventListener('load', onLoad);
|
|
||||||
|
|
||||||
legacyOff = () => {
|
|
||||||
ipcRenderer.send(LEGACY_OFF);
|
|
||||||
ipcRenderer.off(IS_UNREAD, checkUnread);
|
|
||||||
clearInterval(unreadInterval);
|
|
||||||
window.removeEventListener('storage', onStorageChanged);
|
|
||||||
window.removeEventListener('load', onLoad);
|
|
||||||
|
|
||||||
legacyEnabled = false;
|
|
||||||
console.log('New API preload initialized');
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
@@ -42,7 +42,6 @@ import {
|
|||||||
PLAY_SOUND,
|
PLAY_SOUND,
|
||||||
MODAL_OPEN,
|
MODAL_OPEN,
|
||||||
MODAL_CLOSE,
|
MODAL_CLOSE,
|
||||||
TOGGLE_BACK_BUTTON,
|
|
||||||
UPDATE_MENTIONS,
|
UPDATE_MENTIONS,
|
||||||
SHOW_DOWNLOADS_DROPDOWN_BUTTON_BADGE,
|
SHOW_DOWNLOADS_DROPDOWN_BUTTON_BADGE,
|
||||||
HIDE_DOWNLOADS_DROPDOWN_BUTTON_BADGE,
|
HIDE_DOWNLOADS_DROPDOWN_BUTTON_BADGE,
|
||||||
@@ -163,7 +162,6 @@ contextBridge.exposeInMainWorld('desktop', {
|
|||||||
onPlaySound: (listener) => ipcRenderer.on(PLAY_SOUND, (_, soundName) => listener(soundName)),
|
onPlaySound: (listener) => ipcRenderer.on(PLAY_SOUND, (_, soundName) => listener(soundName)),
|
||||||
onModalOpen: (listener) => ipcRenderer.on(MODAL_OPEN, () => listener()),
|
onModalOpen: (listener) => ipcRenderer.on(MODAL_OPEN, () => listener()),
|
||||||
onModalClose: (listener) => ipcRenderer.on(MODAL_CLOSE, () => listener()),
|
onModalClose: (listener) => ipcRenderer.on(MODAL_CLOSE, () => listener()),
|
||||||
onToggleBackButton: (listener) => ipcRenderer.on(TOGGLE_BACK_BUTTON, (_, showExtraBar) => listener(showExtraBar)),
|
|
||||||
onUpdateMentions: (listener) => ipcRenderer.on(UPDATE_MENTIONS, (_event, view, mentions, unreads, isExpired) => listener(view, mentions, unreads, isExpired)),
|
onUpdateMentions: (listener) => ipcRenderer.on(UPDATE_MENTIONS, (_event, view, mentions, unreads, isExpired) => listener(view, mentions, unreads, isExpired)),
|
||||||
onCloseServersDropdown: (listener) => ipcRenderer.on(CLOSE_SERVERS_DROPDOWN, () => listener()),
|
onCloseServersDropdown: (listener) => ipcRenderer.on(CLOSE_SERVERS_DROPDOWN, () => listener()),
|
||||||
onOpenServersDropdown: (listener) => ipcRenderer.on(OPEN_SERVERS_DROPDOWN, () => listener()),
|
onOpenServersDropdown: (listener) => ipcRenderer.on(OPEN_SERVERS_DROPDOWN, () => listener()),
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
import {BACK_BAR_HEIGHT, TAB_BAR_HEIGHT} from 'common/utils/constants';
|
import {TAB_BAR_HEIGHT} from 'common/utils/constants';
|
||||||
|
|
||||||
import * as Utils from './utils';
|
import * as Utils from './utils';
|
||||||
|
|
||||||
@@ -68,27 +68,6 @@ describe('main/utils', () => {
|
|||||||
height: 400 - TAB_BAR_HEIGHT,
|
height: 400 - TAB_BAR_HEIGHT,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should include back bar height when specified', () => {
|
|
||||||
expect(Utils.getWindowBoundaries({
|
|
||||||
getContentBounds: () => ({width: 500, height: 400}),
|
|
||||||
}, true)).toStrictEqual({
|
|
||||||
x: 0,
|
|
||||||
y: TAB_BAR_HEIGHT + BACK_BAR_HEIGHT,
|
|
||||||
width: 500,
|
|
||||||
height: 400 - TAB_BAR_HEIGHT - BACK_BAR_HEIGHT,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('shouldHaveBackBar', () => {
|
|
||||||
it('should have back bar for custom logins', () => {
|
|
||||||
expect(Utils.shouldHaveBackBar(new URL('https://server-1.com'), new URL('https://server-1.com/login/sso/saml'))).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not have back bar for regular login', () => {
|
|
||||||
expect(Utils.shouldHaveBackBar(new URL('https://server-1.com'), new URL('https://server-1.com/login'))).toBe(false);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('isStringWithLength', () => {
|
describe('isStringWithLength', () => {
|
||||||
|
@@ -11,8 +11,7 @@ const exec = promisify(execOriginal);
|
|||||||
import type {BrowserWindow} from 'electron';
|
import type {BrowserWindow} from 'electron';
|
||||||
import {app} from 'electron';
|
import {app} from 'electron';
|
||||||
|
|
||||||
import {BACK_BAR_HEIGHT, customLoginRegexPaths, TAB_BAR_HEIGHT} from 'common/utils/constants';
|
import {TAB_BAR_HEIGHT} from 'common/utils/constants';
|
||||||
import {isAdminUrl, isPluginUrl, isTeamUrl, isUrlType, parseURL} from 'common/utils/url';
|
|
||||||
|
|
||||||
import type {Args} from 'types/args';
|
import type {Args} from 'types/args';
|
||||||
|
|
||||||
@@ -48,42 +47,20 @@ export function shouldBeHiddenOnStartup(parsedArgv: Args) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getWindowBoundaries(win: BrowserWindow, hasBackBar = false) {
|
export function getWindowBoundaries(win: BrowserWindow) {
|
||||||
const {width, height} = win.getContentBounds();
|
const {width, height} = win.getContentBounds();
|
||||||
return getAdjustedWindowBoundaries(width, height, hasBackBar);
|
return getAdjustedWindowBoundaries(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getAdjustedWindowBoundaries(width: number, height: number, hasBackBar = false) {
|
export function getAdjustedWindowBoundaries(width: number, height: number) {
|
||||||
return {
|
return {
|
||||||
x: 0,
|
x: 0,
|
||||||
y: TAB_BAR_HEIGHT + (hasBackBar ? BACK_BAR_HEIGHT : 0),
|
y: TAB_BAR_HEIGHT,
|
||||||
width,
|
width,
|
||||||
height: height - TAB_BAR_HEIGHT - (hasBackBar ? BACK_BAR_HEIGHT : 0),
|
height: height - TAB_BAR_HEIGHT,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function shouldHaveBackBar(serverUrl: URL, inputURL: URL) {
|
|
||||||
if (isUrlType('login', serverUrl, inputURL)) {
|
|
||||||
const serverURL = parseURL(serverUrl);
|
|
||||||
const subpath = serverURL ? serverURL.pathname : '';
|
|
||||||
const parsedURL = parseURL(inputURL);
|
|
||||||
if (!parsedURL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const urlPath = parsedURL.pathname;
|
|
||||||
const replacement = subpath.endsWith('/') ? '/' : '';
|
|
||||||
const replacedPath = urlPath.replace(subpath, replacement);
|
|
||||||
for (const regexPath of customLoginRegexPaths) {
|
|
||||||
if (replacedPath.match(regexPath)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return !isTeamUrl(serverUrl, inputURL) && !isAdminUrl(serverUrl, inputURL) && !isPluginUrl(serverUrl, inputURL);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getLocalPreload(file: string) {
|
export function getLocalPreload(file: string) {
|
||||||
return path.join(app.getAppPath(), file);
|
return path.join(app.getAppPath(), file);
|
||||||
}
|
}
|
||||||
|
@@ -4,14 +4,13 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import AppState from 'common/appState';
|
import AppState from 'common/appState';
|
||||||
import {LOAD_FAILED, TOGGLE_BACK_BUTTON, UPDATE_TARGET_URL} from 'common/communication';
|
import {LOAD_FAILED, UPDATE_TARGET_URL} from 'common/communication';
|
||||||
import {MattermostServer} from 'common/servers/MattermostServer';
|
import {MattermostServer} from 'common/servers/MattermostServer';
|
||||||
import MessagingView from 'common/views/MessagingView';
|
import MessagingView from 'common/views/MessagingView';
|
||||||
|
|
||||||
import {MattermostBrowserView} from './MattermostBrowserView';
|
import {MattermostBrowserView} from './MattermostBrowserView';
|
||||||
|
|
||||||
import ContextMenu from '../contextMenu';
|
import ContextMenu from '../contextMenu';
|
||||||
import Utils from '../utils';
|
|
||||||
import MainWindow from '../windows/mainWindow';
|
import MainWindow from '../windows/mainWindow';
|
||||||
|
|
||||||
jest.mock('electron', () => ({
|
jest.mock('electron', () => ({
|
||||||
@@ -423,28 +422,6 @@ describe('main/views/MattermostBrowserView', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('handleDidNavigate', () => {
|
|
||||||
const window = {on: jest.fn()};
|
|
||||||
const mattermostView = new MattermostBrowserView(view, {}, {});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
MainWindow.get.mockReturnValue(window);
|
|
||||||
mattermostView.setBounds = jest.fn();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should hide back button on internal url', () => {
|
|
||||||
Utils.shouldHaveBackBar.mockReturnValue(false);
|
|
||||||
mattermostView.handleDidNavigate(null, 'http://server-1.com/path/to/channels');
|
|
||||||
expect(MainWindow.sendToRenderer).toHaveBeenCalledWith(TOGGLE_BACK_BUTTON, false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should show back button on external url', () => {
|
|
||||||
Utils.shouldHaveBackBar.mockReturnValue(true);
|
|
||||||
mattermostView.handleDidNavigate(null, 'http://server-2.com/some/other/path');
|
|
||||||
expect(MainWindow.sendToRenderer).toHaveBeenCalledWith(TOGGLE_BACK_BUTTON, true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('handleUpdateTarget', () => {
|
describe('handleUpdateTarget', () => {
|
||||||
const window = {on: jest.fn()};
|
const window = {on: jest.fn()};
|
||||||
const mattermostView = new MattermostBrowserView(view, {}, {});
|
const mattermostView = new MattermostBrowserView(view, {}, {});
|
||||||
@@ -476,18 +453,4 @@ describe('main/views/MattermostBrowserView', () => {
|
|||||||
expect(mattermostView.emit).toHaveBeenCalled();
|
expect(mattermostView.emit).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('updateMentionsFromTitle', () => {
|
|
||||||
const mattermostView = new MattermostBrowserView(view, {}, {});
|
|
||||||
|
|
||||||
it('should parse mentions from title', () => {
|
|
||||||
mattermostView.updateMentionsFromTitle('(7) Mattermost');
|
|
||||||
expect(AppState.updateMentions).toHaveBeenCalledWith(mattermostView.view.id, 7);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should parse unreads from title', () => {
|
|
||||||
mattermostView.updateMentionsFromTitle('* Mattermost');
|
|
||||||
expect(AppState.updateMentions).toHaveBeenCalledWith(mattermostView.view.id, 0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@@ -11,8 +11,6 @@ import {
|
|||||||
LOAD_SUCCESS,
|
LOAD_SUCCESS,
|
||||||
LOAD_FAILED,
|
LOAD_FAILED,
|
||||||
UPDATE_TARGET_URL,
|
UPDATE_TARGET_URL,
|
||||||
IS_UNREAD,
|
|
||||||
TOGGLE_BACK_BUTTON,
|
|
||||||
LOADSCREEN_END,
|
LOADSCREEN_END,
|
||||||
SERVERS_URL_MODIFIED,
|
SERVERS_URL_MODIFIED,
|
||||||
BROWSER_HISTORY_STATUS_UPDATED,
|
BROWSER_HISTORY_STATUS_UPDATED,
|
||||||
@@ -32,7 +30,7 @@ import MainWindow from 'main/windows/mainWindow';
|
|||||||
import WebContentsEventManager from './webContentEvents';
|
import WebContentsEventManager from './webContentEvents';
|
||||||
|
|
||||||
import ContextMenu from '../contextMenu';
|
import ContextMenu from '../contextMenu';
|
||||||
import {getWindowBoundaries, getLocalPreload, composeUserAgent, shouldHaveBackBar} from '../utils';
|
import {getWindowBoundaries, getLocalPreload, composeUserAgent} from '../utils';
|
||||||
|
|
||||||
enum Status {
|
enum Status {
|
||||||
LOADING,
|
LOADING,
|
||||||
@@ -41,9 +39,6 @@ enum Status {
|
|||||||
ERROR = -1,
|
ERROR = -1,
|
||||||
}
|
}
|
||||||
|
|
||||||
const MENTIONS_GROUP = 2;
|
|
||||||
const titleParser = /(\((\d+)\) )?(\* )?/g;
|
|
||||||
|
|
||||||
export class MattermostBrowserView extends EventEmitter {
|
export class MattermostBrowserView extends EventEmitter {
|
||||||
view: MattermostView;
|
view: MattermostView;
|
||||||
isVisible: boolean;
|
isVisible: boolean;
|
||||||
@@ -84,7 +79,6 @@ export class MattermostBrowserView extends EventEmitter {
|
|||||||
this.log.verbose('View created');
|
this.log.verbose('View created');
|
||||||
|
|
||||||
this.browserView.webContents.on('update-target-url', this.handleUpdateTarget);
|
this.browserView.webContents.on('update-target-url', this.handleUpdateTarget);
|
||||||
this.browserView.webContents.on('did-navigate', this.handleDidNavigate);
|
|
||||||
if (process.platform !== 'darwin') {
|
if (process.platform !== 'darwin') {
|
||||||
this.browserView.webContents.on('before-input-event', this.handleInputEvents);
|
this.browserView.webContents.on('before-input-event', this.handleInputEvents);
|
||||||
}
|
}
|
||||||
@@ -95,10 +89,6 @@ export class MattermostBrowserView extends EventEmitter {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Legacy handlers using the title/favicon
|
|
||||||
this.browserView.webContents.on('page-title-updated', this.handleTitleUpdate);
|
|
||||||
this.browserView.webContents.on('page-favicon-updated', this.handleFaviconUpdate);
|
|
||||||
|
|
||||||
WebContentsEventManager.addWebContentsEventListeners(this.browserView.webContents);
|
WebContentsEventManager.addWebContentsEventListeners(this.browserView.webContents);
|
||||||
|
|
||||||
if (!DeveloperMode.get('disableContextMenu')) {
|
if (!DeveloperMode.get('disableContextMenu')) {
|
||||||
@@ -233,7 +223,7 @@ export class MattermostBrowserView extends EventEmitter {
|
|||||||
this.isVisible = true;
|
this.isVisible = true;
|
||||||
mainWindow.addBrowserView(this.browserView);
|
mainWindow.addBrowserView(this.browserView);
|
||||||
mainWindow.setTopBrowserView(this.browserView);
|
mainWindow.setTopBrowserView(this.browserView);
|
||||||
this.setBounds(getWindowBoundaries(mainWindow, shouldHaveBackBar(this.view.url || '', this.currentURL)));
|
this.setBounds(getWindowBoundaries(mainWindow));
|
||||||
if (this.status === Status.READY) {
|
if (this.status === Status.READY) {
|
||||||
this.focus();
|
this.focus();
|
||||||
}
|
}
|
||||||
@@ -280,15 +270,6 @@ export class MattermostBrowserView extends EventEmitter {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Code to turn off the old method of getting unreads
|
|
||||||
* Newer web apps will send the mentions/unreads directly
|
|
||||||
*/
|
|
||||||
offLegacyUnreads = () => {
|
|
||||||
this.browserView.webContents.off('page-title-updated', this.handleTitleUpdate);
|
|
||||||
this.browserView.webContents.off('page-favicon-updated', this.handleFaviconUpdate);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Status hooks
|
* Status hooks
|
||||||
*/
|
*/
|
||||||
@@ -393,41 +374,6 @@ export class MattermostBrowserView extends EventEmitter {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Unreads/mentions handlers
|
|
||||||
*/
|
|
||||||
|
|
||||||
private updateMentionsFromTitle = (title: string) => {
|
|
||||||
const resultsIterator = title.matchAll(titleParser);
|
|
||||||
const results = resultsIterator.next(); // we are only interested in the first set
|
|
||||||
const mentions = (results && results.value && parseInt(results.value[MENTIONS_GROUP], 10)) || 0;
|
|
||||||
|
|
||||||
AppState.updateMentions(this.id, mentions);
|
|
||||||
};
|
|
||||||
|
|
||||||
// if favicon is null, it will affect appState, but won't be memoized
|
|
||||||
private findUnreadState = (favicon: string | null) => {
|
|
||||||
try {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private handleTitleUpdate = (e: Event, title: string) => {
|
|
||||||
this.log.debug('handleTitleUpdate', title);
|
|
||||||
|
|
||||||
this.updateMentionsFromTitle(title);
|
|
||||||
};
|
|
||||||
|
|
||||||
private handleFaviconUpdate = (e: Event, favicons: string[]) => {
|
|
||||||
this.log.silly('handleFaviconUpdate', favicons);
|
|
||||||
|
|
||||||
// if unread state is stored for that favicon, retrieve value.
|
|
||||||
// if not, get related info from preload and store it for future changes
|
|
||||||
this.findUnreadState(favicons[0]);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loading/retry logic
|
* Loading/retry logic
|
||||||
*/
|
*/
|
||||||
@@ -486,16 +432,12 @@ export class MattermostBrowserView extends EventEmitter {
|
|||||||
this.log.verbose(`finished loading ${loadURL}`);
|
this.log.verbose(`finished loading ${loadURL}`);
|
||||||
MainWindow.sendToRenderer(LOAD_SUCCESS, this.id);
|
MainWindow.sendToRenderer(LOAD_SUCCESS, this.id);
|
||||||
this.maxRetries = MAX_SERVER_RETRIES;
|
this.maxRetries = MAX_SERVER_RETRIES;
|
||||||
if (this.status === Status.LOADING) {
|
|
||||||
this.updateMentionsFromTitle(this.browserView.webContents.getTitle());
|
|
||||||
this.findUnreadState(null);
|
|
||||||
}
|
|
||||||
this.status = Status.WAITING_MM;
|
this.status = Status.WAITING_MM;
|
||||||
this.removeLoading = setTimeout(this.setInitialized, MAX_LOADING_SCREEN_SECONDS, true);
|
this.removeLoading = setTimeout(this.setInitialized, MAX_LOADING_SCREEN_SECONDS, true);
|
||||||
this.emit(LOAD_SUCCESS, this.id, loadURL);
|
this.emit(LOAD_SUCCESS, this.id, loadURL);
|
||||||
const mainWindow = MainWindow.get();
|
const mainWindow = MainWindow.get();
|
||||||
if (mainWindow && this.currentURL) {
|
if (mainWindow && this.currentURL) {
|
||||||
this.setBounds(getWindowBoundaries(mainWindow, shouldHaveBackBar(this.view.url || '', this.currentURL)));
|
this.setBounds(getWindowBoundaries(mainWindow));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -504,29 +446,6 @@ export class MattermostBrowserView extends EventEmitter {
|
|||||||
* WebContents event handlers
|
* WebContents event handlers
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private handleDidNavigate = (event: Event, url: string) => {
|
|
||||||
this.log.debug('handleDidNavigate', url);
|
|
||||||
|
|
||||||
const mainWindow = MainWindow.get();
|
|
||||||
if (!mainWindow) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const parsedURL = parseURL(url);
|
|
||||||
if (!parsedURL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldHaveBackBar(this.view.url || '', parsedURL)) {
|
|
||||||
this.setBounds(getWindowBoundaries(mainWindow, true));
|
|
||||||
MainWindow.sendToRenderer(TOGGLE_BACK_BUTTON, true);
|
|
||||||
this.log.debug('show back button');
|
|
||||||
} else {
|
|
||||||
this.setBounds(getWindowBoundaries(mainWindow));
|
|
||||||
MainWindow.sendToRenderer(TOGGLE_BACK_BUTTON, false);
|
|
||||||
this.log.debug('hide back button');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private handleUpdateTarget = (e: Event, url: string) => {
|
private handleUpdateTarget = (e: Event, url: string) => {
|
||||||
this.log.silly('handleUpdateTarget', e, url);
|
this.log.silly('handleUpdateTarget', e, url);
|
||||||
const parsedURL = parseURL(url);
|
const parsedURL = parseURL(url);
|
||||||
|
@@ -18,10 +18,7 @@ import {
|
|||||||
UPDATE_URL_VIEW_WIDTH,
|
UPDATE_URL_VIEW_WIDTH,
|
||||||
SERVERS_UPDATE,
|
SERVERS_UPDATE,
|
||||||
REACT_APP_INITIALIZED,
|
REACT_APP_INITIALIZED,
|
||||||
APP_LOGGED_OUT,
|
|
||||||
APP_LOGGED_IN,
|
|
||||||
RELOAD_CURRENT_VIEW,
|
RELOAD_CURRENT_VIEW,
|
||||||
UNREAD_RESULT,
|
|
||||||
HISTORY,
|
HISTORY,
|
||||||
GET_VIEW_INFO_FOR_TEST,
|
GET_VIEW_INFO_FOR_TEST,
|
||||||
SESSION_EXPIRED,
|
SESSION_EXPIRED,
|
||||||
@@ -31,7 +28,6 @@ import {
|
|||||||
SWITCH_TAB,
|
SWITCH_TAB,
|
||||||
GET_IS_DEV_MODE,
|
GET_IS_DEV_MODE,
|
||||||
REQUEST_BROWSER_HISTORY_STATUS,
|
REQUEST_BROWSER_HISTORY_STATUS,
|
||||||
LEGACY_OFF,
|
|
||||||
UNREADS_AND_MENTIONS,
|
UNREADS_AND_MENTIONS,
|
||||||
TAB_LOGIN_CHANGED,
|
TAB_LOGIN_CHANGED,
|
||||||
DEVELOPER_MODE_UPDATED,
|
DEVELOPER_MODE_UPDATED,
|
||||||
@@ -58,7 +54,7 @@ import LoadingScreen from './loadingScreen';
|
|||||||
import {MattermostBrowserView} from './MattermostBrowserView';
|
import {MattermostBrowserView} from './MattermostBrowserView';
|
||||||
import modalManager from './modalManager';
|
import modalManager from './modalManager';
|
||||||
|
|
||||||
import {getLocalPreload, getAdjustedWindowBoundaries, shouldHaveBackBar} from '../utils';
|
import {getLocalPreload, getAdjustedWindowBoundaries} from '../utils';
|
||||||
|
|
||||||
const log = new Logger('ViewManager');
|
const log = new Logger('ViewManager');
|
||||||
const URL_VIEW_DURATION = 10 * SECOND;
|
const URL_VIEW_DURATION = 10 * SECOND;
|
||||||
@@ -84,14 +80,10 @@ export class ViewManager {
|
|||||||
ipcMain.on(HISTORY, this.handleHistory);
|
ipcMain.on(HISTORY, this.handleHistory);
|
||||||
ipcMain.on(REACT_APP_INITIALIZED, this.handleReactAppInitialized);
|
ipcMain.on(REACT_APP_INITIALIZED, this.handleReactAppInitialized);
|
||||||
ipcMain.on(BROWSER_HISTORY_PUSH, this.handleBrowserHistoryPush);
|
ipcMain.on(BROWSER_HISTORY_PUSH, this.handleBrowserHistoryPush);
|
||||||
ipcMain.on(APP_LOGGED_IN, this.handleAppLoggedIn);
|
|
||||||
ipcMain.on(APP_LOGGED_OUT, this.handleAppLoggedOut);
|
|
||||||
ipcMain.on(TAB_LOGIN_CHANGED, this.handleTabLoginChanged);
|
ipcMain.on(TAB_LOGIN_CHANGED, this.handleTabLoginChanged);
|
||||||
ipcMain.on(RELOAD_CURRENT_VIEW, this.handleReloadCurrentView);
|
ipcMain.on(RELOAD_CURRENT_VIEW, this.handleReloadCurrentView);
|
||||||
ipcMain.on(UNREAD_RESULT, this.handleUnreadChanged);
|
|
||||||
ipcMain.on(UNREADS_AND_MENTIONS, this.handleUnreadsAndMentionsChanged);
|
ipcMain.on(UNREADS_AND_MENTIONS, this.handleUnreadsAndMentionsChanged);
|
||||||
ipcMain.on(SESSION_EXPIRED, this.handleSessionExpired);
|
ipcMain.on(SESSION_EXPIRED, this.handleSessionExpired);
|
||||||
ipcMain.on(LEGACY_OFF, this.handleLegacyOff);
|
|
||||||
|
|
||||||
ipcMain.on(SWITCH_TAB, (event, viewId) => this.showById(viewId));
|
ipcMain.on(SWITCH_TAB, (event, viewId) => this.showById(viewId));
|
||||||
|
|
||||||
@@ -108,7 +100,7 @@ export class ViewManager {
|
|||||||
private handleDeveloperModeUpdated = (json: DeveloperSettings) => {
|
private handleDeveloperModeUpdated = (json: DeveloperSettings) => {
|
||||||
log.debug('handleDeveloperModeUpdated', json);
|
log.debug('handleDeveloperModeUpdated', json);
|
||||||
|
|
||||||
if (['browserOnly', 'disableContextMenu', 'forceLegacyAPI', 'forceNewAPI'].some((key) => Object.hasOwn(json, key))) {
|
if (['browserOnly', 'disableContextMenu'].some((key) => Object.hasOwn(json, key))) {
|
||||||
this.views.forEach((view) => view.destroy());
|
this.views.forEach((view) => view.destroy());
|
||||||
this.views = new Map();
|
this.views = new Map();
|
||||||
this.closedViews = new Map();
|
this.closedViews = new Map();
|
||||||
@@ -504,27 +496,6 @@ export class ViewManager {
|
|||||||
this.getCurrentView()?.goToOffset(offset);
|
this.getCurrentView()?.goToOffset(offset);
|
||||||
};
|
};
|
||||||
|
|
||||||
private handleAppLoggedIn = (event: IpcMainEvent) => {
|
|
||||||
log.debug('handleAppLoggedIn', event.sender.id);
|
|
||||||
const view = this.getViewByWebContentsId(event.sender.id);
|
|
||||||
if (!view) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
view.onLogin(true);
|
|
||||||
flushCookiesStore();
|
|
||||||
};
|
|
||||||
|
|
||||||
private handleAppLoggedOut = (event: IpcMainEvent) => {
|
|
||||||
log.debug('handleAppLoggedOut', event.sender.id);
|
|
||||||
const view = this.getViewByWebContentsId(event.sender.id);
|
|
||||||
if (!view) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
view.onLogin(false);
|
|
||||||
AppState.clear(view.id);
|
|
||||||
flushCookiesStore();
|
|
||||||
};
|
|
||||||
|
|
||||||
private handleTabLoginChanged = (event: IpcMainEvent, loggedIn: boolean) => {
|
private handleTabLoginChanged = (event: IpcMainEvent, loggedIn: boolean) => {
|
||||||
log.debug('handleTabLoggedIn', event.sender.id);
|
log.debug('handleTabLoggedIn', event.sender.id);
|
||||||
const view = this.getViewByWebContentsId(event.sender.id);
|
const view = this.getViewByWebContentsId(event.sender.id);
|
||||||
@@ -605,28 +576,6 @@ export class ViewManager {
|
|||||||
this.showById(view?.id);
|
this.showById(view?.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
private handleLegacyOff = (e: IpcMainEvent) => {
|
|
||||||
log.silly('handleLegacyOff', {webContentsId: e.sender.id});
|
|
||||||
|
|
||||||
const view = this.getViewByWebContentsId(e.sender.id);
|
|
||||||
if (!view) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
view.offLegacyUnreads();
|
|
||||||
};
|
|
||||||
|
|
||||||
// if favicon is null, it means it is the initial load,
|
|
||||||
// so don't memoize as we don't have the favicons and there is no rush to find out.
|
|
||||||
private handleUnreadChanged = (e: IpcMainEvent, result: boolean) => {
|
|
||||||
log.silly('handleUnreadChanged', {webContentsId: e.sender.id, result});
|
|
||||||
|
|
||||||
const view = this.getViewByWebContentsId(e.sender.id);
|
|
||||||
if (!view) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
AppState.updateUnreads(view.id, result);
|
|
||||||
};
|
|
||||||
|
|
||||||
private handleUnreadsAndMentionsChanged = (e: IpcMainEvent, isUnread: boolean, mentionCount: number) => {
|
private handleUnreadsAndMentionsChanged = (e: IpcMainEvent, isUnread: boolean, mentionCount: number) => {
|
||||||
log.silly('handleUnreadsAndMentionsChanged', {webContentsId: e.sender.id, isUnread, mentionCount});
|
log.silly('handleUnreadsAndMentionsChanged', {webContentsId: e.sender.id, isUnread, mentionCount});
|
||||||
|
|
||||||
@@ -653,7 +602,7 @@ export class ViewManager {
|
|||||||
|
|
||||||
const currentView = this.getCurrentView();
|
const currentView = this.getCurrentView();
|
||||||
if (currentView && currentView.currentURL) {
|
if (currentView && currentView.currentURL) {
|
||||||
const adjustedBounds = getAdjustedWindowBoundaries(newBounds.width, newBounds.height, shouldHaveBackBar(currentView.view.url, currentView.currentURL));
|
const adjustedBounds = getAdjustedWindowBoundaries(newBounds.width, newBounds.height);
|
||||||
currentView.setBounds(adjustedBounds);
|
currentView.setBounds(adjustedBounds);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -95,27 +95,11 @@ describe('main/views/webContentsEvents', () => {
|
|||||||
expect(event.preventDefault).not.toBeCalled();
|
expect(event.preventDefault).not.toBeCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow navigation when isCustomLoginURL', () => {
|
|
||||||
willNavigate(event, 'http://server-1.com/oauth/authorize');
|
|
||||||
expect(event.preventDefault).not.toBeCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not allow navigation when isCustomLoginURL is external', () => {
|
|
||||||
willNavigate(event, 'http://loginurl.com/oauth/authorize');
|
|
||||||
expect(event.preventDefault).toBeCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should allow navigation when protocol is mailto', () => {
|
it('should allow navigation when protocol is mailto', () => {
|
||||||
willNavigate(event, 'mailto:test@mattermost.com');
|
willNavigate(event, 'mailto:test@mattermost.com');
|
||||||
expect(event.preventDefault).not.toBeCalled();
|
expect(event.preventDefault).not.toBeCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow navigation when a custom login is in progress', () => {
|
|
||||||
webContentsEventManager.customLogins[1] = {inProgress: true};
|
|
||||||
willNavigate(event, 'http://anyoldurl.com');
|
|
||||||
expect(event.preventDefault).not.toBeCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should allow navigation when it isChannelExportUrl', () => {
|
it('should allow navigation when it isChannelExportUrl', () => {
|
||||||
willNavigate(event, 'http://server-1.com/plugins/com.mattermost.plugin-channel-export/api/v1/export');
|
willNavigate(event, 'http://server-1.com/plugins/com.mattermost.plugin-channel-export/api/v1/export');
|
||||||
expect(event.preventDefault).not.toBeCalled();
|
expect(event.preventDefault).not.toBeCalled();
|
||||||
@@ -127,32 +111,6 @@ describe('main/views/webContentsEvents', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('didStartNavigation', () => {
|
|
||||||
const webContentsEventManager = new WebContentsEventManager();
|
|
||||||
const didStartNavigation = webContentsEventManager.generateDidStartNavigation(1);
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
webContentsEventManager.getServerURLFromWebContentsId = jest.fn().mockImplementation(() => new URL('http://server-1.com'));
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
jest.clearAllMocks();
|
|
||||||
webContentsEventManager.customLogins = {};
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should add custom login entry on custom login URL', () => {
|
|
||||||
webContentsEventManager.customLogins[1] = {inProgress: false};
|
|
||||||
didStartNavigation(event, 'http://server-1.com/oauth/authorize');
|
|
||||||
expect(webContentsEventManager.customLogins[1]).toStrictEqual({inProgress: true});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should remove custom login entry once navigating back to internal URL', () => {
|
|
||||||
webContentsEventManager.customLogins[1] = {inProgress: true};
|
|
||||||
didStartNavigation(event, 'http://server-1.com/subpath');
|
|
||||||
expect(webContentsEventManager.customLogins[1]).toStrictEqual({inProgress: false});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('newWindow', () => {
|
describe('newWindow', () => {
|
||||||
const webContentsEventManager = new WebContentsEventManager();
|
const webContentsEventManager = new WebContentsEventManager();
|
||||||
const newWindow = webContentsEventManager.generateNewWindowListener(1, true);
|
const newWindow = webContentsEventManager.generateNewWindowListener(1, true);
|
||||||
|
@@ -11,7 +11,6 @@ import {
|
|||||||
isAdminUrl,
|
isAdminUrl,
|
||||||
isCallsPopOutURL,
|
isCallsPopOutURL,
|
||||||
isChannelExportUrl,
|
isChannelExportUrl,
|
||||||
isCustomLoginURL,
|
|
||||||
isHelpUrl,
|
isHelpUrl,
|
||||||
isImageProxyUrl,
|
isImageProxyUrl,
|
||||||
isInternalURL,
|
isInternalURL,
|
||||||
@@ -20,11 +19,9 @@ import {
|
|||||||
isPluginUrl,
|
isPluginUrl,
|
||||||
isPublicFilesUrl,
|
isPublicFilesUrl,
|
||||||
isTeamUrl,
|
isTeamUrl,
|
||||||
isTrustedURL,
|
|
||||||
isValidURI,
|
isValidURI,
|
||||||
parseURL,
|
parseURL,
|
||||||
} from 'common/utils/url';
|
} from 'common/utils/url';
|
||||||
import {flushCookiesStore} from 'main/app/utils';
|
|
||||||
import ContextMenu from 'main/contextMenu';
|
import ContextMenu from 'main/contextMenu';
|
||||||
import PluginsPopUpsManager from 'main/views/pluginsPopUps';
|
import PluginsPopUpsManager from 'main/views/pluginsPopUps';
|
||||||
import ViewManager from 'main/views/viewManager';
|
import ViewManager from 'main/views/viewManager';
|
||||||
@@ -36,19 +33,13 @@ import {generateHandleConsoleMessage, isCustomProtocol} from './webContentEvents
|
|||||||
import allowProtocolDialog from '../allowProtocolDialog';
|
import allowProtocolDialog from '../allowProtocolDialog';
|
||||||
import {composeUserAgent} from '../utils';
|
import {composeUserAgent} from '../utils';
|
||||||
|
|
||||||
type CustomLogin = {
|
|
||||||
inProgress: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const log = new Logger('WebContentsEventManager');
|
const log = new Logger('WebContentsEventManager');
|
||||||
|
|
||||||
export class WebContentsEventManager {
|
export class WebContentsEventManager {
|
||||||
customLogins: Record<number, CustomLogin>;
|
|
||||||
listeners: Record<number, () => void>;
|
listeners: Record<number, () => void>;
|
||||||
popupWindow?: {win: BrowserWindow; serverURL?: URL};
|
popupWindow?: {win: BrowserWindow; serverURL?: URL};
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.customLogins = {};
|
|
||||||
this.listeners = {};
|
this.listeners = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,16 +92,9 @@ export class WebContentsEventManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (serverURL && isCustomLoginURL(parsedURL, serverURL)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (parsedURL.protocol === 'mailto:') {
|
if (parsedURL.protocol === 'mailto:') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.customLogins[webContentsId]?.inProgress) {
|
|
||||||
flushCookiesStore();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const callID = CallsWidgetWindow.callID;
|
const callID = CallsWidgetWindow.callID;
|
||||||
if (serverURL && callID && isCallsPopOutURL(serverURL, parsedURL, callID)) {
|
if (serverURL && callID && isCallsPopOutURL(serverURL, parsedURL, callID)) {
|
||||||
@@ -122,25 +106,6 @@ export class WebContentsEventManager {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
private generateDidStartNavigation = (webContentsId: number) => {
|
|
||||||
return (event: Event, url: string) => {
|
|
||||||
this.log(webContentsId).debug('did-start-navigation', url);
|
|
||||||
|
|
||||||
const parsedURL = parseURL(url)!;
|
|
||||||
const serverURL = this.getServerURLFromWebContentsId(webContentsId);
|
|
||||||
|
|
||||||
if (!serverURL || !isTrustedURL(parsedURL, serverURL)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (serverURL && isCustomLoginURL(parsedURL, serverURL)) {
|
|
||||||
this.customLogins[webContentsId].inProgress = true;
|
|
||||||
} else if (serverURL && this.customLogins[webContentsId].inProgress && isInternalURL(serverURL || new URL(''), parsedURL)) {
|
|
||||||
this.customLogins[webContentsId].inProgress = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
private denyNewWindow = (details: Electron.HandlerDetails): {action: 'deny' | 'allow'} => {
|
private denyNewWindow = (details: Electron.HandlerDetails): {action: 'deny' | 'allow'} => {
|
||||||
this.log().warn(`Prevented popup window to open a new window to ${details.url}.`);
|
this.log().warn(`Prevented popup window to open a new window to ${details.url}.`);
|
||||||
return {action: 'deny'};
|
return {action: 'deny'};
|
||||||
@@ -239,9 +204,6 @@ export class WebContentsEventManager {
|
|||||||
}),
|
}),
|
||||||
serverURL,
|
serverURL,
|
||||||
};
|
};
|
||||||
this.customLogins[this.popupWindow.win.webContents.id] = {
|
|
||||||
inProgress: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
popup = this.popupWindow.win;
|
popup = this.popupWindow.win;
|
||||||
popup.webContents.on('will-redirect', (event, url) => {
|
popup.webContents.on('will-redirect', (event, url) => {
|
||||||
@@ -256,7 +218,6 @@ export class WebContentsEventManager {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
popup.webContents.on('will-navigate', this.generateWillNavigate(popup.webContents.id));
|
popup.webContents.on('will-navigate', this.generateWillNavigate(popup.webContents.id));
|
||||||
popup.webContents.on('did-start-navigation', this.generateDidStartNavigation(popup.webContents.id));
|
|
||||||
popup.webContents.setWindowOpenHandler(this.denyNewWindow);
|
popup.webContents.setWindowOpenHandler(this.denyNewWindow);
|
||||||
popup.once('closed', () => {
|
popup.once('closed', () => {
|
||||||
this.popupWindow = undefined;
|
this.popupWindow = undefined;
|
||||||
@@ -304,11 +265,6 @@ export class WebContentsEventManager {
|
|||||||
addListeners?: (contents: WebContents) => void,
|
addListeners?: (contents: WebContents) => void,
|
||||||
removeListeners?: (contents: WebContents) => void,
|
removeListeners?: (contents: WebContents) => void,
|
||||||
) => {
|
) => {
|
||||||
// initialize custom login tracking
|
|
||||||
this.customLogins[contents.id] = {
|
|
||||||
inProgress: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.listeners[contents.id]) {
|
if (this.listeners[contents.id]) {
|
||||||
this.removeWebContentsListeners(contents.id);
|
this.removeWebContentsListeners(contents.id);
|
||||||
}
|
}
|
||||||
@@ -316,14 +272,6 @@ export class WebContentsEventManager {
|
|||||||
const willNavigate = this.generateWillNavigate(contents.id);
|
const willNavigate = this.generateWillNavigate(contents.id);
|
||||||
contents.on('will-navigate', willNavigate);
|
contents.on('will-navigate', willNavigate);
|
||||||
|
|
||||||
// handle custom login requests (oath, saml):
|
|
||||||
// 1. are we navigating to a supported local custom login path from the `/login` page?
|
|
||||||
// - indicate custom login is in progress
|
|
||||||
// 2. are we finished with the custom login process?
|
|
||||||
// - indicate custom login is NOT in progress
|
|
||||||
const didStartNavigation = this.generateDidStartNavigation(contents.id);
|
|
||||||
contents.on('did-start-navigation', didStartNavigation);
|
|
||||||
|
|
||||||
const spellcheck = Config.useSpellChecker;
|
const spellcheck = Config.useSpellChecker;
|
||||||
const newWindow = this.generateNewWindowListener(contents.id, spellcheck);
|
const newWindow = this.generateNewWindowListener(contents.id, spellcheck);
|
||||||
contents.setWindowOpenHandler(newWindow);
|
contents.setWindowOpenHandler(newWindow);
|
||||||
@@ -340,7 +288,6 @@ export class WebContentsEventManager {
|
|||||||
const removeWebContentsListeners = () => {
|
const removeWebContentsListeners = () => {
|
||||||
try {
|
try {
|
||||||
contents.removeListener('will-navigate', willNavigate);
|
contents.removeListener('will-navigate', willNavigate);
|
||||||
contents.removeListener('did-start-navigation', didStartNavigation);
|
|
||||||
contents.removeListener('console-message', consoleMessage);
|
contents.removeListener('console-message', consoleMessage);
|
||||||
removeListeners?.(contents);
|
removeListeners?.(contents);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@@ -784,74 +784,6 @@ describe('main/windows/callsWidgetWindow', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('handleCallsWidgetChannelLinkClick', () => {
|
|
||||||
const callsWidgetWindow = new CallsWidgetWindow();
|
|
||||||
callsWidgetWindow.win = {webContents: {id: 1}};
|
|
||||||
callsWidgetWindow.mainView = {
|
|
||||||
view: {
|
|
||||||
server: {
|
|
||||||
id: 'server-2',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
sendToRenderer: jest.fn(),
|
|
||||||
};
|
|
||||||
callsWidgetWindow.getChannelURL = jest.fn();
|
|
||||||
const servers = [
|
|
||||||
{
|
|
||||||
name: 'server-1',
|
|
||||||
order: 1,
|
|
||||||
views: [
|
|
||||||
{
|
|
||||||
name: 'view-1',
|
|
||||||
order: 0,
|
|
||||||
isOpen: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'view-2',
|
|
||||||
order: 2,
|
|
||||||
isOpen: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}, {
|
|
||||||
name: 'server-2',
|
|
||||||
order: 0,
|
|
||||||
views: [
|
|
||||||
{
|
|
||||||
name: 'view-1',
|
|
||||||
order: 0,
|
|
||||||
isOpen: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'view-2',
|
|
||||||
order: 2,
|
|
||||||
isOpen: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
lastActiveView: 2,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
const map = servers.reduce((arr, item) => {
|
|
||||||
item.views.forEach((view) => {
|
|
||||||
arr.push([`${item.name}_${view.name}`, {}]);
|
|
||||||
});
|
|
||||||
return arr;
|
|
||||||
}, []);
|
|
||||||
const views = new Map(map);
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
ViewManager.getView.mockImplementation((viewId) => views.get(viewId));
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
jest.resetAllMocks();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should switch server', () => {
|
|
||||||
callsWidgetWindow.handleCallsWidgetChannelLinkClick({sender: {id: 1}});
|
|
||||||
expect(ServerViewState.switchServer).toHaveBeenCalledWith('server-2');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('forwardToMainApp', () => {
|
describe('forwardToMainApp', () => {
|
||||||
const view = {
|
const view = {
|
||||||
view: {
|
view: {
|
||||||
|
@@ -14,7 +14,6 @@ import {
|
|||||||
CALLS_LEAVE_CALL,
|
CALLS_LEAVE_CALL,
|
||||||
CALLS_LINK_CLICK,
|
CALLS_LINK_CLICK,
|
||||||
CALLS_POPOUT_FOCUS,
|
CALLS_POPOUT_FOCUS,
|
||||||
CALLS_WIDGET_CHANNEL_LINK_CLICK,
|
|
||||||
CALLS_WIDGET_RESIZE,
|
CALLS_WIDGET_RESIZE,
|
||||||
CALLS_WIDGET_SHARE_SCREEN,
|
CALLS_WIDGET_SHARE_SCREEN,
|
||||||
CALLS_WIDGET_OPEN_THREAD,
|
CALLS_WIDGET_OPEN_THREAD,
|
||||||
@@ -80,9 +79,6 @@ export class CallsWidgetWindow {
|
|||||||
ipcMain.on(CALLS_WIDGET_OPEN_THREAD, this.handleCallsOpenThread);
|
ipcMain.on(CALLS_WIDGET_OPEN_THREAD, this.handleCallsOpenThread);
|
||||||
ipcMain.on(CALLS_WIDGET_OPEN_STOP_RECORDING_MODAL, this.handleCallsOpenStopRecordingModal);
|
ipcMain.on(CALLS_WIDGET_OPEN_STOP_RECORDING_MODAL, this.handleCallsOpenStopRecordingModal);
|
||||||
ipcMain.on(CALLS_WIDGET_OPEN_USER_SETTINGS, this.forwardToMainApp(CALLS_WIDGET_OPEN_USER_SETTINGS));
|
ipcMain.on(CALLS_WIDGET_OPEN_USER_SETTINGS, this.forwardToMainApp(CALLS_WIDGET_OPEN_USER_SETTINGS));
|
||||||
|
|
||||||
// deprecated in favour of CALLS_LINK_CLICK
|
|
||||||
ipcMain.on(CALLS_WIDGET_CHANNEL_LINK_CLICK, this.handleCallsWidgetChannelLinkClick);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -555,25 +551,6 @@ export class CallsWidgetWindow {
|
|||||||
MainWindow.get()?.focus();
|
MainWindow.get()?.focus();
|
||||||
this.mainView?.sendToRenderer(BROWSER_HISTORY_PUSH, url);
|
this.mainView?.sendToRenderer(BROWSER_HISTORY_PUSH, url);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
private handleCallsWidgetChannelLinkClick = (event: IpcMainEvent) => {
|
|
||||||
log.debug('handleCallsWidgetChannelLinkClick');
|
|
||||||
|
|
||||||
if (!this.isCallsWidget(event.sender.id)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.serverID) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ServerViewState.switchServer(this.serverID);
|
|
||||||
MainWindow.get()?.focus();
|
|
||||||
this.mainView?.sendToRenderer(BROWSER_HISTORY_PUSH, this.options?.channelURL);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const callsWidgetWindow = new CallsWidgetWindow();
|
const callsWidgetWindow = new CallsWidgetWindow();
|
||||||
|
@@ -1,53 +0,0 @@
|
|||||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
|
||||||
// See LICENSE.txt for license information.
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import {Row, Button} from 'react-bootstrap';
|
|
||||||
import {FormattedMessage} from 'react-intl';
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
darkMode?: boolean;
|
|
||||||
goBack?: () => void;
|
|
||||||
show?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default class ExtraBar extends React.PureComponent<Props> {
|
|
||||||
handleBack = () => {
|
|
||||||
if (this.props.goBack) {
|
|
||||||
this.props.goBack();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
render() {
|
|
||||||
let barClass = 'clear-mode';
|
|
||||||
if (!this.props.show) {
|
|
||||||
barClass = 'hidden';
|
|
||||||
} else if (this.props.darkMode) {
|
|
||||||
barClass = 'dark-mode';
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Row
|
|
||||||
id={'extra-bar'}
|
|
||||||
className={barClass}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={'container-fluid'}
|
|
||||||
onClick={this.handleBack}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
variant={'link'}
|
|
||||||
size={'sm'}
|
|
||||||
>
|
|
||||||
<span className={'backIcon icon-arrow-left'}/>
|
|
||||||
<span className={'backLabel'}>
|
|
||||||
<FormattedMessage
|
|
||||||
id='renderer.components.extraBar.back'
|
|
||||||
defaultMessage='Back'
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</Row>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -15,7 +15,6 @@ import type {DownloadedItems} from 'types/downloads';
|
|||||||
import DeveloperModeIndicator from './DeveloperModeIndicator';
|
import DeveloperModeIndicator from './DeveloperModeIndicator';
|
||||||
import DownloadsDropdownButton from './DownloadsDropdown/DownloadsDropdownButton';
|
import DownloadsDropdownButton from './DownloadsDropdown/DownloadsDropdownButton';
|
||||||
import ErrorView from './ErrorView';
|
import ErrorView from './ErrorView';
|
||||||
import ExtraBar from './ExtraBar';
|
|
||||||
import ServerDropdownButton from './ServerDropdownButton';
|
import ServerDropdownButton from './ServerDropdownButton';
|
||||||
import TabBar from './TabBar';
|
import TabBar from './TabBar';
|
||||||
|
|
||||||
@@ -50,7 +49,6 @@ type State = {
|
|||||||
tabViewStatus: Map<string, TabViewStatus>;
|
tabViewStatus: Map<string, TabViewStatus>;
|
||||||
modalOpen?: boolean;
|
modalOpen?: boolean;
|
||||||
fullScreen?: boolean;
|
fullScreen?: boolean;
|
||||||
showExtraBar?: boolean;
|
|
||||||
isMenuOpen: boolean;
|
isMenuOpen: boolean;
|
||||||
isDownloadsDropdownOpen: boolean;
|
isDownloadsDropdownOpen: boolean;
|
||||||
showDownloadsBadge: boolean;
|
showDownloadsBadge: boolean;
|
||||||
@@ -210,10 +208,6 @@ class MainPage extends React.PureComponent<Props, State> {
|
|||||||
this.setState({modalOpen: false});
|
this.setState({modalOpen: false});
|
||||||
});
|
});
|
||||||
|
|
||||||
window.desktop.onToggleBackButton((showExtraBar) => {
|
|
||||||
this.setState({showExtraBar});
|
|
||||||
});
|
|
||||||
|
|
||||||
window.desktop.onUpdateMentions((view, mentions, unreads, isExpired) => {
|
window.desktop.onUpdateMentions((view, mentions, unreads, isExpired) => {
|
||||||
const {unreadCounts, mentionCounts, sessionsExpired} = this.state;
|
const {unreadCounts, mentionCounts, sessionsExpired} = this.state;
|
||||||
|
|
||||||
@@ -532,13 +526,6 @@ class MainPage extends React.PureComponent<Props, State> {
|
|||||||
|
|
||||||
const viewsRow = (
|
const viewsRow = (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<ExtraBar
|
|
||||||
darkMode={this.props.darkMode}
|
|
||||||
show={this.state.showExtraBar}
|
|
||||||
goBack={() => {
|
|
||||||
window.desktop.goBack();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Row>
|
<Row>
|
||||||
{views()}
|
{views()}
|
||||||
</Row>
|
</Row>
|
||||||
|
@@ -1,44 +0,0 @@
|
|||||||
#extra-bar {
|
|
||||||
max-height: 76px;
|
|
||||||
transition: max-height 0.25s ease;
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
}
|
|
||||||
|
|
||||||
#extra-bar div {
|
|
||||||
padding: 0 0.93em 0.2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
#extra-bar.hidden {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#extra-bar.dark-mode {
|
|
||||||
background: #1F1F1F;
|
|
||||||
}
|
|
||||||
|
|
||||||
#extra-bar.dark-mode .btn-link {
|
|
||||||
color: rgba(243,243,243,0.7);
|
|
||||||
}
|
|
||||||
|
|
||||||
#extra-bar.dark-mode span.backLabel {
|
|
||||||
color: rgba(243,243,243,0.7);
|
|
||||||
}
|
|
||||||
|
|
||||||
span.backLabel {
|
|
||||||
font-family: "Open Sans", sans-serif;
|
|
||||||
font-weight: normal;
|
|
||||||
line-height: 30px;
|
|
||||||
font-size: 14px;
|
|
||||||
padding-top: 0px;
|
|
||||||
padding-bottom: 0px;
|
|
||||||
color: #166de0;
|
|
||||||
}
|
|
||||||
|
|
||||||
span.backIcon {
|
|
||||||
margin-right: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container-fluid button:first-child {
|
|
||||||
padding-left: 0px;
|
|
||||||
}
|
|
@@ -6,6 +6,5 @@
|
|||||||
@import url("TabBar.css");
|
@import url("TabBar.css");
|
||||||
@import url("UpdaterPage.css");
|
@import url("UpdaterPage.css");
|
||||||
@import url("CertificateModal.css");
|
@import url("CertificateModal.css");
|
||||||
@import url("ExtraBar.css");
|
|
||||||
@import url("LoadingScreen.css");
|
@import url("LoadingScreen.css");
|
||||||
@import url("LoadingAnimation.css");
|
@import url("LoadingAnimation.css");
|
||||||
|
@@ -14,6 +14,4 @@ export type DeveloperSettings = {
|
|||||||
disableNotificationStorage?: boolean;
|
disableNotificationStorage?: boolean;
|
||||||
disableUserActivityMonitor?: boolean;
|
disableUserActivityMonitor?: boolean;
|
||||||
disableContextMenu?: boolean;
|
disableContextMenu?: boolean;
|
||||||
forceLegacyAPI?: boolean;
|
|
||||||
forceNewAPI?: boolean;
|
|
||||||
};
|
};
|
||||||
|
@@ -79,7 +79,6 @@ declare global {
|
|||||||
onPlaySound: (listener: (soundName: string) => void) => void;
|
onPlaySound: (listener: (soundName: string) => void) => void;
|
||||||
onModalOpen: (listener: () => void) => void;
|
onModalOpen: (listener: () => void) => void;
|
||||||
onModalClose: (listener: () => void) => void;
|
onModalClose: (listener: () => void) => void;
|
||||||
onToggleBackButton: (listener: (showExtraBar: boolean) => void) => void;
|
|
||||||
onUpdateMentions: (listener: (view: string, mentions: number, unreads: boolean, isExpired: boolean) => void) => void;
|
onUpdateMentions: (listener: (view: string, mentions: number, unreads: boolean, isExpired: boolean) => void) => void;
|
||||||
onCloseServersDropdown: (listener: () => void) => void;
|
onCloseServersDropdown: (listener: () => void) => void;
|
||||||
onOpenServersDropdown: (listener: () => void) => void;
|
onOpenServersDropdown: (listener: () => void) => void;
|
||||||
|
Reference in New Issue
Block a user