1
0
mirror of https://github.com/bitwarden/browser synced 2026-01-08 03:23:50 +00:00

Move desktop into apps/desktop

This commit is contained in:
Hinton
2022-05-05 17:16:23 +02:00
parent 9852f2ec22
commit 28bc4113b9
331 changed files with 2 additions and 2 deletions

View File

@@ -0,0 +1,87 @@
import { BrowserWindow, clipboard, dialog, MenuItemConstructorOptions } from "electron";
import { I18nService } from "jslib-common/abstractions/i18n.service";
import { UpdaterMain } from "jslib-electron/updater.main";
import { isMacAppStore, isSnapStore, isWindowsStore } from "jslib-electron/utils";
import { IMenubarMenu } from "./menubar";
export class AboutMenu implements IMenubarMenu {
readonly id: string = "about";
get label(): string {
return "";
}
get items(): MenuItemConstructorOptions[] {
return [this.separator, this.checkForUpdates, this.aboutBitwarden];
}
private readonly _i18nService: I18nService;
private readonly _updater: UpdaterMain;
private readonly _window: BrowserWindow;
private readonly _version: string;
constructor(
i18nService: I18nService,
version: string,
window: BrowserWindow,
updater: UpdaterMain
) {
this._i18nService = i18nService;
this._updater = updater;
this._version = version;
this._window = window;
}
private get separator(): MenuItemConstructorOptions {
return { type: "separator" };
}
private get checkForUpdates(): MenuItemConstructorOptions {
return {
id: "checkForUpdates",
label: this.localize("checkForUpdates"),
visible: !isWindowsStore() && !isSnapStore() && !isMacAppStore(),
click: () => this.checkForUpdate(),
};
}
private get aboutBitwarden(): MenuItemConstructorOptions {
return {
id: "aboutBitwarden",
label: this.localize("aboutBitwarden"),
click: async () => {
const aboutInformation =
this.localize("version", this._version) +
"\nShell " +
process.versions.electron +
"\nRenderer " +
process.versions.chrome +
"\nNode " +
process.versions.node +
"\nArchitecture " +
process.arch;
const result = await dialog.showMessageBox(this._window, {
title: "Bitwarden",
message: "Bitwarden",
detail: aboutInformation,
type: "info",
noLink: true,
buttons: [this.localize("ok"), this.localize("copy")],
});
if (result.response === 1) {
clipboard.writeText(aboutInformation);
}
},
};
}
private localize(s: string, p?: string) {
return this._i18nService.t(s, p);
}
private async checkForUpdate() {
this._updater.checkForUpdate(true);
}
}

View File

