mirror of
https://github.com/finos/SymphonyElectron.git
synced 2024-11-21 08:34:40 -06:00
SDA-4089 Browser login autoconnect (#1809)
* RTC-13931 Disable D3D11 by default * SDA-4089 Browser login autolaunch * SDA-4089 Browser login autolaunch * SDA-4089 Browser login autolaunch * SDA-4089 Browser login autolaunch * SDA-4089 Browser login autolaunch * SDA-4089 Browser login autolaunch * SDA-4089 Browser login autolaunch * cleanup
This commit is contained in:
parent
6f9239e577
commit
0af813ae97
@ -5,6 +5,7 @@
|
||||
"isAutoUpdateEnabled": true,
|
||||
"autoUpdateCheckInterval": "30",
|
||||
"enableBrowserLogin": false,
|
||||
"browserLoginAutoConnect": false,
|
||||
"overrideUserAgent": false,
|
||||
"minimizeOnClose" : "ENABLED",
|
||||
"launchOnStartup" : "ENABLED",
|
||||
|
@ -18,6 +18,7 @@ always_on_top=$(sed -n '5p' ${settingsFilePath});
|
||||
bring_to_front=$(sed -n '6p' ${settingsFilePath});
|
||||
dev_tools_enabled=$(sed -n '7p' ${settingsFilePath});
|
||||
enable_browser_login=$(sed -n '8p' ${settingsFilePath});
|
||||
browser_login_autoconnect=$(sed -n '9p' ${settingsFilePath});
|
||||
|
||||
## If any of the above values turn out to be empty, set default values ##
|
||||
if [ "$pod_url" = "" ]; then pod_url="https://my.symphony.com"; fi
|
||||
@ -28,6 +29,8 @@ if [ "$always_on_top" = "" ] || [ "$always_on_top" = 'false' ]; then always_on_t
|
||||
if [ "$bring_to_front" = "" ] || [ "$bring_to_front" = 'false' ]; then bring_to_front='DISABLED'; else bring_to_front='ENABLED'; fi
|
||||
if [ "$dev_tools_enabled" = "" ]; then dev_tools_enabled=true; fi
|
||||
if [ "$enable_browser_login" = "" ]; then enable_browser_login=false; fi
|
||||
if [ "$browser_login_autoconnect" = "" ]; then browser_login_autoconnect=false; fi
|
||||
|
||||
pod_url_escaped=$(sed 's#[&/\]#\\&#g' <<<"$pod_url")
|
||||
context_origin_url_escaped=$(sed 's#[&/\]#\\&#g' <<<"$context_origin_url")
|
||||
|
||||
@ -40,6 +43,7 @@ sed -i "" -E "s#\"launchOnStartup\" ?: ?\"([Ee][Nn][Aa][Bb][Ll][Ee][Dd]|[Dd][Ii]
|
||||
sed -i "" -E "s#\"bringToFront\" ?: ?\"([Ee][Nn][Aa][Bb][Ll][Ee][Dd]|[Dd][Ii][Ss][Aa][Bb][Ll][Ee][Dd])\"#\"bringToFront\":\ \"$bring_to_front\"#g" "${newPath}"
|
||||
sed -i "" -E "s#\"devToolsEnabled\" ?: ?([Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee])#\"devToolsEnabled\":\ $dev_tools_enabled#g" "${newPath}"
|
||||
sed -i "" -E "s#\"enableBrowserLogin\" ?: ?([Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee])#\"enableBrowserLogin\":\ $enable_browser_login#g" "${newPath}"
|
||||
sed -i "" -E "s#\"browserLoginAutoConnect\" ?: ?([Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee])#\"browserLoginAutoConnect\":\ $browser_login_autoconnect#g" "${newPath}"
|
||||
|
||||
## Get Symphony Permissions from the temp file ##
|
||||
media=$(sed -n '1p' ${permissionsFilePath});
|
||||
|
@ -159,6 +159,7 @@ class Script
|
||||
new PublicProperty("USER_DATA_PATH", ""),
|
||||
new PublicProperty("OVERRIDE_USER_AGENT", "false"),
|
||||
new PublicProperty("ENABLE_BROWSER_LOGIN", "false"),
|
||||
new PublicProperty("BROWSER_LOGIN_AUTOCONNECT", "false"),
|
||||
new PublicProperty("CHROME_FLAGS", ""),
|
||||
new Property("MSIINSTALLPERUSER", "1"),
|
||||
new Property("PROGRAMSFOLDER", System.Environment.ExpandEnvironmentVariables(@"%PROGRAMFILES%"))
|
||||
@ -187,7 +188,7 @@ class Script
|
||||
new ElevatedManagedAction(CustomActions.UpdateConfig, Return.check, When.After, Step.InstallFiles, Condition.NOT_BeingRemoved )
|
||||
{
|
||||
// The UpdateConfig action needs the built-in property INSTALLDIR as well as most of the custom properties
|
||||
UsesProperties = "INSTALLDIR,POD_URL,CONTEXT_ORIGIN_URL,MINIMIZE_ON_CLOSE,ALWAYS_ON_TOP,AUTO_START,BRING_TO_FRONT,MEDIA,LOCATION,NOTIFICATIONS,MIDI_SYSEX,POINTER_LOCK,FULL_SCREEN,OPEN_EXTERNAL,CUSTOM_TITLE_BAR,DEV_TOOLS_ENABLED,AUTO_LAUNCH_PATH,USER_DATA_PATH,OVERRIDE_USER_AGENT,CHROME_FLAGS,ENABLE_BROWSER_LOGIN"
|
||||
UsesProperties = "INSTALLDIR,POD_URL,CONTEXT_ORIGIN_URL,MINIMIZE_ON_CLOSE,ALWAYS_ON_TOP,AUTO_START,BRING_TO_FRONT,MEDIA,LOCATION,NOTIFICATIONS,MIDI_SYSEX,POINTER_LOCK,FULL_SCREEN,OPEN_EXTERNAL,CUSTOM_TITLE_BAR,DEV_TOOLS_ENABLED,AUTO_LAUNCH_PATH,USER_DATA_PATH,OVERRIDE_USER_AGENT,CHROME_FLAGS,ENABLE_BROWSER_LOGIN,BROWSER_LOGIN_AUTOCONNECT"
|
||||
},
|
||||
|
||||
// CleanRegistry
|
||||
@ -362,6 +363,7 @@ public class CustomActions
|
||||
data = ReplaceBooleanProperty(data, "devToolsEnabled", session.Property("DEV_TOOLS_ENABLED"));
|
||||
data = ReplaceBooleanProperty(data, "overrideUserAgent", session.Property("OVERRIDE_USER_AGENT"));
|
||||
data = ReplaceBooleanProperty(data, "enableBrowserLogin", session.Property("ENABLE_BROWSER_LOGIN"));
|
||||
data = ReplaceBooleanProperty(data, "browserLoginAutoConnect", session.Property("BROWSER_LOGIN_AUTOCONNECT"));
|
||||
// Write the contents back to the file
|
||||
System.IO.File.WriteAllText(filename, data);
|
||||
}
|
||||
|
@ -535,15 +535,15 @@ Expected values:
|
||||
Expected values:
|
||||
|
||||
* "true"
|
||||
SDA will authenticate the user by relying on third-party browser
|
||||
SDA will authenticate the user by relying on default browser
|
||||
* "false"
|
||||
SDA will authenticate the user in SDA
|
||||
|
||||
#### Example, install with user-agent override
|
||||
#### Example, install with browser login enabled
|
||||
|
||||
msiexec /i Symphony.msi ENABLE_BROWSER_LOGIN="true"
|
||||
|
||||
#### Example, install without user-agent override
|
||||
#### Example, install without browser login enabled
|
||||
|
||||
msiexec /i Symphony.msi ENABLE_BROWSER_LOGIN="false"
|
||||
|
||||
@ -554,3 +554,25 @@ or
|
||||
|
||||
|
||||
-------------------------------------------------------------------
|
||||
### BROWSER_LOGIN_AUTOCONNECT
|
||||
|
||||
Acts in combination with ENABLE_BROWSER_LOGIN, if ENABLE_BROWSER_LOGIN is set to true.
|
||||
|
||||
Expected values:
|
||||
|
||||
* "true"
|
||||
SDA will automatically authenticate the user by relying on default browser
|
||||
* "false"
|
||||
User will need to click on Login button to start browser login flow.
|
||||
|
||||
#### Example, install with browser login autoconnect enabled
|
||||
|
||||
msiexec /i Symphony.msi BROWSER_LOGIN_AUTOCONNECT="true"
|
||||
|
||||
#### Example, install with browser login autoconnect disabled
|
||||
|
||||
msiexec /i Symphony.msi BROWSER_LOGIN_AUTOCONNECT="false"
|
||||
|
||||
or
|
||||
|
||||
msiexec /i Symphony.msi
|
||||
|
@ -99,35 +99,60 @@ exports[`welcome should render correctly 1`] = `
|
||||
1,000 institutions.
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
className="Welcome-login-text"
|
||||
>
|
||||
<span>
|
||||
Log in with your pod URL
|
||||
</span>
|
||||
<div
|
||||
className="Welcome-login-text"
|
||||
>
|
||||
<span>
|
||||
Log in with your pod URL
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className="Welcome-input-container"
|
||||
>
|
||||
<span>
|
||||
Pod URL
|
||||
</span>
|
||||
<div>
|
||||
<input
|
||||
className="Welcome-main-container-podurl-box"
|
||||
data-testid="Welcome-main-container-podurl-box"
|
||||
disabled={false}
|
||||
onChange={[Function]}
|
||||
tabIndex={0}
|
||||
type="url"
|
||||
value="https://[POD].symphony.com"
|
||||
/>
|
||||
<label
|
||||
className="Welcome-input-message"
|
||||
>
|
||||
Find your pod URL in your invitation email.
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
className="Welcome-input-container"
|
||||
</div>
|
||||
<div
|
||||
className="Welcome-auto-connect-wrapper"
|
||||
>
|
||||
<label
|
||||
className="switch"
|
||||
>
|
||||
<span>
|
||||
Pod URL
|
||||
<input
|
||||
checked={false}
|
||||
disabled={false}
|
||||
onChange={[Function]}
|
||||
type="checkbox"
|
||||
/>
|
||||
<span
|
||||
className="slider round"
|
||||
/>
|
||||
</label>
|
||||
<div
|
||||
className="auto-connect-labels"
|
||||
>
|
||||
<span
|
||||
className="auto-connect-label"
|
||||
>
|
||||
Automatically redirect to your web browser on launch
|
||||
</span>
|
||||
<div>
|
||||
<input
|
||||
className="Welcome-main-container-podurl-box"
|
||||
data-testid="Welcome-main-container-podurl-box"
|
||||
onChange={[Function]}
|
||||
tabIndex={0}
|
||||
type="url"
|
||||
value="https://[POD].symphony.com"
|
||||
/>
|
||||
<label
|
||||
className="Welcome-input-message"
|
||||
>
|
||||
Find your pod URL in your invitation email.
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
|
@ -10,7 +10,8 @@ describe('welcome', () => {
|
||||
message: '',
|
||||
urlValid: true,
|
||||
isPodConfigured: false,
|
||||
isSeamlessLoginEnabled: true,
|
||||
isBrowserLoginEnabled: false,
|
||||
browserLoginAutoConnect: false,
|
||||
};
|
||||
const onLabelEvent = 'on';
|
||||
const removeListenerLabelEvent = 'removeListener';
|
||||
@ -26,17 +27,6 @@ describe('welcome', () => {
|
||||
expect(spy).toBeCalledWith(welcomeLabel, expect.any(Function));
|
||||
});
|
||||
|
||||
it('should remove listener `welcome` when component is unmounted', () => {
|
||||
const spyMount = jest.spyOn(ipcRenderer, onLabelEvent);
|
||||
const spyUnmount = jest.spyOn(ipcRenderer, removeListenerLabelEvent);
|
||||
|
||||
const wrapper = shallow(React.createElement(Welcome));
|
||||
expect(spyMount).toBeCalledWith(welcomeLabel, expect.any(Function));
|
||||
|
||||
wrapper.unmount();
|
||||
expect(spyUnmount).toBeCalledWith(welcomeLabel, expect.any(Function));
|
||||
});
|
||||
|
||||
it('should call `updateState` when component is mounted', () => {
|
||||
const spy = jest.spyOn(Welcome.prototype, 'setState');
|
||||
shallow(React.createElement(Welcome));
|
||||
@ -113,11 +103,24 @@ describe('welcome', () => {
|
||||
message: '',
|
||||
urlValid: true,
|
||||
isPodConfigured: true,
|
||||
isSeamlessLoginEnabled: true,
|
||||
isBrowserLoginEnabled: false,
|
||||
browserLoginAutoConnect: false,
|
||||
isLoading: false,
|
||||
};
|
||||
const wrapper = shallow(React.createElement(Welcome));
|
||||
ipcRenderer.send('welcome', welcomeMock);
|
||||
const podUrlBox = `input.Welcome-main-container-podurl-box`;
|
||||
expect(wrapper.find(podUrlBox).getElements()).toEqual([]);
|
||||
});
|
||||
|
||||
it('should remove listener `welcome` when component is unmounted', () => {
|
||||
const spyMount = jest.spyOn(ipcRenderer, onLabelEvent);
|
||||
const spyUnmount = jest.spyOn(ipcRenderer, removeListenerLabelEvent);
|
||||
|
||||
const wrapper = shallow(React.createElement(Welcome));
|
||||
expect(spyMount).toBeCalledWith(welcomeLabel, expect.any(Function));
|
||||
|
||||
wrapper.unmount();
|
||||
expect(spyUnmount).toBeCalledWith(welcomeLabel, expect.any(Function));
|
||||
});
|
||||
});
|
||||
|
@ -517,6 +517,7 @@ export class AppMenu {
|
||||
enabled:
|
||||
!bringToFrontCC || bringToFrontCC === CloudConfigDataTypes.NOT_SET,
|
||||
},
|
||||
this.buildSeparator(),
|
||||
{
|
||||
type: 'checkbox',
|
||||
label: i18n.t('Browser login')(),
|
||||
|
@ -95,6 +95,9 @@ export class AutoUpdate {
|
||||
data: info,
|
||||
});
|
||||
}
|
||||
if (isMac) {
|
||||
config.backupGlobalConfig();
|
||||
}
|
||||
});
|
||||
|
||||
this.autoUpdater.on('error', (error) => {
|
||||
|
@ -58,7 +58,8 @@ export interface IConfig {
|
||||
installVariant?: string;
|
||||
bootCount?: number;
|
||||
startedAfterAutoUpdate?: boolean;
|
||||
enableBrowserLogin: boolean;
|
||||
enableBrowserLogin?: boolean;
|
||||
browserLoginAutoConnect?: boolean;
|
||||
}
|
||||
|
||||
export interface IGlobalConfig {
|
||||
|
@ -69,7 +69,7 @@ const broadcastMessage = (method, data) => {
|
||||
mainEvents.publish(apiCmds.onSwiftSearchMessage, [method, data]);
|
||||
};
|
||||
|
||||
const getSeamlessLoginUrl = (pod: string) =>
|
||||
const getBrowserLoginUrl = (pod: string) =>
|
||||
`${pod}/login/sso/initsso?RelayState=${pod}/client-bff/device-login/index.html?callbackScheme=symphony&action=login`;
|
||||
const AUTH_STATUS_PATH = '/login/checkauth?type=user';
|
||||
/**
|
||||
@ -384,9 +384,14 @@ ipcMain.on(
|
||||
mainWebContents.focus();
|
||||
}
|
||||
break;
|
||||
case apiCmds.seamlessLogin:
|
||||
case apiCmds.browserLogin:
|
||||
await config.updateUserConfig({
|
||||
browserLoginAutoConnect: arg.browserLoginAutoConnect,
|
||||
});
|
||||
if (!arg.isPodConfigured) {
|
||||
await config.updateUserConfig({ url: arg.newPodUrl });
|
||||
await config.updateUserConfig({
|
||||
url: arg.newPodUrl,
|
||||
});
|
||||
}
|
||||
const urlFromCmd = getCommandLineArgs(process.argv, '--url=', false);
|
||||
const { url: userConfigURL } = config.getUserConfigFields(['url']);
|
||||
@ -398,7 +403,7 @@ ipcMain.on(
|
||||
: globalConfigURL;
|
||||
const { subdomain, domain, tld } = whitelistHandler.parseDomain(podUrl);
|
||||
const formattedPodUrl = `https://${subdomain}.${domain}${tld}`;
|
||||
const loginUrl = getSeamlessLoginUrl(formattedPodUrl);
|
||||
const loginUrl = getBrowserLoginUrl(formattedPodUrl);
|
||||
logger.info(
|
||||
'main-api-handler:',
|
||||
'check if sso is enabled for the pod',
|
||||
@ -408,19 +413,19 @@ ipcMain.on(
|
||||
const authResponse = (await response.json()) as IAuthResponse;
|
||||
logger.info('main-api-handler:', 'check auth response', authResponse);
|
||||
if (
|
||||
arg.isSeamlessLoginEnabled &&
|
||||
arg.isBrowserLoginEnabled &&
|
||||
authResponse.authenticationType === 'sso'
|
||||
) {
|
||||
logger.info(
|
||||
'main-api-handler:',
|
||||
'seamless login is enabled - logging in',
|
||||
'browser login is enabled - logging in',
|
||||
loginUrl,
|
||||
);
|
||||
await shell.openExternal(loginUrl);
|
||||
} else {
|
||||
logger.info(
|
||||
'main-api-handler:',
|
||||
'seamless login is not enabled - loading main window with',
|
||||
'browser login is not enabled - loading main window with',
|
||||
formattedPodUrl,
|
||||
);
|
||||
const mainWebContents = windowHandler.getMainWebContents();
|
||||
|
@ -63,7 +63,7 @@ class ProtocolHandler {
|
||||
);
|
||||
// Handle protocol for Seamless login
|
||||
if (url?.includes('skey') && url?.includes('anticsrf')) {
|
||||
await this.handleSeamlessLogin(url);
|
||||
await this.handleBrowserLogin(url);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -112,11 +112,12 @@ class ProtocolHandler {
|
||||
/**
|
||||
* Sets session cookies and navigates to the pod url
|
||||
*/
|
||||
public async handleSeamlessLogin(protocolUri: string): Promise<void> {
|
||||
public async handleBrowserLogin(protocolUri: string): Promise<void> {
|
||||
const globalConfig = config.getGlobalConfigFields(['url']);
|
||||
const userConfig = config.getUserConfigFields(['url']);
|
||||
const url = userConfig.url ? userConfig.url : globalConfig.url;
|
||||
const { subdomain, tld, domain } = whitelistHandler.parseDomain(url);
|
||||
const redirectURL = userConfig.url ? userConfig.url : globalConfig.url;
|
||||
const { subdomain, tld, domain } =
|
||||
whitelistHandler.parseDomain(redirectURL);
|
||||
const cookieDomain = `.${subdomain}.${domain}${tld}`;
|
||||
if (protocolUri) {
|
||||
const urlParams = new URLSearchParams(new URL(protocolUri).search);
|
||||
@ -124,7 +125,7 @@ class ProtocolHandler {
|
||||
const anticsrfValue = urlParams.get('anticsrf');
|
||||
if (skeyValue && anticsrfValue) {
|
||||
const skeyCookie: CookiesSetDetails = {
|
||||
url,
|
||||
url: redirectURL,
|
||||
name: 'skey',
|
||||
value: skeyValue,
|
||||
secure: true,
|
||||
@ -133,7 +134,7 @@ class ProtocolHandler {
|
||||
domain: cookieDomain,
|
||||
};
|
||||
const csrfCookie: CookiesSetDetails = {
|
||||
url,
|
||||
url: redirectURL,
|
||||
name: 'anti-csrf-cookie',
|
||||
value: anticsrfValue,
|
||||
secure: true,
|
||||
@ -152,10 +153,13 @@ class ProtocolHandler {
|
||||
}
|
||||
}
|
||||
const mainWebContents = windowHandler.getMainWebContents();
|
||||
if (mainWebContents && !mainWebContents?.isDestroyed() && url) {
|
||||
logger.info('protocol-handler: redirecting main webContents ', url);
|
||||
windowHandler.setMainWindowOrigin(url);
|
||||
mainWebContents?.loadURL(url);
|
||||
if (mainWebContents && !mainWebContents?.isDestroyed() && redirectURL) {
|
||||
logger.info(
|
||||
'protocol-handler: redirecting main webContents ',
|
||||
redirectURL,
|
||||
);
|
||||
windowHandler.setMainWindowOrigin(redirectURL);
|
||||
mainWebContents?.loadURL(redirectURL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ import { isMac, isWindowsOS } from '../../common/env';
|
||||
export class PresenceStatus {
|
||||
private presenceStatus: IStatusBadge = {
|
||||
statusCategory: EPresenceStatusCategory.NO_PRESENCE,
|
||||
statusGroup: EPresenceStatusGroup.OFFLINE,
|
||||
statusGroup: EPresenceStatusGroup.HIDE_PRESENCE,
|
||||
count: 0,
|
||||
};
|
||||
private tray: ITray = {
|
||||
|
@ -222,6 +222,7 @@ export class WindowHandler {
|
||||
'clientSwitch',
|
||||
'enableRendererLogs',
|
||||
'enableBrowserLogin',
|
||||
'browserLoginAutoConnect',
|
||||
]);
|
||||
logger.info(
|
||||
`window-handler: main windows initialized with following config data`,
|
||||
@ -252,7 +253,7 @@ export class WindowHandler {
|
||||
this.isPodConfigured = !config.isFirstTimeLaunch();
|
||||
this.didShowWelcomeScreen = false;
|
||||
this.shouldShowWelcomeScreen =
|
||||
config.isFirstTimeLaunch() || this.config.enableBrowserLogin;
|
||||
config.isFirstTimeLaunch() || !!this.config.enableBrowserLogin;
|
||||
|
||||
this.windowOpts = {
|
||||
...this.getWindowOpts(
|
||||
@ -566,7 +567,8 @@ export class WindowHandler {
|
||||
message: '',
|
||||
urlValid: !!userConfigUrl,
|
||||
isPodConfigured: this.isPodConfigured && !!userConfigUrl,
|
||||
isSeamlessLoginEnabled: this.config.enableBrowserLogin,
|
||||
isBrowserLoginEnabled: this.config.enableBrowserLogin,
|
||||
browserLoginAutoConnect: this.config.browserLoginAutoConnect,
|
||||
});
|
||||
this.didShowWelcomeScreen = true;
|
||||
this.mainWebContents.focus();
|
||||
@ -860,11 +862,6 @@ export class WindowHandler {
|
||||
return;
|
||||
}
|
||||
logger.info(`finished loading welcome screen.`);
|
||||
const ssoValue = !!(
|
||||
this.userConfig.url &&
|
||||
this.userConfig.url.indexOf('/login/sso/initsso') > -1
|
||||
);
|
||||
|
||||
this.welcomeScreenWindow.webContents.send('page-load-welcome', {
|
||||
locale: i18n.getLocale(),
|
||||
resource: i18n.loadedResources,
|
||||
@ -882,7 +879,6 @@ export class WindowHandler {
|
||||
url: userConfigUrl || this.startUrl,
|
||||
message: '',
|
||||
urlValid: !!userConfigUrl,
|
||||
sso: ssoValue,
|
||||
});
|
||||
this.appMenu = new AppMenu();
|
||||
this.addWindow(opts.winKey, this.welcomeScreenWindow);
|
||||
|
@ -72,7 +72,7 @@ export enum apiCmds {
|
||||
updateAndRestart = 'update-and-restart',
|
||||
downloadUpdate = 'download-update',
|
||||
checkForUpdates = 'check-for-updates',
|
||||
seamlessLogin = 'seamless-login',
|
||||
browserLogin = 'browser-login',
|
||||
updateMyPresence = 'update-my-presence',
|
||||
getMyPresence = 'get-my-presence',
|
||||
updateSymphonyTray = 'update-system-tray',
|
||||
@ -125,7 +125,8 @@ export interface IApiArgs {
|
||||
newPodUrl: string;
|
||||
startUrl: string;
|
||||
isPodConfigured: boolean;
|
||||
isSeamlessLoginEnabled: boolean;
|
||||
isBrowserLoginEnabled: boolean;
|
||||
browserLoginAutoConnect: boolean;
|
||||
swiftSearchData: any;
|
||||
types: string[];
|
||||
thumbnailSize: Size;
|
||||
|
@ -223,6 +223,7 @@
|
||||
"Find your pod URL in your invitation email.": "Find your pod URL in your invitation email.",
|
||||
"Welcome to the largest global community in financial services with over": "Welcome to the largest global community in financial services with over",
|
||||
" half a million users": " half a million users",
|
||||
" and more than": " and more than",
|
||||
" 1,000 institutions.": " 1,000 institutions.",
|
||||
"Log in with your pod URL": "Log in with your pod URL",
|
||||
"You’ll momentarily be redirected to your web browser.": "You’ll momentarily be redirected to your web browser.",
|
||||
@ -231,7 +232,9 @@
|
||||
"Pod URL": "Pod URL",
|
||||
"SSO": "SSO",
|
||||
"Symphony Logo": "Symphony",
|
||||
"WelcomeText": "Welcome"
|
||||
"WelcomeText": "Welcome",
|
||||
"Automatically redirect to your web browser on launch": "Automatically redirect to your web browser on launch",
|
||||
"Retry": "Retry"
|
||||
},
|
||||
"Window": "Window",
|
||||
"Would you like to restart and apply these new settings now?": "Would you like to restart and apply these new settings now?",
|
||||
|
@ -223,6 +223,7 @@
|
||||
"Find your pod URL in your invitation email.": "Find your pod URL in your invitation email.",
|
||||
"Welcome to the largest global community in financial services with over": "Welcome to the largest global community in financial services with over",
|
||||
" half a million users": " half a million users",
|
||||
" and more than": " and more than",
|
||||
" 1,000 institutions.": " 1,000 institutions.",
|
||||
"Log in with your pod URL": "Log in with your pod URL",
|
||||
"You’ll momentarily be redirected to your web browser.": "You’ll momentarily be redirected to your web browser.",
|
||||
@ -231,7 +232,9 @@
|
||||
"Pod URL": "Pod URL",
|
||||
"SSO": "SSO",
|
||||
"Symphony Logo": "Symphony",
|
||||
"WelcomeText": "Welcome"
|
||||
"WelcomeText": "Welcome",
|
||||
"Automatically redirect to your web browser on launch": "Automatically redirect to your web browser on launch",
|
||||
"Retry": "Retry"
|
||||
},
|
||||
"Window": "Window",
|
||||
"Would you like to restart and apply these new settings now?": "Would you like to restart and apply these new settings now?",
|
||||
|
@ -223,6 +223,7 @@
|
||||
"Find your pod URL in your invitation email.": "L'URL de votre instance Symphony est indiquée dans l'email d'invitation.",
|
||||
"Welcome to the largest global community in financial services with over": "Bienvenue dans la communauté des services financiers la plus large au monde avec plus d'",
|
||||
" half a million users": " un demi million d'usagers",
|
||||
" and more than": " et plus de",
|
||||
" 1,000 institutions.": " 1000 institutions.",
|
||||
"Log in with your pod URL": "Connectez-vous avec l'URL de votre instance",
|
||||
"You’ll momentarily be redirected to your web browser.": "Vous allez être redirigé(e) brièvement vers votre navigateur internet.",
|
||||
|
@ -223,6 +223,7 @@
|
||||
"Find your pod URL in your invitation email.": "Find your pod URL in your invitation email.",
|
||||
"Welcome to the largest global community in financial services with over": "Welcome to the largest global community in financial services with over",
|
||||
" half a million users": " half a million users",
|
||||
" and more than": " et plus de",
|
||||
" 1,000 institutions.": " 1,000 institutions.",
|
||||
"Log in with your pod URL": "Log in with your pod URL",
|
||||
"You’ll momentarily be redirected to your web browser.": "You’ll momentarily be redirected to your web browser.",
|
||||
|
@ -223,6 +223,7 @@
|
||||
"Find your pod URL in your invitation email.": "招待メールでpod URLをご確認ください。",
|
||||
"Welcome to the largest global community in financial services with over": "Welcome to the largest global community in financial services with over",
|
||||
" half a million users": " half a million users",
|
||||
" and more than": " and more than",
|
||||
" 1,000 institutions.": " 1,000 institutions.",
|
||||
"Log in with your pod URL": "Pod URLからログインください",
|
||||
"You’ll momentarily be redirected to your web browser.": "まもなくウェブブラウザーにリダイレクトさせていただきます。",
|
||||
|
@ -223,6 +223,7 @@
|
||||
"Find your pod URL in your invitation email.": "招待メールでpod URLをご確認ください。",
|
||||
"Welcome to the largest global community in financial services with over": "Welcome to the largest global community in financial services with over",
|
||||
" half a million users": " half a million users",
|
||||
" and more than": " and more than",
|
||||
" 1,000 institutions.": " 1,000 institutions.",
|
||||
"Log in with your pod URL": "Pod URLからログインください",
|
||||
"You’ll momentarily be redirected to your web browser.": "まもなくウェブブラウザーにリダイレクトさせていただきます。",
|
||||
|
@ -8,19 +8,23 @@ interface IState {
|
||||
message: string;
|
||||
urlValid: boolean;
|
||||
isPodConfigured: boolean;
|
||||
isSeamlessLoginEnabled: boolean;
|
||||
isBrowserLoginEnabled: boolean;
|
||||
browserLoginAutoConnect: boolean;
|
||||
isLoading: boolean;
|
||||
}
|
||||
|
||||
const WELCOME_NAMESPACE = 'Welcome';
|
||||
const DEFAULT_MESSAGE = 'Find your pod URL in your invitation email.';
|
||||
const DEFAULT_POD_URL = 'https://[POD].symphony.com';
|
||||
const BROWSER_LOGIN_AUTO_REDIRECT_TIMEOUT = 2000;
|
||||
|
||||
export default class Welcome extends React.Component<{}, IState> {
|
||||
private readonly eventHandlers = {
|
||||
onLogin: () => this.login(),
|
||||
};
|
||||
|
||||
private browserLoginTimeoutId: NodeJS.Timeout | undefined;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
@ -28,7 +32,8 @@ export default class Welcome extends React.Component<{}, IState> {
|
||||
message: '',
|
||||
urlValid: false,
|
||||
isPodConfigured: false,
|
||||
isSeamlessLoginEnabled: true,
|
||||
isBrowserLoginEnabled: true,
|
||||
browserLoginAutoConnect: false,
|
||||
isLoading: false,
|
||||
};
|
||||
this.updateState = this.updateState.bind(this);
|
||||
@ -38,33 +43,44 @@ export default class Welcome extends React.Component<{}, IState> {
|
||||
* Render the component
|
||||
*/
|
||||
public render(): JSX.Element {
|
||||
const { url, message, isPodConfigured } = this.state;
|
||||
const { url, message, isPodConfigured, isLoading, isBrowserLoginEnabled } =
|
||||
this.state;
|
||||
return (
|
||||
<div className='Welcome' lang={i18n.getLocale()}>
|
||||
<div className='Welcome-content'>
|
||||
<div className='Welcome-image-container'>
|
||||
{this.getWelcomeImage()}
|
||||
</div>
|
||||
<div
|
||||
className='Welcome-about-symphony-text'
|
||||
style={{ marginTop: isPodConfigured ? '35px' : '8px' }}
|
||||
>
|
||||
<span>
|
||||
{i18n.t(
|
||||
'Welcome to the largest global community in financial services with over',
|
||||
WELCOME_NAMESPACE,
|
||||
)()}
|
||||
</span>
|
||||
<span className='Welcome-text-bold'>
|
||||
{i18n.t(' half a million users', WELCOME_NAMESPACE)()}
|
||||
</span>
|
||||
<span>{i18n.t(' and more than', WELCOME_NAMESPACE)()}</span>
|
||||
<span className='Welcome-text-bold'>
|
||||
{i18n.t(' 1,000 institutions.', WELCOME_NAMESPACE)()}
|
||||
</span>
|
||||
</div>
|
||||
{isPodConfigured && (
|
||||
<React.Fragment>
|
||||
<span className='Welcome-welcome-back'>
|
||||
{i18n.t('Welcome back!', WELCOME_NAMESPACE)()}
|
||||
</span>
|
||||
<span className='Welcome-login-label'>
|
||||
{i18n.t('Please login to continue', WELCOME_NAMESPACE)()}
|
||||
</span>
|
||||
</React.Fragment>
|
||||
)}
|
||||
{!isPodConfigured && (
|
||||
<div>
|
||||
<React.Fragment>
|
||||
<div
|
||||
className='Welcome-about-symphony-text'
|
||||
style={{ marginTop: isPodConfigured ? '35px' : '8px' }}
|
||||
>
|
||||
<span>
|
||||
{i18n.t(
|
||||
'Welcome to the largest global community in financial services with over',
|
||||
WELCOME_NAMESPACE,
|
||||
)()}
|
||||
</span>
|
||||
<span className='Welcome-text-bold'>
|
||||
{i18n.t(' half a million users', WELCOME_NAMESPACE)()}
|
||||
</span>
|
||||
<span>{i18n.t(' and more than', WELCOME_NAMESPACE)()}</span>
|
||||
<span className='Welcome-text-bold'>
|
||||
{i18n.t(' 1,000 institutions.', WELCOME_NAMESPACE)()}
|
||||
</span>
|
||||
</div>
|
||||
<div className='Welcome-login-text'>
|
||||
<span>
|
||||
{i18n.t('Log in with your pod URL', WELCOME_NAMESPACE)()}
|
||||
@ -74,6 +90,7 @@ export default class Welcome extends React.Component<{}, IState> {
|
||||
<span>{i18n.t('Pod URL', WELCOME_NAMESPACE)()}</span>
|
||||
<div>
|
||||
<input
|
||||
disabled={isLoading}
|
||||
data-testid={'Welcome-main-container-podurl-box'}
|
||||
className='Welcome-main-container-podurl-box'
|
||||
tabIndex={0}
|
||||
@ -88,19 +105,29 @@ export default class Welcome extends React.Component<{}, IState> {
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
)}
|
||||
|
||||
{this.renderLoginButton()}
|
||||
|
||||
<div className='Welcome-redirect-info-text-container'>
|
||||
<span>
|
||||
{i18n.t(
|
||||
'You’ll momentarily be redirected to your web browser.',
|
||||
WELCOME_NAMESPACE,
|
||||
)()}
|
||||
</span>
|
||||
</div>
|
||||
{isBrowserLoginEnabled && (
|
||||
<div className='Welcome-redirect-info-text-container'>
|
||||
<span>
|
||||
{i18n.t(
|
||||
'You’ll momentarily be redirected to your web browser.',
|
||||
WELCOME_NAMESPACE,
|
||||
)()}
|
||||
</span>
|
||||
{isLoading && (
|
||||
<button
|
||||
className='Welcome-retry-button'
|
||||
onClick={this.eventHandlers.onLogin}
|
||||
>
|
||||
{i18n.t('Retry', WELCOME_NAMESPACE)()}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -110,7 +137,18 @@ export default class Welcome extends React.Component<{}, IState> {
|
||||
* Perform actions on component being mounted
|
||||
*/
|
||||
public componentDidMount(): void {
|
||||
ipcRenderer.on('welcome', this.updateState);
|
||||
ipcRenderer.on('welcome', (_event, data) => {
|
||||
if (
|
||||
data.isPodConfigured &&
|
||||
data.browserLoginAutoConnect &&
|
||||
data.isBrowserLoginEnabled
|
||||
) {
|
||||
this.browserLoginTimeoutId = setTimeout(() => {
|
||||
this.login();
|
||||
}, BROWSER_LOGIN_AUTO_REDIRECT_TIMEOUT);
|
||||
}
|
||||
this.updateState(_event, data);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -125,12 +163,18 @@ export default class Welcome extends React.Component<{}, IState> {
|
||||
*/
|
||||
public login(): void {
|
||||
this.setState({ isLoading: true });
|
||||
const { url, isPodConfigured, isSeamlessLoginEnabled } = this.state;
|
||||
const {
|
||||
url,
|
||||
isPodConfigured,
|
||||
isBrowserLoginEnabled,
|
||||
browserLoginAutoConnect,
|
||||
} = this.state;
|
||||
ipcRenderer.send(apiName.symphonyApi, {
|
||||
cmd: apiCmds.seamlessLogin,
|
||||
cmd: apiCmds.browserLogin,
|
||||
newPodUrl: url,
|
||||
isPodConfigured,
|
||||
isSeamlessLoginEnabled,
|
||||
isBrowserLoginEnabled,
|
||||
browserLoginAutoConnect,
|
||||
});
|
||||
}
|
||||
|
||||
@ -159,13 +203,73 @@ export default class Welcome extends React.Component<{}, IState> {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update pod url from the text box
|
||||
* @param event
|
||||
*/
|
||||
public updateBrowserLoginAutoConnect(event) {
|
||||
const { urlValid } = this.state;
|
||||
const browserLoginAutoConnect = event.target.checked;
|
||||
if (!browserLoginAutoConnect) {
|
||||
if (this.browserLoginTimeoutId) {
|
||||
clearTimeout(this.browserLoginTimeoutId);
|
||||
}
|
||||
this.setState({
|
||||
browserLoginAutoConnect,
|
||||
});
|
||||
}
|
||||
|
||||
if (urlValid && browserLoginAutoConnect) {
|
||||
this.setState({
|
||||
browserLoginAutoConnect,
|
||||
isLoading: true,
|
||||
});
|
||||
const { url, isPodConfigured, isBrowserLoginEnabled } = this.state;
|
||||
ipcRenderer.send(apiName.symphonyApi, {
|
||||
cmd: apiCmds.browserLogin,
|
||||
newPodUrl: url,
|
||||
isPodConfigured,
|
||||
isBrowserLoginEnabled,
|
||||
browserLoginAutoConnect,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders login button content
|
||||
*/
|
||||
private renderLoginButton() {
|
||||
const { isLoading, isPodConfigured, urlValid } = this.state;
|
||||
if (!isLoading) {
|
||||
return (
|
||||
const {
|
||||
isLoading,
|
||||
isPodConfigured,
|
||||
urlValid,
|
||||
isBrowserLoginEnabled,
|
||||
browserLoginAutoConnect,
|
||||
} = this.state;
|
||||
return (
|
||||
<React.Fragment>
|
||||
{isBrowserLoginEnabled && (
|
||||
<div className='Welcome-auto-connect-wrapper'>
|
||||
<label className='switch'>
|
||||
<input
|
||||
type='checkbox'
|
||||
disabled={isLoading}
|
||||
checked={browserLoginAutoConnect}
|
||||
onChange={this.updateBrowserLoginAutoConnect.bind(this)}
|
||||
/>
|
||||
<span className='slider round'></span>
|
||||
</label>
|
||||
<div className='auto-connect-labels'>
|
||||
<span className='auto-connect-label'>
|
||||
{i18n.t(
|
||||
'Automatically redirect to your web browser on launch',
|
||||
WELCOME_NAMESPACE,
|
||||
)()}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<button
|
||||
className='Welcome-continue-button'
|
||||
tabIndex={1}
|
||||
@ -173,29 +277,29 @@ export default class Welcome extends React.Component<{}, IState> {
|
||||
onClick={this.eventHandlers.onLogin}
|
||||
style={isPodConfigured ? { marginTop: '40px' } : {}}
|
||||
>
|
||||
{i18n.t('log in', WELCOME_NAMESPACE)()}
|
||||
{isLoading && (
|
||||
<div className='splash-screen--spinner-container'>
|
||||
<div
|
||||
className='splash-screen--spinner'
|
||||
role='progressbar'
|
||||
data-testid='SPLASH_SCREEN_SPINNER'
|
||||
>
|
||||
<svg viewBox='22 22 44 44'>
|
||||
<circle
|
||||
className='splash-screen--spinner-circle'
|
||||
cx='44'
|
||||
cy='44'
|
||||
r='20.2'
|
||||
fill='none'
|
||||
stroke-width='3.6'
|
||||
></circle>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!isLoading && i18n.t('log in', WELCOME_NAMESPACE)()}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className='splash-screen--spinner-container'>
|
||||
<div
|
||||
className='splash-screen--spinner'
|
||||
role='progressbar'
|
||||
data-testid='SPLASH_SCREEN_SPINNER'
|
||||
>
|
||||
<svg viewBox='22 22 44 44'>
|
||||
<circle
|
||||
className='splash-screen--spinner-circle'
|
||||
cx='44'
|
||||
cy='44'
|
||||
r='20.2'
|
||||
fill='none'
|
||||
stroke-width='3.6'
|
||||
></circle>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -7,11 +7,13 @@
|
||||
|
||||
// Color palette
|
||||
@electricity-ui-05: #e9f2f9;
|
||||
@electricity-ui-50: #0277d6;
|
||||
@electricity-ui-40: #2996fd;
|
||||
@electricity-ui-20: #aad4f8;
|
||||
@electricity-ui-30: #6eb9fd;
|
||||
@electricity-ui-40: #2996fd;
|
||||
@electricity-ui-50: #0277d6;
|
||||
@electricity-ui-60: #27588e;
|
||||
|
||||
@graphite-10: #e3e5e7;
|
||||
@graphite-20: #cdcfd4;
|
||||
@graphite-05: #f1f1f3;
|
||||
@graphite-80: #27292c;
|
||||
|
@ -220,10 +220,10 @@ body {
|
||||
text-decoration: none;
|
||||
line-height: 12px;
|
||||
background-color: @electricity-ui-50;
|
||||
color: #ffffff;
|
||||
color: @text-color-primary;
|
||||
cursor: pointer;
|
||||
text-transform: uppercase;
|
||||
margin: 24px 0 4px 0;
|
||||
margin: 16px 0 4px 0;
|
||||
|
||||
&:focus {
|
||||
box-shadow: 0 0 10px rgba(61, 162, 253, 1);
|
||||
@ -236,16 +236,61 @@ body {
|
||||
}
|
||||
|
||||
&-redirect-info-text-container {
|
||||
margin-top: 32px;
|
||||
margin-top: 40px;
|
||||
margin-bottom: 16px;
|
||||
|
||||
span {
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
line-height: 16px;
|
||||
text-align: center;
|
||||
color: @graphite-30;
|
||||
}
|
||||
}
|
||||
|
||||
&-retry-button {
|
||||
box-shadow: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
margin: 0 8px;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
text-decoration: none;
|
||||
background-color: transparent;
|
||||
color: @electricity-ui-30;
|
||||
}
|
||||
|
||||
&-auto-connect-wrapper {
|
||||
display: flex;
|
||||
margin-top: 18px;
|
||||
|
||||
.auto-connect-labels {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.auto-connect-label {
|
||||
font-size: 14px;
|
||||
color: @graphite-10;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 300;
|
||||
}
|
||||
.auto-connect-help-label {
|
||||
font-size: 12px;
|
||||
color: @graphite-30;
|
||||
}
|
||||
}
|
||||
}
|
||||
&-welcome-back {
|
||||
font-size: 14px;
|
||||
color: @graphite-05;
|
||||
margin-top: 40px;
|
||||
}
|
||||
&-login-label {
|
||||
font-size: 18px;
|
||||
color: @graphite-05;
|
||||
margin-top: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fade-in-logo {
|
||||
@ -313,15 +358,14 @@ body {
|
||||
.splash-screen--spinner-container {
|
||||
opacity: 0;
|
||||
animation: fade-in-spinner 1s ease-in 0s forwards;
|
||||
margin-top: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
.splash-screen--spinner {
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
color: #0277d6;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
color: @electricity-ui-50;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
animation: progress-circular-rotate 1.4s linear infinite;
|
||||
}
|
||||
.splash-screen--spinner-circle {
|
||||
@ -330,3 +374,80 @@ body {
|
||||
stroke-dashoffset: 0px;
|
||||
stroke: currentColor;
|
||||
}
|
||||
|
||||
.switch {
|
||||
margin: auto;
|
||||
margin-right: 8px;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 43px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
/* Hide default HTML checkbox */
|
||||
.switch input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
/* The slider */
|
||||
.slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
-webkit-transition: 0.4s;
|
||||
transition: 0.4s;
|
||||
background-color: @graphite-60;
|
||||
}
|
||||
|
||||
.slider:before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
height: 14px;
|
||||
width: 14px;
|
||||
bottom: -5px;
|
||||
-webkit-transition: 0.4s;
|
||||
transition: 0.4s;
|
||||
background-color: @graphite-80;
|
||||
border: 2px solid @electricity-ui-40;
|
||||
&:disabled {
|
||||
background-color: #090a0b;
|
||||
}
|
||||
}
|
||||
|
||||
input:checked + .slider:before {
|
||||
-webkit-transform: translateX(20px);
|
||||
-ms-transform: translateX(20px);
|
||||
transform: translateX(20px);
|
||||
background-color: @electricity-ui-40;
|
||||
}
|
||||
|
||||
input:not(:checked) + .slider {
|
||||
&:hover {
|
||||
&::before {
|
||||
border-color: @electricity-ui-30;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
input:checked + .slider {
|
||||
&:hover {
|
||||
&::before {
|
||||
background-color: @electricity-ui-30;
|
||||
border-color: @electricity-ui-30;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Rounded sliders */
|
||||
.slider.round {
|
||||
border-radius: 34px;
|
||||
}
|
||||
|
||||
.slider.round:before {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user