mirror of
https://github.com/bitwarden/browser
synced 2025-12-11 22:03:36 +00:00
fix(desktop): persist zoom state across vault locks (#17217)
* fix(desktop): persist zoom state across vault locks Replace role-based zoom menu items with custom click handlers to fix zoom persistence issue where keyboard shortcuts (Ctrl+/-/0, Cmd+/-/0) weren't saving zoom changes after vault lock. Changes: - Add custom click handlers for zoomIn/zoomOut/resetZoom menu items - Add WindowMain.saveZoomFactor() method for immediate persistence - Pass WindowMain dependency to ViewMenu constructor - Update zoom-changed event comment to clarify coverage - Maintain existing mouse wheel zoom persistence via zoom-changed event Fixes: PM-791 Fixes: https://github.com/bitwarden/clients/issues/4675 * chore: update to macos-15 runners * review: downgrade macos build runner to 14 * review: align step with min zoom level * cleanup from merge
This commit is contained in:
@@ -6,6 +6,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
|
|||||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||||
|
|
||||||
import { isDev } from "../../utils";
|
import { isDev } from "../../utils";
|
||||||
|
import { WindowMain } from "../window.main";
|
||||||
|
|
||||||
import { IMenubarMenu } from "./menubar";
|
import { IMenubarMenu } from "./menubar";
|
||||||
|
|
||||||
@@ -42,11 +43,18 @@ export class ViewMenu implements IMenubarMenu {
|
|||||||
private readonly _i18nService: I18nService;
|
private readonly _i18nService: I18nService;
|
||||||
private readonly _messagingService: MessagingService;
|
private readonly _messagingService: MessagingService;
|
||||||
private readonly _isLocked: boolean;
|
private readonly _isLocked: boolean;
|
||||||
|
private readonly _windowMain: WindowMain;
|
||||||
|
|
||||||
constructor(i18nService: I18nService, messagingService: MessagingService, isLocked: boolean) {
|
constructor(
|
||||||
|
i18nService: I18nService,
|
||||||
|
messagingService: MessagingService,
|
||||||
|
isLocked: boolean,
|
||||||
|
windowMain: WindowMain,
|
||||||
|
) {
|
||||||
this._i18nService = i18nService;
|
this._i18nService = i18nService;
|
||||||
this._messagingService = messagingService;
|
this._messagingService = messagingService;
|
||||||
this._isLocked = isLocked;
|
this._isLocked = isLocked;
|
||||||
|
this._windowMain = windowMain;
|
||||||
}
|
}
|
||||||
|
|
||||||
private get searchVault(): MenuItemConstructorOptions {
|
private get searchVault(): MenuItemConstructorOptions {
|
||||||
@@ -86,7 +94,12 @@ export class ViewMenu implements IMenubarMenu {
|
|||||||
return {
|
return {
|
||||||
id: "zoomIn",
|
id: "zoomIn",
|
||||||
label: this.localize("zoomIn"),
|
label: this.localize("zoomIn"),
|
||||||
role: "zoomIn",
|
click: async () => {
|
||||||
|
const currentZoom = this._windowMain.win.webContents.zoomFactor;
|
||||||
|
const newZoom = currentZoom + 0.1;
|
||||||
|
this._windowMain.win.webContents.zoomFactor = newZoom;
|
||||||
|
await this._windowMain.saveZoomFactor(newZoom);
|
||||||
|
},
|
||||||
accelerator: "CmdOrCtrl+=",
|
accelerator: "CmdOrCtrl+=",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -95,7 +108,12 @@ export class ViewMenu implements IMenubarMenu {
|
|||||||
return {
|
return {
|
||||||
id: "zoomOut",
|
id: "zoomOut",
|
||||||
label: this.localize("zoomOut"),
|
label: this.localize("zoomOut"),
|
||||||
role: "zoomOut",
|
click: async () => {
|
||||||
|
const currentZoom = this._windowMain.win.webContents.zoomFactor;
|
||||||
|
const newZoom = Math.max(0.2, currentZoom - 0.1);
|
||||||
|
this._windowMain.win.webContents.zoomFactor = newZoom;
|
||||||
|
await this._windowMain.saveZoomFactor(newZoom);
|
||||||
|
},
|
||||||
accelerator: "CmdOrCtrl+-",
|
accelerator: "CmdOrCtrl+-",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -104,7 +122,11 @@ export class ViewMenu implements IMenubarMenu {
|
|||||||
return {
|
return {
|
||||||
id: "resetZoom",
|
id: "resetZoom",
|
||||||
label: this.localize("resetZoom"),
|
label: this.localize("resetZoom"),
|
||||||
role: "resetZoom",
|
click: async () => {
|
||||||
|
const newZoom = 1.0;
|
||||||
|
this._windowMain.win.webContents.zoomFactor = newZoom;
|
||||||
|
await this._windowMain.saveZoomFactor(newZoom);
|
||||||
|
},
|
||||||
accelerator: "CmdOrCtrl+0",
|
accelerator: "CmdOrCtrl+0",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ export class Menubar {
|
|||||||
updateRequest?.restrictedCipherTypes,
|
updateRequest?.restrictedCipherTypes,
|
||||||
),
|
),
|
||||||
new EditMenu(i18nService, messagingService, isLocked),
|
new EditMenu(i18nService, messagingService, isLocked),
|
||||||
new ViewMenu(i18nService, messagingService, isLocked),
|
new ViewMenu(i18nService, messagingService, isLocked, windowMain),
|
||||||
new AccountMenu(
|
new AccountMenu(
|
||||||
i18nService,
|
i18nService,
|
||||||
messagingService,
|
messagingService,
|
||||||
|
|||||||
@@ -303,7 +303,9 @@ export class WindowMain {
|
|||||||
this.win.webContents.zoomFactor = this.windowStates[mainWindowSizeKey].zoomFactor ?? 1.0;
|
this.win.webContents.zoomFactor = this.windowStates[mainWindowSizeKey].zoomFactor ?? 1.0;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Persist zoom changes immediately when user zooms in/out or resets zoom
|
// Persist zoom changes from mouse wheel and programmatic zoom operations
|
||||||
|
// NOTE: This event does NOT fire for keyboard shortcuts (Ctrl+/-/0, Cmd+/-/0)
|
||||||
|
// which are handled by custom menu click handlers in ViewMenu
|
||||||
// We can't depend on higher level web events (like close) to do this
|
// We can't depend on higher level web events (like close) to do this
|
||||||
// because locking the vault resets window state.
|
// because locking the vault resets window state.
|
||||||
this.win.webContents.on("zoom-changed", async () => {
|
this.win.webContents.on("zoom-changed", async () => {
|
||||||
@@ -432,6 +434,11 @@ export class WindowMain {
|
|||||||
await this.desktopSettingsService.setAlwaysOnTop(this.enableAlwaysOnTop);
|
await this.desktopSettingsService.setAlwaysOnTop(this.enableAlwaysOnTop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async saveZoomFactor(zoomFactor: number) {
|
||||||
|
this.windowStates[mainWindowSizeKey].zoomFactor = zoomFactor;
|
||||||
|
await this.desktopSettingsService.setWindow(this.windowStates[mainWindowSizeKey]);
|
||||||
|
}
|
||||||
|
|
||||||
private windowStateChangeHandler(configKey: string, win: BrowserWindow) {
|
private windowStateChangeHandler(configKey: string, win: BrowserWindow) {
|
||||||
global.clearTimeout(this.windowStateChangeTimer);
|
global.clearTimeout(this.windowStateChangeTimer);
|
||||||
this.windowStateChangeTimer = global.setTimeout(async () => {
|
this.windowStateChangeTimer = global.setTimeout(async () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user