@@ -0,0 +1,115 @@
import { BrowserWindow, dialog, MenuItemConstructorOptions, shell } from "electron";
import { I18nService } from "jslib-common/abstractions/i18n.service";
import { MessagingService } from "jslib-common/abstractions/messaging.service";
import { isMacAppStore, isWindowsStore } from "jslib-electron/utils";
import { IMenubarMenu } from "./menubar";
export class AccountMenu implements IMenubarMenu {
readonly id: string = "accountMenu";
get label(): string {
return this.localize("account");
}
get items(): MenuItemConstructorOptions[] {
return [
this.premiumMembership,
this.changeMasterPassword,
this.twoStepLogin,
this.fingerprintPhrase,
];
}
private readonly _i18nService: I18nService;
private readonly _messagingService: MessagingService;
private readonly _webVaultUrl: string;
private readonly _window: BrowserWindow;
private readonly _isLocked: boolean;
constructor(
i18nService: I18nService,
messagingService: MessagingService,
webVaultUrl: string,
window: BrowserWindow,
isLocked: boolean
) {
this._i18nService = i18nService;
this._messagingService = messagingService;
this._webVaultUrl = webVaultUrl;
this._window = window;
this._isLocked = isLocked;
}
private get premiumMembership(): MenuItemConstructorOptions {
return {
label: this.localize("premiumMembership"),
click: () => this.sendMessage("openPremium"),
id: "premiumMembership",
visible: !isWindowsStore() && !isMacAppStore(),
enabled: !this._isLocked,
};
}
private get changeMasterPassword(): MenuItemConstructorOptions {
return {
label: this.localize("changeMasterPass"),
id: "changeMasterPass",
click: async () => {
const result = await dialog.showMessageBox(this._window, {
title: this.localize("changeMasterPass"),
message: this.localize("changeMasterPass"),
detail: this.localize("changeMasterPasswordConfirmation"),
buttons: [this.localize("yes"), this.localize("no")],
cancelId: 1,
defaultId: 0,
noLink: true,
});
if (result.response === 0) {
shell.openExternal(this._webVaultUrl);
}
},
enabled: !this._isLocked,
};
}
private get twoStepLogin(): MenuItemConstructorOptions {
return {
label: this.localize("twoStepLogin"),
id: "twoStepLogin",
click: async () => {
const result = await dialog.showMessageBox(this._window, {
title: this.localize("twoStepLogin"),
message: this.localize("twoStepLogin"),
detail: this.localize("twoStepLoginConfirmation"),
buttons: [this.localize("yes"), this.localize("no")],
cancelId: 1,
defaultId: 0,
noLink: true,
});
if (result.response === 0) {
shell.openExternal(this._webVaultUrl);
}
},
enabled: !this._isLocked,
};
}
private get fingerprintPhrase(): MenuItemConstructorOptions {
return {
label: this.localize("fingerprintPhrase"),
id: "fingerprintPhrase",
click: () => this.sendMessage("showFingerprintPhrase"),
enabled: !this._isLocked,
};
}
private localize(s: string) {
return this._i18nService.t(s);
}
private sendMessage(message: string, args?: any) {
this._messagingService.send(message, args);
}
}

View File

