bitwarden-estensione-browser/src/main/menu.bitwarden.ts

238 lines
7.1 KiB
TypeScript
Raw Normal View History

[Account Switching] [Feature] Add the ability to maintain state for up to 5 accounts at once (#1079) * [refactor] Remove references to deprecated services * [feature] Implement account switching * [bug] Fix state handling for authentication dependent system menu items * [bug] Enable the account switcher to fucntion properly when switching to a locked accounts * [feature] Enable locking any account from the menu * [bug] Ensure the avatar instance used in the account switcher updates on account change * [style] Fix lint complaints * [bug] Ensure the logout command callback can handle any user in state * [style] Fix lint complaints * rollup * [style] Fix lint complaints * [bug] Don't clean up state until everything else is done on logout * [bug] Navigate to vault on a succesful account switch * [bug] Init the state service on start * [feature] Limit account switching to 5 account maximum * [bug] Resolve app lock state with 5 logged out accounts * [chore] Update account refrences to match recent jslib restructuring * [bug] Add missing awaits * [bug] Update app menu on logout * [bug] Hide the switcher if there are no authed accounts * [bug] Move authenticationStatus display information out of jslib * [bug] Remove unused active style from scss * [refactor] Rewrite the menu bar * [style] Fix lint complaints * [bug] Clean state of loggout out user after redirect * [bug] Redirect on logout if not explicity provided a userId that isn't active * [bug] Relocated several settings items to persistant storage * [bug] Correct account switcher styles on all themes * [chore] Include state migration service in services * [bug] Swap to next account on logout * [bug] Correct DI service * [bug] fix loginGuard deps in services.module * [chore] update jslib * [bug] Remove badly merged scss * [chore] update jslib * [review] Code review cleanup * [review] Code review cleanup Co-authored-by: Hinton <oscar@oscarhinton.com>
2021-12-15 23:32:00 +01:00
import {
BrowserWindow,
dialog,
MenuItem,
MenuItemConstructorOptions,
} from 'electron';
import { I18nService } from 'jslib-common/abstractions/i18n.service';
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
import { UpdaterMain } from 'jslib-electron/updater.main';
import { isMacAppStore, isSnapStore, isWindowsStore } from 'jslib-electron/utils';
import { IMenubarMenu } from './menubar';
import { MenuAccount } from './menu.updater';
// AKA: "FirstMenu" or "MacMenu" - the first menu that shows on all macOs apps
export class BitwardenMenu implements IMenubarMenu {
readonly id: string = 'bitwarden';
readonly label: string = 'Bitwarden';
get items(): MenuItemConstructorOptions[] {
return [
this.aboutBitwarden,
this.checkForUpdates,
this.separator,
this.settings,
this.lock,
this.lockAll,
this.logOut,
this.services,
this.separator,
this.hideBitwarden,
this.hideOthers,
this.showAll,
this.separator,
this.quitBitwarden,
];
}
private readonly _i18nService: I18nService;
private readonly _updater: UpdaterMain;
private readonly _messagingService: MessagingService;
private readonly _accounts: { [userId: string]: MenuAccount };
private readonly _window: BrowserWindow;
constructor(
i18nService: I18nService,
messagingService: MessagingService,
updater: UpdaterMain,
window: BrowserWindow,
accounts: { [userId: string]: MenuAccount },
) {
this._i18nService = i18nService;
this._updater = updater;
this._messagingService = messagingService;
this._window = window;
this._accounts = accounts;
}
private get hasAccounts(): boolean {
return this._accounts != null && Object.keys(this._accounts).length > 0;
}
private get aboutBitwarden(): MenuItemConstructorOptions {
return {
id: 'aboutBitwarden',
label: this.localize('aboutBitwarden'),
role: 'about',
visible: isMacAppStore(),
};
}
private get checkForUpdates(): MenuItemConstructorOptions {
return {
id: 'checkForUpdates',
label: this.localize('checkForUpdates'),
click: menuItem => this.checkForUpdate(menuItem),
visible: !isMacAppStore() && !isWindowsStore() && !isSnapStore(),
};
}
private get separator(): MenuItemConstructorOptions {
return {
type: 'separator',
};
}
private get settings(): MenuItemConstructorOptions {
return {
id: 'settings',
label: this.localize(process.platform === 'darwin' ?
'preferences' :
'settings'
),
click: () => this.sendMessage('openSettings'),
accelerator: 'CmdOrCtrl+,',
};
}
private get lock(): MenuItemConstructorOptions {
return {
id: 'lock',
label: this.localize('lockVault'),
submenu: this.lockSubmenu,
enabled: this.hasAccounts,
};
}
private get lockSubmenu(): MenuItemConstructorOptions[] {
const value: MenuItemConstructorOptions[] = [];
for (const userId in this._accounts) {
if (userId == null) {
continue;
}
value.push({
label: this._accounts[userId].email,
id: `lockNow_${this._accounts[userId].userId}`,
click: () => this.sendMessage('lockVault', { userId: this._accounts[userId].userId }),
enabled: !this._accounts[userId].isLocked,
visible: this._accounts[userId].isAuthenticated,
});
}
return value;
}
private get lockAll(): MenuItemConstructorOptions {
return {
id: 'lockAllNow',
label: this.localize('lockAllVaults'),
click: () => this.sendMessage('lockAllVaults'),
accelerator: 'CmdOrCtrl+L',
enabled: this.hasAccounts,
};
}
private get logOut(): MenuItemConstructorOptions {
return {
id: 'logOut',
label: this.localize('logOut'),
submenu: this.logOutSubmenu,
enabled: this.hasAccounts,
};
}
private get logOutSubmenu(): MenuItemConstructorOptions[] {
const value: MenuItemConstructorOptions[] = [];
for (const userId in this._accounts) {
if (userId == null) {
continue;
}
value.push({
label: this._accounts[userId].email,
id: `logOut_${this._accounts[userId].userId}`,
click: async () => {
const result = await dialog.showMessageBox(this._window, {
title: this.localize('logOut'),
message: this.localize('logOut'),
detail: this.localize('logOutConfirmation'),
buttons: [this.localize('logOut'), this.localize('cancel')],
cancelId: 1,
defaultId: 0,
noLink: true,
});
if (result.response === 0) {
this.sendMessage('logout', { userId: this._accounts[userId].userId });
}
},
visible: this._accounts[userId].isAuthenticated,
});
}
return value;
}
private get services(): MenuItemConstructorOptions {
return {
id: 'services',
label: this.localize('services'),
role: 'services',
submenu: [],
visible: isMacAppStore(),
};
}
private get hideBitwarden(): MenuItemConstructorOptions {
return {
id: 'hideBitwarden',
label: this.localize('hideBitwarden'),
role: 'hide',
visible: isMacAppStore(),
};
}
private get hideOthers(): MenuItemConstructorOptions {
return {
id: 'hideOthers',
label: this.localize('hideOthers'),
role: 'hideOthers',
visible: isMacAppStore(),
};
}
private get showAll(): MenuItemConstructorOptions {
return {
id: 'showAll',
label: this.localize('showAll'),
role: 'unhide',
visible: isMacAppStore(),
};
}
private get quitBitwarden(): MenuItemConstructorOptions {
return {
id: 'quitBitwarden',
label: this.localize('quitBitwarden'),
role: 'quit',
visible: isMacAppStore(),
};
}
private localize(s: string) {
return this._i18nService.t(s);
}
private async checkForUpdate(menuItem: MenuItem) {
menuItem.enabled = false;
this._updater.checkForUpdate(true);
menuItem.enabled = true;
}
private sendMessage(message: string, args?: any) {
this._messagingService.send(message, args);
}
}