diff --git a/i18n/en.json b/i18n/en.json index ce63337f..2172facd 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -85,8 +85,6 @@ "main.menus.app.view.developerModeDisableContextMenu": "Disable Context Menu", "main.menus.app.view.developerModeDisableNotificationStorage": "Disable Notification Storage", "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.devToolsCurrentCallWidget": "Developer Tools for Call Widget", "main.menus.app.view.devToolsCurrentServer": "Developer Tools for Current Server", @@ -164,7 +162,6 @@ "renderer.components.errorView.troubleshooting.browserView.canReachFromBrowserWindow": "You can reach {url} from a browser window.", "renderer.components.errorView.troubleshooting.computerIsConnected": "Your computer is connected to the internet.", "renderer.components.errorView.troubleshooting.urlIsCorrect.appNameIsCorrect": "The {appName} URL {url} is correct", - "renderer.components.extraBar.back": "Back", "renderer.components.input.required": "This field is required", "renderer.components.mainPage.contextMenu.ariaLabel": "Context menu", "renderer.components.mainPage.titleBar": "{appName}", diff --git a/src/common/communication.ts b/src/common/communication.ts index 903403d3..cd73b89d 100644 --- a/src/common/communication.ts +++ b/src/common/communication.ts @@ -55,15 +55,11 @@ export const PLAY_SOUND = 'play_sound'; export const GET_DOWNLOAD_LOCATION = 'get_download_location'; 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 SESSION_EXPIRED = 'session_expired'; 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 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 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 GET_AVAILABLE_SPELL_CHECKER_LANGUAGES = 'get-available-spell-checker-languages'; @@ -118,13 +112,11 @@ export const VIEW_FINISHED_RESIZING = 'view-finished-resizing'; // Calls 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 CALLS_JOIN_CALL = 'calls-join-call'; export const CALLS_LEAVE_CALL = 'calls-leave-call'; export const CALLS_WIDGET_RESIZE = 'calls-widget-resize'; 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_JOINED_CALL = 'calls-joined-call'; 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 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 DEVELOPER_MODE_UPDATED = 'developer-mode-updated'; 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_RECEIVE = 'metrics-receive'; diff --git a/src/common/utils/constants.ts b/src/common/utils/constants.ts index f7307722..6b347784 100644 --- a/src/common/utils/constants.ts +++ b/src/common/utils/constants.ts @@ -15,7 +15,6 @@ export const MAX_LOADING_SCREEN_SECONDS = 4 * SECOND; export const TAB_BAR_HEIGHT = 40; 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_MAC = 80; export const MENU_SHADOW_WIDTH = 24; @@ -55,20 +54,6 @@ export const URLValidationStatus = { 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 = [ 'plugins', 'signup', diff --git a/src/common/utils/url.test.js b/src/common/utils/url.test.js index 2f18a345..16375423 100644 --- a/src/common/utils/url.test.js +++ b/src/common/utils/url.test.js @@ -9,7 +9,6 @@ import { isValidURI, parseURL, isInternalURL, - isCustomLoginURL, isCallsPopOutURL, isTrustedURL, } 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', () => { it('should match correct URL', () => { expect(isCallsPopOutURL( diff --git a/src/common/utils/url.ts b/src/common/utils/url.ts index 856beb93..c0ce205b 100644 --- a/src/common/utils/url.ts +++ b/src/common/utils/url.ts @@ -4,7 +4,7 @@ import {isHttpsUri, isHttpUri, isUri} from 'valid-url'; 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 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))); }; -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) => { const matches = inputURL.pathname.match(new RegExp(`^${escapeRegExp(getFormattedPathName(serverURL.pathname))}([A-Za-z0-9-_]+)/`, 'i')); diff --git a/src/main/authManager.test.js b/src/main/authManager.test.js index c408e58e..3a1b2306 100644 --- a/src/main/authManager.test.js +++ b/src/main/authManager.test.js @@ -14,9 +14,6 @@ jest.mock('common/utils/url', () => { isTrustedURL: (url) => { 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(); }); - 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', () => { 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()); diff --git a/src/main/authManager.ts b/src/main/authManager.ts index a0a8d892..444acf4b 100644 --- a/src/main/authManager.ts +++ b/src/main/authManager.ts @@ -4,7 +4,7 @@ import type {AuthenticationResponseDetails, AuthInfo, WebContents, Event} from ' import {Logger} from 'common/log'; 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 {getLocalPreload} from 'main/utils'; 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 - 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); } else { this.popPermissionModal(request, authInfo, BASIC_AUTH_PERMISSION); diff --git a/src/main/developerMode.ts b/src/main/developerMode.ts index c8496ba7..d85f344f 100644 --- a/src/main/developerMode.ts +++ b/src/main/developerMode.ts @@ -4,7 +4,7 @@ import {ipcMain} from 'electron'; 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 {developerModeJson} from 'main/constants'; @@ -18,7 +18,6 @@ export class DeveloperMode extends EventEmitter { this.json = new JsonFileManager(file); 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'; diff --git a/src/main/menus/app.ts b/src/main/menus/app.ts index 3bc64898..816d0b86 100644 --- a/src/main/menus/app.ts +++ b/src/main/menus/app.ts @@ -214,22 +214,6 @@ export function createTemplate(config: Config, updateManager: UpdateManager) { 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'); - }, - }, ]); } diff --git a/src/main/preload/externalAPI.ts b/src/main/preload/externalAPI.ts index 46d81c25..f8016bd8 100644 --- a/src/main/preload/externalAPI.ts +++ b/src/main/preload/externalAPI.ts @@ -8,16 +8,11 @@ import type {DesktopAPI} from '@mattermost/desktop-api'; import { NOTIFY_MENTION, - IS_UNREAD, - UNREAD_RESULT, SESSION_EXPIRED, REACT_APP_INITIALIZED, USER_ACTIVITY_UPDATE, BROWSER_HISTORY_PUSH, - APP_LOGGED_IN, - APP_LOGGED_OUT, GET_VIEW_INFO_FOR_TEST, - DESKTOP_SOURCES_RESULT, VIEW_FINISHED_RESIZING, CALLS_JOIN_CALL, CALLS_JOINED_CALL, @@ -33,7 +28,6 @@ import { BROWSER_HISTORY_STATUS_UPDATED, NOTIFICATION_CLICKED, CALLS_WIDGET_RESIZE, - CALLS_WIDGET_CHANNEL_LINK_CLICK, CALLS_LINK_CLICK, CALLS_POPOUT_FOCUS, CALLS_WIDGET_OPEN_THREAD, @@ -41,9 +35,7 @@ import { CALLS_WIDGET_OPEN_USER_SETTINGS, GET_DESKTOP_SOURCES, UNREADS_AND_MENTIONS, - LEGACY_OFF, TAB_LOGIN_CHANGED, - GET_DEVELOPER_MODE_SETTING, METRICS_SEND, METRICS_REQUEST, METRICS_RECEIVE, @@ -51,96 +43,80 @@ import { import type {ExternalAPI} from 'types/externalAPI'; -let legacyEnabled = false; -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[]) => - listener(...args as never[]); - ipcRenderer.on(channel, listenerWithEvent); - return () => { - ipcRenderer.off(channel, listenerWithEvent); - }; +const createListener: ExternalAPI['createListener'] = (channel: string, listener: (...args: never[]) => void) => { + const listenerWithEvent = (_: IpcRendererEvent, ...args: unknown[]) => + listener(...args as never[]); + ipcRenderer.on(channel, listenerWithEvent); + return () => { + ipcRenderer.off(channel, listenerWithEvent); }; +}; - const desktopAPI: DesktopAPI = { +const desktopAPI: DesktopAPI = { - // Initialization - isDev: () => ipcRenderer.invoke(GET_IS_DEV_MODE), - getAppInfo: () => { - // Using this signal as the sign to disable the legacy code, since it is run before the app is rendered - if (legacyEnabled) { - legacyOff?.(); - } + // Initialization + isDev: () => ipcRenderer.invoke(GET_IS_DEV_MODE), + getAppInfo: () => ipcRenderer.invoke(GET_APP_INFO), + reactAppInitialized: () => ipcRenderer.send(REACT_APP_INITIALIZED), - return ipcRenderer.invoke(GET_APP_INFO); - }, - reactAppInitialized: () => ipcRenderer.send(REACT_APP_INITIALIZED), + // Session + setSessionExpired: (isExpired) => ipcRenderer.send(SESSION_EXPIRED, isExpired), + onUserActivityUpdate: (listener) => createListener(USER_ACTIVITY_UPDATE, listener), - // Session - setSessionExpired: (isExpired) => ipcRenderer.send(SESSION_EXPIRED, isExpired), - onUserActivityUpdate: (listener) => createListener(USER_ACTIVITY_UPDATE, listener), + onLogin: () => ipcRenderer.send(TAB_LOGIN_CHANGED, true), + onLogout: () => ipcRenderer.send(TAB_LOGIN_CHANGED, false), - onLogin: () => ipcRenderer.send(TAB_LOGIN_CHANGED, true), - onLogout: () => ipcRenderer.send(TAB_LOGIN_CHANGED, false), + // Unreads/mentions/notifications + sendNotification: (title, body, channelId, teamId, url, silent, soundName) => + ipcRenderer.invoke(NOTIFY_MENTION, title, body, channelId, teamId, url, silent, soundName), + onNotificationClicked: (listener) => createListener(NOTIFICATION_CLICKED, listener), + setUnreadsAndMentions: (isUnread, mentionCount) => ipcRenderer.send(UNREADS_AND_MENTIONS, isUnread, mentionCount), - // Unreads/mentions/notifications - sendNotification: (title, body, channelId, teamId, url, silent, soundName) => - ipcRenderer.invoke(NOTIFY_MENTION, title, body, channelId, teamId, url, silent, soundName), - onNotificationClicked: (listener) => createListener(NOTIFICATION_CLICKED, listener), - setUnreadsAndMentions: (isUnread, mentionCount) => ipcRenderer.send(UNREADS_AND_MENTIONS, isUnread, mentionCount), + // Navigation + requestBrowserHistoryStatus: () => ipcRenderer.invoke(REQUEST_BROWSER_HISTORY_STATUS), + onBrowserHistoryStatusUpdated: (listener) => createListener(BROWSER_HISTORY_STATUS_UPDATED, listener), + onBrowserHistoryPush: (listener) => createListener(BROWSER_HISTORY_PUSH, listener), + sendBrowserHistoryPush: (path) => ipcRenderer.send(BROWSER_HISTORY_PUSH, path), - // Navigation - requestBrowserHistoryStatus: () => ipcRenderer.invoke(REQUEST_BROWSER_HISTORY_STATUS), - onBrowserHistoryStatusUpdated: (listener) => createListener(BROWSER_HISTORY_STATUS_UPDATED, listener), - onBrowserHistoryPush: (listener) => createListener(BROWSER_HISTORY_PUSH, listener), - sendBrowserHistoryPush: (path) => ipcRenderer.send(BROWSER_HISTORY_PUSH, path), + // Calls + joinCall: (opts) => ipcRenderer.invoke(CALLS_JOIN_CALL, opts), + leaveCall: () => ipcRenderer.send(CALLS_LEAVE_CALL), - // Calls - joinCall: (opts) => ipcRenderer.invoke(CALLS_JOIN_CALL, opts), - leaveCall: () => ipcRenderer.send(CALLS_LEAVE_CALL), + callsWidgetConnected: (callID, sessionID) => ipcRenderer.send(CALLS_JOINED_CALL, callID, sessionID), + resizeCallsWidget: (width, height) => ipcRenderer.send(CALLS_WIDGET_RESIZE, width, height), - callsWidgetConnected: (callID, sessionID) => ipcRenderer.send(CALLS_JOINED_CALL, callID, sessionID), - resizeCallsWidget: (width, height) => ipcRenderer.send(CALLS_WIDGET_RESIZE, width, height), + sendCallsError: (err, callID, errMsg) => ipcRenderer.send(CALLS_ERROR, err, callID, errMsg), + onCallsError: (listener) => createListener(CALLS_ERROR, listener), - sendCallsError: (err, callID, errMsg) => ipcRenderer.send(CALLS_ERROR, err, callID, errMsg), - onCallsError: (listener) => createListener(CALLS_ERROR, listener), + getDesktopSources: (opts) => ipcRenderer.invoke(GET_DESKTOP_SOURCES, opts), + openScreenShareModal: () => ipcRenderer.send(DESKTOP_SOURCES_MODAL_REQUEST), + onOpenScreenShareModal: (listener) => createListener(DESKTOP_SOURCES_MODAL_REQUEST, listener), - getDesktopSources: (opts) => ipcRenderer.invoke(GET_DESKTOP_SOURCES, opts), - openScreenShareModal: () => ipcRenderer.send(DESKTOP_SOURCES_MODAL_REQUEST), - onOpenScreenShareModal: (listener) => createListener(DESKTOP_SOURCES_MODAL_REQUEST, listener), + shareScreen: (sourceID, withAudio) => ipcRenderer.send(CALLS_WIDGET_SHARE_SCREEN, sourceID, withAudio), + onScreenShared: (listener) => createListener(CALLS_WIDGET_SHARE_SCREEN, listener), - shareScreen: (sourceID, withAudio) => ipcRenderer.send(CALLS_WIDGET_SHARE_SCREEN, sourceID, withAudio), - onScreenShared: (listener) => createListener(CALLS_WIDGET_SHARE_SCREEN, listener), + sendJoinCallRequest: (callId) => ipcRenderer.send(CALLS_JOIN_REQUEST, callId), + onJoinCallRequest: (listener) => createListener(CALLS_JOIN_REQUEST, listener), - sendJoinCallRequest: (callId) => ipcRenderer.send(CALLS_JOIN_REQUEST, callId), - onJoinCallRequest: (listener) => createListener(CALLS_JOIN_REQUEST, listener), + openLinkFromCalls: (url) => ipcRenderer.send(CALLS_LINK_CLICK, url), - openLinkFromCalls: (url) => ipcRenderer.send(CALLS_LINK_CLICK, url), + focusPopout: () => ipcRenderer.send(CALLS_POPOUT_FOCUS), - focusPopout: () => ipcRenderer.send(CALLS_POPOUT_FOCUS), + openThreadForCalls: (threadID) => ipcRenderer.send(CALLS_WIDGET_OPEN_THREAD, threadID), + onOpenThreadForCalls: (listener) => createListener(CALLS_WIDGET_OPEN_THREAD, listener), - openThreadForCalls: (threadID) => ipcRenderer.send(CALLS_WIDGET_OPEN_THREAD, threadID), - onOpenThreadForCalls: (listener) => createListener(CALLS_WIDGET_OPEN_THREAD, listener), + openStopRecordingModal: (channelID) => ipcRenderer.send(CALLS_WIDGET_OPEN_STOP_RECORDING_MODAL, channelID), + onOpenStopRecordingModal: (listener) => createListener(CALLS_WIDGET_OPEN_STOP_RECORDING_MODAL, listener), - openStopRecordingModal: (channelID) => ipcRenderer.send(CALLS_WIDGET_OPEN_STOP_RECORDING_MODAL, channelID), - onOpenStopRecordingModal: (listener) => createListener(CALLS_WIDGET_OPEN_STOP_RECORDING_MODAL, listener), + openCallsUserSettings: () => ipcRenderer.send(CALLS_WIDGET_OPEN_USER_SETTINGS), + onOpenCallsUserSettings: (listener) => createListener(CALLS_WIDGET_OPEN_USER_SETTINGS, listener), - openCallsUserSettings: () => ipcRenderer.send(CALLS_WIDGET_OPEN_USER_SETTINGS), - onOpenCallsUserSettings: (listener) => createListener(CALLS_WIDGET_OPEN_USER_SETTINGS, listener), + onSendMetrics: (listener) => createListener(METRICS_SEND, listener), - onSendMetrics: (listener) => createListener(METRICS_SEND, listener), - - // Utility - unregister: (channel) => ipcRenderer.removeAllListeners(channel), - }; - contextBridge.exposeInMainWorld('desktopAPI', desktopAPI); -}); + // Utility + unregister: (channel) => ipcRenderer.removeAllListeners(channel), +}; +contextBridge.exposeInMainWorld('desktopAPI', desktopAPI); ipcRenderer.on(METRICS_REQUEST, async (_, name, serverId) => { const memory = await process.getProcessMemoryInfo(); @@ -203,319 +179,3 @@ const CLEAR_CACHE_INTERVAL = 6 * 60 * 60 * 1000; // 6 hours setInterval(() => { webFrame.clearCache(); }, 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 = 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'); - }; -}); diff --git a/src/main/preload/internalAPI.js b/src/main/preload/internalAPI.js index 4b0a4b57..d5bb4ad3 100644 --- a/src/main/preload/internalAPI.js +++ b/src/main/preload/internalAPI.js @@ -42,7 +42,6 @@ import { PLAY_SOUND, MODAL_OPEN, MODAL_CLOSE, - TOGGLE_BACK_BUTTON, UPDATE_MENTIONS, SHOW_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)), onModalOpen: (listener) => ipcRenderer.on(MODAL_OPEN, () => 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)), onCloseServersDropdown: (listener) => ipcRenderer.on(CLOSE_SERVERS_DROPDOWN, () => listener()), onOpenServersDropdown: (listener) => ipcRenderer.on(OPEN_SERVERS_DROPDOWN, () => listener()), diff --git a/src/main/utils.test.js b/src/main/utils.test.js index f6dd57ff..1f69d8a9 100644 --- a/src/main/utils.test.js +++ b/src/main/utils.test.js @@ -2,7 +2,7 @@ // See LICENSE.txt for license information. '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'; @@ -68,27 +68,6 @@ describe('main/utils', () => { 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', () => { diff --git a/src/main/utils.ts b/src/main/utils.ts index b98469cb..2696bf6d 100644 --- a/src/main/utils.ts +++ b/src/main/utils.ts @@ -11,8 +11,7 @@ const exec = promisify(execOriginal); import type {BrowserWindow} from 'electron'; import {app} from 'electron'; -import {BACK_BAR_HEIGHT, customLoginRegexPaths, TAB_BAR_HEIGHT} from 'common/utils/constants'; -import {isAdminUrl, isPluginUrl, isTeamUrl, isUrlType, parseURL} from 'common/utils/url'; +import {TAB_BAR_HEIGHT} from 'common/utils/constants'; import type {Args} from 'types/args'; @@ -48,42 +47,20 @@ export function shouldBeHiddenOnStartup(parsedArgv: Args) { return false; } -export function getWindowBoundaries(win: BrowserWindow, hasBackBar = false) { +export function getWindowBoundaries(win: BrowserWindow) { 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 { x: 0, - y: TAB_BAR_HEIGHT + (hasBackBar ? BACK_BAR_HEIGHT : 0), + y: TAB_BAR_HEIGHT, 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) { return path.join(app.getAppPath(), file); } diff --git a/src/main/views/MattermostBrowserView.test.js b/src/main/views/MattermostBrowserView.test.js index 49a35a92..ec01e09f 100644 --- a/src/main/views/MattermostBrowserView.test.js +++ b/src/main/views/MattermostBrowserView.test.js @@ -4,14 +4,13 @@ 'use strict'; 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 MessagingView from 'common/views/MessagingView'; import {MattermostBrowserView} from './MattermostBrowserView'; import ContextMenu from '../contextMenu'; -import Utils from '../utils'; import MainWindow from '../windows/mainWindow'; 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', () => { const window = {on: jest.fn()}; const mattermostView = new MattermostBrowserView(view, {}, {}); @@ -476,18 +453,4 @@ describe('main/views/MattermostBrowserView', () => { 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); - }); - }); }); diff --git a/src/main/views/MattermostBrowserView.ts b/src/main/views/MattermostBrowserView.ts index 7ee8a3c0..1a5599cd 100644 --- a/src/main/views/MattermostBrowserView.ts +++ b/src/main/views/MattermostBrowserView.ts @@ -11,8 +11,6 @@ import { LOAD_SUCCESS, LOAD_FAILED, UPDATE_TARGET_URL, - IS_UNREAD, - TOGGLE_BACK_BUTTON, LOADSCREEN_END, SERVERS_URL_MODIFIED, BROWSER_HISTORY_STATUS_UPDATED, @@ -32,7 +30,7 @@ import MainWindow from 'main/windows/mainWindow'; import WebContentsEventManager from './webContentEvents'; import ContextMenu from '../contextMenu'; -import {getWindowBoundaries, getLocalPreload, composeUserAgent, shouldHaveBackBar} from '../utils'; +import {getWindowBoundaries, getLocalPreload, composeUserAgent} from '../utils'; enum Status { LOADING, @@ -41,9 +39,6 @@ enum Status { ERROR = -1, } -const MENTIONS_GROUP = 2; -const titleParser = /(\((\d+)\) )?(\* )?/g; - export class MattermostBrowserView extends EventEmitter { view: MattermostView; isVisible: boolean; @@ -84,7 +79,6 @@ export class MattermostBrowserView extends EventEmitter { this.log.verbose('View created'); this.browserView.webContents.on('update-target-url', this.handleUpdateTarget); - this.browserView.webContents.on('did-navigate', this.handleDidNavigate); if (process.platform !== 'darwin') { 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); if (!DeveloperMode.get('disableContextMenu')) { @@ -233,7 +223,7 @@ export class MattermostBrowserView extends EventEmitter { this.isVisible = true; mainWindow.addBrowserView(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) { 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 */ @@ -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 */ @@ -486,16 +432,12 @@ export class MattermostBrowserView extends EventEmitter { this.log.verbose(`finished loading ${loadURL}`); MainWindow.sendToRenderer(LOAD_SUCCESS, this.id); 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.removeLoading = setTimeout(this.setInitialized, MAX_LOADING_SCREEN_SECONDS, true); this.emit(LOAD_SUCCESS, this.id, loadURL); const mainWindow = MainWindow.get(); 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 */ - 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) => { this.log.silly('handleUpdateTarget', e, url); const parsedURL = parseURL(url); diff --git a/src/main/views/viewManager.ts b/src/main/views/viewManager.ts index c2147fac..14357687 100644 --- a/src/main/views/viewManager.ts +++ b/src/main/views/viewManager.ts @@ -18,10 +18,7 @@ import { UPDATE_URL_VIEW_WIDTH, SERVERS_UPDATE, REACT_APP_INITIALIZED, - APP_LOGGED_OUT, - APP_LOGGED_IN, RELOAD_CURRENT_VIEW, - UNREAD_RESULT, HISTORY, GET_VIEW_INFO_FOR_TEST, SESSION_EXPIRED, @@ -31,7 +28,6 @@ import { SWITCH_TAB, GET_IS_DEV_MODE, REQUEST_BROWSER_HISTORY_STATUS, - LEGACY_OFF, UNREADS_AND_MENTIONS, TAB_LOGIN_CHANGED, DEVELOPER_MODE_UPDATED, @@ -58,7 +54,7 @@ import LoadingScreen from './loadingScreen'; import {MattermostBrowserView} from './MattermostBrowserView'; import modalManager from './modalManager'; -import {getLocalPreload, getAdjustedWindowBoundaries, shouldHaveBackBar} from '../utils'; +import {getLocalPreload, getAdjustedWindowBoundaries} from '../utils'; const log = new Logger('ViewManager'); const URL_VIEW_DURATION = 10 * SECOND; @@ -84,14 +80,10 @@ export class ViewManager { ipcMain.on(HISTORY, this.handleHistory); ipcMain.on(REACT_APP_INITIALIZED, this.handleReactAppInitialized); 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(RELOAD_CURRENT_VIEW, this.handleReloadCurrentView); - ipcMain.on(UNREAD_RESULT, this.handleUnreadChanged); ipcMain.on(UNREADS_AND_MENTIONS, this.handleUnreadsAndMentionsChanged); ipcMain.on(SESSION_EXPIRED, this.handleSessionExpired); - ipcMain.on(LEGACY_OFF, this.handleLegacyOff); ipcMain.on(SWITCH_TAB, (event, viewId) => this.showById(viewId)); @@ -108,7 +100,7 @@ export class ViewManager { private handleDeveloperModeUpdated = (json: DeveloperSettings) => { 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 = new Map(); this.closedViews = new Map(); @@ -504,27 +496,6 @@ export class ViewManager { 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) => { log.debug('handleTabLoggedIn', event.sender.id); const view = this.getViewByWebContentsId(event.sender.id); @@ -605,28 +576,6 @@ export class ViewManager { 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) => { log.silly('handleUnreadsAndMentionsChanged', {webContentsId: e.sender.id, isUnread, mentionCount}); @@ -653,7 +602,7 @@ export class ViewManager { const currentView = this.getCurrentView(); 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); } }; diff --git a/src/main/views/webContentEvents.test.js b/src/main/views/webContentEvents.test.js index c64c7acb..392ef5e7 100644 --- a/src/main/views/webContentEvents.test.js +++ b/src/main/views/webContentEvents.test.js @@ -95,27 +95,11 @@ describe('main/views/webContentsEvents', () => { 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', () => { willNavigate(event, 'mailto:test@mattermost.com'); 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', () => { willNavigate(event, 'http://server-1.com/plugins/com.mattermost.plugin-channel-export/api/v1/export'); 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', () => { const webContentsEventManager = new WebContentsEventManager(); const newWindow = webContentsEventManager.generateNewWindowListener(1, true); diff --git a/src/main/views/webContentEvents.ts b/src/main/views/webContentEvents.ts index 30144010..bfa6e7fe 100644 --- a/src/main/views/webContentEvents.ts +++ b/src/main/views/webContentEvents.ts @@ -11,7 +11,6 @@ import { isAdminUrl, isCallsPopOutURL, isChannelExportUrl, - isCustomLoginURL, isHelpUrl, isImageProxyUrl, isInternalURL, @@ -20,11 +19,9 @@ import { isPluginUrl, isPublicFilesUrl, isTeamUrl, - isTrustedURL, isValidURI, parseURL, } from 'common/utils/url'; -import {flushCookiesStore} from 'main/app/utils'; import ContextMenu from 'main/contextMenu'; import PluginsPopUpsManager from 'main/views/pluginsPopUps'; import ViewManager from 'main/views/viewManager'; @@ -36,19 +33,13 @@ import {generateHandleConsoleMessage, isCustomProtocol} from './webContentEvents import allowProtocolDialog from '../allowProtocolDialog'; import {composeUserAgent} from '../utils'; -type CustomLogin = { - inProgress: boolean; -} - const log = new Logger('WebContentsEventManager'); export class WebContentsEventManager { - customLogins: Record; listeners: Record void>; popupWindow?: {win: BrowserWindow; serverURL?: URL}; constructor() { - this.customLogins = {}; this.listeners = {}; } @@ -101,16 +92,9 @@ export class WebContentsEventManager { return; } - if (serverURL && isCustomLoginURL(parsedURL, serverURL)) { - return; - } if (parsedURL.protocol === 'mailto:') { return; } - if (this.customLogins[webContentsId]?.inProgress) { - flushCookiesStore(); - return; - } const callID = CallsWidgetWindow.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'} => { this.log().warn(`Prevented popup window to open a new window to ${details.url}.`); return {action: 'deny'}; @@ -239,9 +204,6 @@ export class WebContentsEventManager { }), serverURL, }; - this.customLogins[this.popupWindow.win.webContents.id] = { - inProgress: false, - }; popup = this.popupWindow.win; 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('did-start-navigation', this.generateDidStartNavigation(popup.webContents.id)); popup.webContents.setWindowOpenHandler(this.denyNewWindow); popup.once('closed', () => { this.popupWindow = undefined; @@ -304,11 +265,6 @@ export class WebContentsEventManager { addListeners?: (contents: WebContents) => void, removeListeners?: (contents: WebContents) => void, ) => { - // initialize custom login tracking - this.customLogins[contents.id] = { - inProgress: false, - }; - if (this.listeners[contents.id]) { this.removeWebContentsListeners(contents.id); } @@ -316,14 +272,6 @@ export class WebContentsEventManager { const willNavigate = this.generateWillNavigate(contents.id); 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 newWindow = this.generateNewWindowListener(contents.id, spellcheck); contents.setWindowOpenHandler(newWindow); @@ -340,7 +288,6 @@ export class WebContentsEventManager { const removeWebContentsListeners = () => { try { contents.removeListener('will-navigate', willNavigate); - contents.removeListener('did-start-navigation', didStartNavigation); contents.removeListener('console-message', consoleMessage); removeListeners?.(contents); } catch (e) { diff --git a/src/main/windows/callsWidgetWindow.test.js b/src/main/windows/callsWidgetWindow.test.js index 8630af2f..9efe2203 100644 --- a/src/main/windows/callsWidgetWindow.test.js +++ b/src/main/windows/callsWidgetWindow.test.js @@ -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', () => { const view = { view: { diff --git a/src/main/windows/callsWidgetWindow.ts b/src/main/windows/callsWidgetWindow.ts index 3ae9f8eb..7afaea53 100644 --- a/src/main/windows/callsWidgetWindow.ts +++ b/src/main/windows/callsWidgetWindow.ts @@ -14,7 +14,6 @@ import { CALLS_LEAVE_CALL, CALLS_LINK_CLICK, CALLS_POPOUT_FOCUS, - CALLS_WIDGET_CHANNEL_LINK_CLICK, CALLS_WIDGET_RESIZE, CALLS_WIDGET_SHARE_SCREEN, CALLS_WIDGET_OPEN_THREAD, @@ -80,9 +79,6 @@ export class CallsWidgetWindow { ipcMain.on(CALLS_WIDGET_OPEN_THREAD, this.handleCallsOpenThread); ipcMain.on(CALLS_WIDGET_OPEN_STOP_RECORDING_MODAL, this.handleCallsOpenStopRecordingModal); 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(); 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(); diff --git a/src/renderer/components/ExtraBar.tsx b/src/renderer/components/ExtraBar.tsx deleted file mode 100644 index d841362b..00000000 --- a/src/renderer/components/ExtraBar.tsx +++ /dev/null @@ -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 { - 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 ( - -
- -
-
- ); - } -} diff --git a/src/renderer/components/MainPage.tsx b/src/renderer/components/MainPage.tsx index ac6c4b28..0d669110 100644 --- a/src/renderer/components/MainPage.tsx +++ b/src/renderer/components/MainPage.tsx @@ -15,7 +15,6 @@ import type {DownloadedItems} from 'types/downloads'; import DeveloperModeIndicator from './DeveloperModeIndicator'; import DownloadsDropdownButton from './DownloadsDropdown/DownloadsDropdownButton'; import ErrorView from './ErrorView'; -import ExtraBar from './ExtraBar'; import ServerDropdownButton from './ServerDropdownButton'; import TabBar from './TabBar'; @@ -50,7 +49,6 @@ type State = { tabViewStatus: Map; modalOpen?: boolean; fullScreen?: boolean; - showExtraBar?: boolean; isMenuOpen: boolean; isDownloadsDropdownOpen: boolean; showDownloadsBadge: boolean; @@ -210,10 +208,6 @@ class MainPage extends React.PureComponent { this.setState({modalOpen: false}); }); - window.desktop.onToggleBackButton((showExtraBar) => { - this.setState({showExtraBar}); - }); - window.desktop.onUpdateMentions((view, mentions, unreads, isExpired) => { const {unreadCounts, mentionCounts, sessionsExpired} = this.state; @@ -532,13 +526,6 @@ class MainPage extends React.PureComponent { const viewsRow = ( - { - window.desktop.goBack(); - }} - /> {views()} diff --git a/src/renderer/css/components/ExtraBar.css b/src/renderer/css/components/ExtraBar.css deleted file mode 100644 index 57a67ce7..00000000 --- a/src/renderer/css/components/ExtraBar.css +++ /dev/null @@ -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; -} diff --git a/src/renderer/css/components/index.css b/src/renderer/css/components/index.css index 44b9d518..bdce1efe 100644 --- a/src/renderer/css/components/index.css +++ b/src/renderer/css/components/index.css @@ -6,6 +6,5 @@ @import url("TabBar.css"); @import url("UpdaterPage.css"); @import url("CertificateModal.css"); -@import url("ExtraBar.css"); @import url("LoadingScreen.css"); @import url("LoadingAnimation.css"); diff --git a/src/types/settings.ts b/src/types/settings.ts index c780b608..934427e7 100644 --- a/src/types/settings.ts +++ b/src/types/settings.ts @@ -14,6 +14,4 @@ export type DeveloperSettings = { disableNotificationStorage?: boolean; disableUserActivityMonitor?: boolean; disableContextMenu?: boolean; - forceLegacyAPI?: boolean; - forceNewAPI?: boolean; }; diff --git a/src/types/window.ts b/src/types/window.ts index ec1af282..8b7f2e67 100644 --- a/src/types/window.ts +++ b/src/types/window.ts @@ -79,7 +79,6 @@ declare global { onPlaySound: (listener: (soundName: string) => void) => void; onModalOpen: (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; onCloseServersDropdown: (listener: () => void) => void; onOpenServersDropdown: (listener: () => void) => void;