@@ -0,0 +1,113 @@
import { BrowserWindow, 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 { isMac } from "jslib-electron/utils";
import { FirstMenu } from "./menu.first";
import { MenuAccount } from "./menu.updater";
import { IMenubarMenu } from "./menubar";
// AKA: "FirstMenu" or "MacMenu" - the first menu that shows on all macOs apps
export class BitwardenMenu extends FirstMenu implements IMenubarMenu {
readonly id: string = "bitwarden";
readonly label: string = "Bitwarden";
get items(): MenuItemConstructorOptions[] {
const items = [this.aboutBitwarden, this.checkForUpdates];
if (this.aboutBitwarden.visible === true || this.checkForUpdates.visible === true) {
items.push(this.separator);
}
items.push(this.settings);
items.push(this.lock);
items.push(this.lockAll);
items.push(this.logOut);
items.push(this.separator);
items.push(this.services);
if (
this.hideBitwarden.visible === true ||
this.hideOthers.visible === true ||
this.showAll.visible === true
) {
items.push(this.separator);
}
items.push(this.hideBitwarden);
items.push(this.hideOthers);
items.push(this.showAll);
if (this.quitBitwarden.visible === true) {
items.push(this.separator);
}
items.push(this.quitBitwarden);
return items;
}
constructor(
i18nService: I18nService,
messagingService: MessagingService,
updater: UpdaterMain,
window: BrowserWindow,
accounts: { [userId: string]: MenuAccount },
isLocked: boolean
) {
super(i18nService, messagingService, updater, window, accounts, isLocked);
}
private get aboutBitwarden(): MenuItemConstructorOptions {
return {
id: "aboutBitwarden",
label: this.localize("aboutBitwarden"),
role: "about",
visible: isMac(),
};
}
private get services(): MenuItemConstructorOptions {
return {
id: "services",
label: this.localize("services"),
role: "services",
submenu: [],
visible: isMac(),
};
}
private get hideBitwarden(): MenuItemConstructorOptions {
return {
id: "hideBitwarden",
label: this.localize("hideBitwarden"),
role: "hide",
visible: isMac(),
};
}
private get hideOthers(): MenuItemConstructorOptions {
return {
id: "hideOthers",
label: this.localize("hideOthers"),
role: "hideOthers",
visible: isMac(),
};
}
private get showAll(): MenuItemConstructorOptions {
return {
id: "showAll",
label: this.localize("showAll"),
role: "unhide",
visible: isMac(),
};
}
private get quitBitwarden(): MenuItemConstructorOptions {
return {
id: "quitBitwarden",
label: this.localize("quitBitwarden"),
role: "quit",
visible: isMac(),
};
}
}

View File

@@ -0,0 +1,131 @@
import { MenuItemConstructorOptions } from "electron";
import { I18nService } from "jslib-common/abstractions/i18n.service";
import { MessagingService } from "jslib-common/abstractions/messaging.service";
import { IMenubarMenu } from "./menubar";
export class EditMenu implements IMenubarMenu {
readonly id: string = "editMenu";
get label(): string {
return this.localize("edit");
}
get items(): MenuItemConstructorOptions[] {
return [
this.undo,
this.redo,
this.separator,
this.cut,
this.copy,
this.paste,
this.separator,
this.selectAll,
this.separator,
this.copyUsername,
this.copyPassword,
this.copyVerificationCodeTotp,
];
}
private readonly _i18nService: I18nService;
private readonly _messagingService: MessagingService;
private readonly _isLocked: boolean;
constructor(i18nService: I18nService, messagingService: MessagingService, isLocked: boolean) {
this._i18nService = i18nService;
this._messagingService = messagingService;
this._isLocked = isLocked;
}
private get undo(): MenuItemConstructorOptions {
return {
id: "undo",
label: this.localize("undo"),
role: "undo",
};
}
private get redo(): MenuItemConstructorOptions {
return {
id: "redo",
label: this.localize("redo"),
role: "redo",
};
}
private get separator(): MenuItemConstructorOptions {
return { type: "separator" };
}
private get cut(): MenuItemConstructorOptions {
return {
id: "cut",
label: this.localize("cut"),
role: "cut",
};
}
private get copy(): MenuItemConstructorOptions {
return {
id: "copy",
label: this.localize("copy"),
role: "copy",
};
}
private get paste(): MenuItemConstructorOptions {
return {
id: "paste",
label: this.localize("paste"),
role: "paste",
};
}
private get selectAll(): MenuItemConstructorOptions {
return {
id: "selectAll",
label: this.localize("selectAll"),
role: "selectAll",
};
}
private get copyUsername(): MenuItemConstructorOptions {
return {
label: this.localize("copyUsername"),
id: "copyUsername",
click: () => this.sendMessage("copyUsername"),
accelerator: "CmdOrCtrl+U",
enabled: !this._isLocked,
};
}
private get copyPassword(): MenuItemConstructorOptions {
return {
label: this.localize("copyPassword"),
id: "copyPassword",
click: () => this.sendMessage("copyPassword"),
accelerator: "CmdOrCtrl+P",
enabled: !this._isLocked,
};
}
private get copyVerificationCodeTotp(): MenuItemConstructorOptions {
return {
label: this.localize("copyVerificationCodeTotp"),
id: "copyTotp",
click: () => this.sendMessage("copyTotp"),
accelerator: "CmdOrCtrl+T",
enabled: !this._isLocked,
};
}
private localize(s: string) {
return this._i18nService.t(s);
}
private sendMessage(message: string) {
this._messagingService.send(message);
}
}

View File

@@ -0,0 +1,141 @@
import { BrowserWindow, 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 { isMac, isMacAppStore } from "jslib-electron/utils";
import { FirstMenu } from "./menu.first";
import { MenuAccount } from "./menu.updater";
import { IMenubarMenu } from "./menubar";
export class FileMenu extends FirstMenu implements IMenubarMenu {
readonly id: string = "fileMenu";
get label(): string {
return this.localize("file");
}
get items(): MenuItemConstructorOptions[] {
let items = [
this.addNewLogin,
this.addNewItem,
this.addNewFolder,
this.separator,
this.syncVault,
this.exportVault,
];
if (!isMac()) {
items = [
...items,
...[
this.separator,
this.settings,
this.lock,
this.lockAll,
this.logOut,
this.separator,
this.quitBitwarden,
],
];
}
return items;
}
constructor(
i18nService: I18nService,
messagingService: MessagingService,
updater: UpdaterMain,
window: BrowserWindow,
accounts: { [userId: string]: MenuAccount },
isLocked: boolean
) {
super(i18nService, messagingService, updater, window, accounts, isLocked);
}
private get addNewLogin(): MenuItemConstructorOptions {
return {
label: this.localize("addNewLogin"),
click: () => this.sendMessage("newLogin"),
accelerator: "CmdOrCtrl+N",
id: "addNewLogin",
enabled: !this._isLocked,
};
}
private get addNewItem(): MenuItemConstructorOptions {
return {
label: this.localize("addNewItem"),
id: "addNewItem",
submenu: this.addNewItemSubmenu,
enabled: !this._isLocked,
};
}
private get addNewItemSubmenu(): MenuItemConstructorOptions[] {
return [
{
id: "typeLogin",
label: this.localize("typeLogin"),
click: () => this.sendMessage("newLogin"),
accelerator: "CmdOrCtrl+Shift+L",
},
{
id: "typeCard",
label: this.localize("typeCard"),
click: () => this.sendMessage("newCard"),
accelerator: "CmdOrCtrl+Shift+C",
},
{
id: "typeIdentity",
label: this.localize("typeIdentity"),
click: () => this.sendMessage("newIdentity"),
accelerator: "CmdOrCtrl+Shift+I",
},
{
id: "typeSecureNote",
label: this.localize("typeSecureNote"),
click: () => this.sendMessage("newSecureNote"),
accelerator: "CmdOrCtrl+Shift+S",
},
];
}
private get addNewFolder(): MenuItemConstructorOptions {
return {
id: "addNewFolder",
label: this.localize("addNewFolder"),
click: () => this.sendMessage("newFolder"),
enabled: !this._isLocked,
};
}
private get syncVault(): MenuItemConstructorOptions {
return {
id: "syncVault",
label: this.localize("syncVault"),
click: () => this.sendMessage("syncVault"),
enabled: !this._isLocked,
};
}
private get exportVault(): MenuItemConstructorOptions {
return {
id: "exportVault",
label: this.localize("exportVault"),
click: () => this.sendMessage("exportVault"),
enabled: !this._isLocked,
};
}
private get quitBitwarden(): MenuItemConstructorOptions {
return {
id: "quitBitwarden",
label: this.localize("quitBitwarden"),
visible: !isMacAppStore(),
role: "quit",
};
}
}

View File

@@ -0,0 +1,152 @@
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 { MenuAccount } from "./menu.updater";
export class FirstMenu {
protected readonly _i18nService: I18nService;
protected readonly _updater: UpdaterMain;
protected readonly _messagingService: MessagingService;
protected readonly _accounts: { [userId: string]: MenuAccount };
protected readonly _window: BrowserWindow;
protected readonly _isLocked: boolean;
constructor(
i18nService: I18nService,
messagingService: MessagingService,
updater: UpdaterMain,
window: BrowserWindow,
accounts: { [userId: string]: MenuAccount },
isLocked: boolean
) {
this._i18nService = i18nService;
this._updater = updater;
this._messagingService = messagingService;
this._window = window;
this._accounts = accounts;
this._isLocked = isLocked;
}
protected get hasAccounts(): boolean {
return this._accounts != null && Object.keys(this._accounts).length > 0;
}
protected get checkForUpdates(): MenuItemConstructorOptions {
return {
id: "checkForUpdates",
label: this.localize("checkForUpdates"),
click: (menuItem) => this.checkForUpdate(menuItem),
visible: !isMacAppStore() && !isWindowsStore() && !isSnapStore(),
};
}
protected get separator(): MenuItemConstructorOptions {
return {
type: "separator",
};
}
protected get settings(): MenuItemConstructorOptions {
return {
id: "settings",
label: this.localize(process.platform === "darwin" ? "preferences" : "settings"),
click: () => this.sendMessage("openSettings"),
accelerator: "CmdOrCtrl+,",
enabled: !this._isLocked,
};
}
protected get lock(): MenuItemConstructorOptions {
return {
id: "lock",
label: this.localize("lockVault"),
submenu: this.lockSubmenu,
enabled: this.hasAccounts,
};
}
protected 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;
}
protected get lockAll(): MenuItemConstructorOptions {
return {
id: "lockAllNow",
label: this.localize("lockAllVaults"),
click: () => this.sendMessage("lockAllVaults"),
accelerator: "CmdOrCtrl+L",
enabled: this.hasAccounts,
};
}
protected get logOut(): MenuItemConstructorOptions {
return {
id: "logOut",
label: this.localize("logOut"),
submenu: this.logOutSubmenu,
enabled: this.hasAccounts,
};
}
protected 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;
}
protected localize(s: string) {
return this._i18nService.t(s);
}
protected async checkForUpdate(menuItem: MenuItem) {
menuItem.enabled = false;
this._updater.checkForUpdate(true);
menuItem.enabled = true;
}
protected sendMessage(message: string, args?: any) {
this._messagingService.send(message, args);
}
}

View File

@@ -0,0 +1,234 @@
import { shell, MenuItemConstructorOptions } from "electron";
import { I18nService } from "jslib-common/abstractions/i18n.service";
import { isMacAppStore, isWindowsStore } from "jslib-electron/utils";
import { AboutMenu } from "./menu.about";
import { IMenubarMenu } from "./menubar";
export class HelpMenu implements IMenubarMenu {
readonly id: string = "help";
get label(): string {
return this.localize("help");
}
get items(): MenuItemConstructorOptions[] {
const items = [
this.getHelp,
this.contactUs,
this.fileBugReport,
this.legal,
this.separator,
this.followUs,
this.separator,
this.goToWebVault,
this.separator,
this.getMobileApp,
this.getBrowserExtension,
];
if (this._aboutMenu != null) {
items.push(...this._aboutMenu.items);
}
return items;
}
private readonly _i18nService: I18nService;
private readonly _webVaultUrl: string;
private readonly _aboutMenu: AboutMenu;
constructor(i18nService: I18nService, webVaultUrl: string, aboutMenu: AboutMenu) {
this._i18nService = i18nService;
this._webVaultUrl = webVaultUrl;
this._aboutMenu = aboutMenu;
}
private get contactUs(): MenuItemConstructorOptions {
return {
id: "contactUs",
label: this.localize("contactUs"),
click: () => shell.openExternal("https://bitwarden.com/contact"),
};
}
private get getHelp(): MenuItemConstructorOptions {
return {
id: "getHelp",
label: this.localize("getHelp"),
click: () => shell.openExternal("https://bitwarden.com/help"),
};
}
private get fileBugReport(): MenuItemConstructorOptions {
return {
id: "fileBugReport",
label: this.localize("fileBugReport"),
click: () => shell.openExternal("https://github.com/bitwarden/desktop/issues"),
};
}
private get legal(): MenuItemConstructorOptions {
return {
id: "legal",
label: this.localize("legal"),
visible: isMacAppStore(),
submenu: this.legalSubmenu,
};
}
private get legalSubmenu(): MenuItemConstructorOptions[] {
return [
{
id: "termsOfService",
label: this.localize("termsOfService"),
click: () => shell.openExternal("https://bitwarden.com/terms/"),
},
{
id: "privacyPolicy",
label: this.localize("privacyPolicy"),
click: () => shell.openExternal("https://bitwarden.com/privacy/"),
},
];
}
private get separator(): MenuItemConstructorOptions {
return { type: "separator" };
}
private get followUs(): MenuItemConstructorOptions {
return {
id: "followUs",
label: this.localize("followUs"),
submenu: this.followUsSubmenu,
};
}
private get followUsSubmenu(): MenuItemConstructorOptions[] {
return [
{
id: "blog",
label: this.localize("blog"),
click: () => shell.openExternal("https://blog.bitwarden.com"),
},
{
id: "twitter",
label: "Twitter",
click: () => shell.openExternal("https://twitter.com/bitwarden"),
},
{
id: "facebook",
label: "Facebook",
click: () => shell.openExternal("https://www.facebook.com/bitwarden/"),
},
{
id: "github",
label: "GitHub",
click: () => shell.openExternal("https://github.com/bitwarden"),
},
];
}
private get goToWebVault(): MenuItemConstructorOptions {
return {
id: "goToWebVault",
label: this.localize("goToWebVault"),
click: () => shell.openExternal(this._webVaultUrl),
};
}
private get getMobileApp(): MenuItemConstructorOptions {
return {
id: "getMobileApp",
label: this.localize("getMobileApp"),
visible: !isWindowsStore(),
submenu: this.getMobileAppSubmenu,
};
}
private get getMobileAppSubmenu(): MenuItemConstructorOptions[] {
return [
{
id: "iOS",
label: "iOS",
click: () => {
shell.openExternal(
"https://itunes.apple.com/app/" + "bitwarden-free-password-manager/id1137397744?mt=8"
);
},
},
{
id: "android",
label: "Android",
click: () => {
shell.openExternal(
"https://play.google.com/store/apps/" + "details?id=com.x8bit.bitwarden"
);
},
},
];
}
private get getBrowserExtension(): MenuItemConstructorOptions {
return {
id: "getBrowserExtension",
label: this.localize("getBrowserExtension"),
visible: !isWindowsStore(),
submenu: this.getBrowserExtensionSubmenu,
};
}
private get getBrowserExtensionSubmenu(): MenuItemConstructorOptions[] {
return [
{
id: "chrome",
label: "Chrome",
click: () => {
shell.openExternal(
"https://chrome.google.com/webstore/detail/" +
"bitwarden-free-password-m/nngceckbapebfimnlniiiahkandclblb"
);
},
},
{
id: "firefox",
label: "Firefox",
click: () => {
shell.openExternal(
"https://addons.mozilla.org/firefox/addon/" + "bitwarden-password-manager/"
);
},
},
{
id: "firefox",
label: "Opera",
click: () => {
shell.openExternal(
"https://addons.opera.com/extensions/details/" + "bitwarden-free-password-manager/"
);
},
},
{
id: "firefox",
label: "Edge",
click: () => {
shell.openExternal(
"https://microsoftedge.microsoft.com/addons/" +
"detail/jbkfoedolllekgbhcbcoahefnbanhhlh"
);
},
},
{
id: "safari",
label: "Safari",
click: () => {
shell.openExternal("https://bitwarden.com/download/");
},
},
];
}
private localize(s: string) {
return this._i18nService.t(s);
}
}

View File

@@ -0,0 +1,52 @@
import { app, Menu } from "electron";
import { BaseMenu } from "jslib-electron/baseMenu";
import { Main } from "../../main";
import { MenuUpdateRequest } from "./menu.updater";
import { Menubar } from "./menubar";
const cloudWebVaultUrl = "https://vault.bitwarden.com";
export class MenuMain extends BaseMenu {
constructor(private main: Main) {
super(main.i18nService, main.windowMain);
}
async init() {
this.initContextMenu();
await this.setMenu();
}
async updateApplicationMenuState(updateRequest: MenuUpdateRequest) {
await this.setMenu(updateRequest);
}
private async setMenu(updateRequest?: MenuUpdateRequest) {
Menu.setApplicationMenu(
new Menubar(
this.main.i18nService,
this.main.messagingService,
this.main.updaterMain,
this.windowMain,
await this.getWebVaultUrl(),
app.getVersion(),
updateRequest
).menu
);
}
private async getWebVaultUrl() {
let webVaultUrl = cloudWebVaultUrl;
const urlsObj: any = await this.main.stateService.getEnvironmentUrls();
if (urlsObj != null) {
if (urlsObj.base != null) {
webVaultUrl = urlsObj.base;
} else if (urlsObj.webVault != null) {
webVaultUrl = urlsObj.webVault;
}
}
return webVaultUrl;
}
}

View File

@@ -0,0 +1,12 @@
export class MenuUpdateRequest {
hideChangeMasterPassword: boolean;
activeUserId: string;
accounts: { [userId: string]: MenuAccount };
}
export class MenuAccount {
isAuthenticated: boolean;
isLocked: boolean;
userId: string;
email: string;
}

View File

@@ -0,0 +1,135 @@
import { MenuItemConstructorOptions } from "electron";
import { I18nService } from "jslib-common/abstractions/i18n.service";
import { MessagingService } from "jslib-common/abstractions/messaging.service";
import { IMenubarMenu } from "./menubar";
export class ViewMenu implements IMenubarMenu {
readonly id: "viewMenu";
get label(): string {
return this.localize("view");
}
get items(): MenuItemConstructorOptions[] {
return [
this.searchVault,
this.separator,
this.generator,
this.passwordHistory,
this.separator,
this.zoomIn,
this.zoomOut,
this.resetZoom,
this.separator,
this.toggleFullscreen,
this.separator,
this.reload,
this.toggleDevTools,
];
}
private readonly _i18nService: I18nService;
private readonly _messagingService: MessagingService;
private readonly _isLocked: boolean;
constructor(i18nService: I18nService, messagingService: MessagingService, isLocked: boolean) {
this._i18nService = i18nService;
this._messagingService = messagingService;
this._isLocked = isLocked;
}
private get searchVault(): MenuItemConstructorOptions {
return {
id: "searchVault",
label: this.localize("searchVault"),
click: () => this.sendMessage("focusSearch"),
accelerator: "CmdOrCtrl+F",
enabled: !this._isLocked,
};
}
private get separator(): MenuItemConstructorOptions {
return { type: "separator" };
}
private get generator(): MenuItemConstructorOptions {
return {
id: "generator",
label: this.localize("generator"),
click: () => this.sendMessage("openGenerator"),
accelerator: "CmdOrCtrl+G",
enabled: !this._isLocked,
};
}
private get passwordHistory(): MenuItemConstructorOptions {
return {
id: "passwordHistory",
label: this.localize("passwordHistory"),
click: () => this.sendMessage("openPasswordHistory"),
enabled: !this._isLocked,
};
}
private get zoomIn(): MenuItemConstructorOptions {
return {
id: "zoomIn",
label: this.localize("zoomIn"),
role: "zoomIn",
accelerator: "CmdOrCtrl+=",
};
}
private get zoomOut(): MenuItemConstructorOptions {
return {
id: "zoomOut",
label: this.localize("zoomOut"),
role: "zoomOut",
accelerator: "CmdOrCtrl+-",
};
}
private get resetZoom(): MenuItemConstructorOptions {
return {
id: "resetZoom",
label: this.localize("resetZoom"),
role: "resetZoom",
accelerator: "CmdOrCtrl+0",
};
}
private get toggleFullscreen(): MenuItemConstructorOptions {
return {
id: "toggleFullScreen",
label: this.localize("toggleFullScreen"),
role: "togglefullscreen",
};
}
private get reload(): MenuItemConstructorOptions {
return {
id: "reload",
label: this.localize("reload"),
role: "forceReload",
};
}
private get toggleDevTools(): MenuItemConstructorOptions {
return {
id: "toggleDevTools",
label: this.localize("toggleDevTools"),
role: "toggleDevTools",
accelerator: "F12",
};
}
private localize(s: string) {
return this._i18nService.t(s);
}
private sendMessage(message: string) {
this._messagingService.send(message);
}
}

View File

@@ -0,0 +1,105 @@
import { MenuItemConstructorOptions } from "electron";
import { I18nService } from "jslib-common/abstractions/i18n.service";
import { MessagingService } from "jslib-common/abstractions/messaging.service";
import { isMac } from "jslib-electron/utils";
import { WindowMain } from "jslib-electron/window.main";
import { IMenubarMenu } from "./menubar";
export class WindowMenu implements IMenubarMenu {
readonly id: string;
get label(): string {
return this.localize("window");
}
get items(): MenuItemConstructorOptions[] {
const items = [this.minimize, this.hideToMenu, this.alwaysOnTop];
if (isMac()) {
items.push(this.zoom, this.separator, this.bringAllToFront);
}
items.push(this.separator, this.close);
return items;
}
private readonly _i18nService: I18nService;
private readonly _messagingService: MessagingService;
private readonly _window: WindowMain;
constructor(
i18nService: I18nService,
messagingService: MessagingService,
windowMain: WindowMain
) {
this._i18nService = i18nService;
this._messagingService = messagingService;
this._window = windowMain;
}
private get minimize(): MenuItemConstructorOptions {
return {
id: "minimize",
label: this.localize("minimize"),
role: "minimize",
};
}
private get hideToMenu(): MenuItemConstructorOptions {
return {
id: "hideToMenu",
label: this.localize(isMac() ? "hideToMenuBar" : "hideToTray"),
click: () => this.sendMessage("hideToTray"),
accelerator: "CmdOrCtrl+Shift+M",
};
}
private get alwaysOnTop(): MenuItemConstructorOptions {
return {
id: "alwaysOnTop",
label: this.localize("alwaysOnTop"),
type: "checkbox",
checked: this._window.win.isAlwaysOnTop(),
click: () => this._window.toggleAlwaysOnTop(),
accelerator: "CmdOrCtrl+Shift+T",
};
}
private get zoom(): MenuItemConstructorOptions {
return {
id: "zoom",
label: this.localize("zoom"),
role: "zoom",
};
}
private get separator(): MenuItemConstructorOptions {
return { type: "separator" };
}
private get bringAllToFront(): MenuItemConstructorOptions {
return {
id: "bringAllToFront",
label: this.localize("bringAllToFront"),
role: "front",
};
}
private get close(): MenuItemConstructorOptions {
return {
id: "close",
label: this.localize("close"),
role: "close",
};
}
private localize(s: string) {
return this._i18nService.t(s);
}
private sendMessage(message: string, args?: any) {
this._messagingService.send(message, args);
}
}

View File

@@ -0,0 +1,100 @@
import { Menu, 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 { isMac } from "jslib-electron/utils";
import { WindowMain } from "jslib-electron/window.main";
import { AboutMenu } from "./menu.about";
import { AccountMenu } from "./menu.account";
import { BitwardenMenu } from "./menu.bitwarden";
import { EditMenu } from "./menu.edit";
import { FileMenu } from "./menu.file";
import { HelpMenu } from "./menu.help";
import { MenuUpdateRequest } from "./menu.updater";
import { ViewMenu } from "./menu.view";
import { WindowMenu } from "./menu.window";
export interface IMenubarMenu {
id: string;
label: string;
visible?: boolean; // Assumes true if null
items: MenuItemConstructorOptions[];
}
export class Menubar {
private readonly items: IMenubarMenu[];
get menu(): Menu {
const template: MenuItemConstructorOptions[] = [];
if (this.items != null) {
this.items.forEach((item: IMenubarMenu) => {
if (item != null) {
template.push({
id: item.id,
label: item.label,
submenu: item.items,
visible: item.visible ?? true,
});
}
});
}
return Menu.buildFromTemplate(template);
}
constructor(
i18nService: I18nService,
messagingService: MessagingService,
updaterMain: UpdaterMain,
windowMain: WindowMain,
webVaultUrl: string,
appVersion: string,
updateRequest?: MenuUpdateRequest
) {
let isLocked = true;
if (
updateRequest != null &&
updateRequest.accounts != null &&
updateRequest.activeUserId != null
) {
isLocked = updateRequest.accounts[updateRequest.activeUserId]?.isLocked ?? true;
}
this.items = [
new FileMenu(
i18nService,
messagingService,
updaterMain,
windowMain.win,
updateRequest?.accounts,
isLocked
),
new EditMenu(i18nService, messagingService, isLocked),
new ViewMenu(i18nService, messagingService, isLocked),
new AccountMenu(i18nService, messagingService, webVaultUrl, windowMain.win, isLocked),
new WindowMenu(i18nService, messagingService, windowMain),
new HelpMenu(
i18nService,
webVaultUrl,
new AboutMenu(i18nService, appVersion, windowMain.win, updaterMain)
),
];
if (isMac()) {
this.items = [
...[
new BitwardenMenu(
i18nService,
messagingService,
updaterMain,
windowMain.win,
updateRequest?.accounts,
isLocked
),
],
...this.items,
];
}
}
}