1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-15 07:43:35 +00:00

[PM-5540] DesktopSettingsService (#8369)

* WIP: First Try at making DesktopSettingsService

Does not work, migrations are ran in renderer but the values are read in main.

* Update window$ retrieval

* Fix DesktopSettings

* Rename Migration

* Add Migration to Builder

* Cleanup

* Update Comments

* Update `migrate.ts`

* Catch Unawaited Promises

* Remove Comments

* Update Tests

* Rename Migration

* Add `alwaysOnTop`

* Make `init` async

* Fix Desktop Build
This commit is contained in:
Justin Baur
2024-03-21 12:53:12 -05:00
committed by GitHub
parent b9f9ad029f
commit b450b31ec4
15 changed files with 459 additions and 263 deletions

View File

@@ -42,6 +42,7 @@ import { AutoConfirmFingerPrintsMigrator } from "./migrations/43-move-auto-confi
import { UserDecryptionOptionsMigrator } from "./migrations/44-move-user-decryption-options-to-state-provider";
import { MergeEnvironmentState } from "./migrations/45-merge-environment-state";
import { DeleteBiometricPromptCancelledData } from "./migrations/46-delete-orphaned-biometric-prompt-data";
import { MoveDesktopSettingsMigrator } from "./migrations/47-move-desktop-settings";
import { AddKeyTypeToOrgKeysMigrator } from "./migrations/5-add-key-type-to-org-keys";
import { RemoveLegacyEtmKeyMigrator } from "./migrations/6-remove-legacy-etm-key";
import { MoveBiometricAutoPromptToAccount } from "./migrations/7-move-biometric-auto-prompt-to-account";
@@ -50,7 +51,7 @@ import { MoveBrowserSettingsToGlobal } from "./migrations/9-move-browser-setting
import { MinVersionMigrator } from "./migrations/min-version";
export const MIN_VERSION = 3;
export const CURRENT_VERSION = 46;
export const CURRENT_VERSION = 47;
export type MinVersion = typeof MIN_VERSION;
export function createMigrationBuilder() {
@@ -98,7 +99,8 @@ export function createMigrationBuilder() {
.with(AutoConfirmFingerPrintsMigrator, 42, 43)
.with(UserDecryptionOptionsMigrator, 43, 44)
.with(MergeEnvironmentState, 44, 45)
.with(DeleteBiometricPromptCancelledData, 45, CURRENT_VERSION);
.with(DeleteBiometricPromptCancelledData, 45, 46)
.with(MoveDesktopSettingsMigrator, 46, CURRENT_VERSION);
}
export async function currentVersion(

View File

@@ -178,12 +178,9 @@ export function mockMigrationHelper(
return mockHelper;
}
// TODO: Use const generic for TUsers in TypeScript 5.0 so consumers don't have to `as const` themselves
export type InitialDataHint<TUsers extends readonly string[]> = {
/**
* A string array of the users id who are authenticated
*
* NOTE: It's recommended to as const this string array so you get type help defining the users data
*/
authenticatedAccounts?: TUsers;
/**
@@ -282,10 +279,9 @@ function expectInjectedData(
* @param initalData The data to start with
* @returns State after your migration has ran.
*/
// TODO: Use const generic for TUsers in TypeScript 5.0 so consumers don't have to `as const` themselves
export async function runMigrator<
TMigrator extends Migrator<number, number>,
TUsers extends readonly string[] = string[],
const TUsers extends readonly string[],
>(
migrator: TMigrator,
initalData?: InitialDataHint<TUsers>,

View File

@@ -0,0 +1,116 @@
import { runMigrator } from "../migration-helper.spec";
import { MoveDesktopSettingsMigrator } from "./47-move-desktop-settings";
describe("MoveDesktopSettings", () => {
const sut = new MoveDesktopSettingsMigrator(46, 47);
it("can migrate truthy values", async () => {
const output = await runMigrator(sut, {
authenticatedAccounts: ["user1"],
global: {
window: {
width: 400,
height: 400,
displayBounds: {
height: 200,
width: 200,
x: 200,
y: 200,
},
},
enableAlwaysOnTop: true,
enableCloseToTray: true,
enableMinimizeToTray: true,
enableStartToTray: true,
enableTray: true,
openAtLogin: true,
alwaysShowDock: true,
},
user1: {
settings: {
enableAlwaysOnTop: true,
},
},
});
expect(output).toEqual({
authenticatedAccounts: ["user1"],
global: {},
global_desktopSettings_window: {
width: 400,
height: 400,
displayBounds: {
height: 200,
width: 200,
x: 200,
y: 200,
},
},
global_desktopSettings_closeToTray: true,
global_desktopSettings_minimizeToTray: true,
global_desktopSettings_startToTray: true,
global_desktopSettings_trayEnabled: true,
global_desktopSettings_openAtLogin: true,
global_desktopSettings_alwaysShowDock: true,
global_desktopSettings_alwaysOnTop: true,
user1: {
settings: {},
},
});
});
it("can migrate falsey values", async () => {
const output = await runMigrator(sut, {
authenticatedAccounts: ["user1"],
global: {
window: null,
enableCloseToTray: false,
enableMinimizeToTray: false,
enableStartToTray: false,
enableTray: false,
openAtLogin: false,
alwaysShowDock: false,
enableAlwaysOnTop: false,
},
user1: {
settings: {
enableAlwaysOnTop: false,
},
},
});
expect(output).toEqual({
authenticatedAccounts: ["user1"],
global: {},
global_desktopSettings_window: null,
global_desktopSettings_closeToTray: false,
global_desktopSettings_minimizeToTray: false,
global_desktopSettings_startToTray: false,
global_desktopSettings_trayEnabled: false,
global_desktopSettings_openAtLogin: false,
global_desktopSettings_alwaysShowDock: false,
global_desktopSettings_alwaysOnTop: false,
user1: {
settings: {},
},
});
});
it("can migrate even if none of our values are found", async () => {
//
const output = await runMigrator(sut, {
authenticatedAccounts: ["user1"] as const,
global: {
anotherSetting: "",
},
});
expect(output).toEqual({
authenticatedAccounts: ["user1"] as const,
global: {
anotherSetting: "",
},
});
});
});

View File

@@ -0,0 +1,128 @@
import { KeyDefinitionLike, MigrationHelper, StateDefinitionLike } from "../migration-helper";
import { IRREVERSIBLE, Migrator } from "../migrator";
type ExpectedGlobalType = {
window?: object;
enableTray?: boolean;
enableMinimizeToTray?: boolean;
enableCloseToTray?: boolean;
enableStartToTray?: boolean;
openAtLogin?: boolean;
alwaysShowDock?: boolean;
enableAlwaysOnTop?: boolean;
};
type ExpectedAccountType = {
settings?: {
enableAlwaysOnTop?: boolean;
};
};
const DESKTOP_SETTINGS_STATE: StateDefinitionLike = { name: "desktopSettings" };
const WINDOW_KEY: KeyDefinitionLike = { key: "window", stateDefinition: DESKTOP_SETTINGS_STATE };
const CLOSE_TO_TRAY_KEY: KeyDefinitionLike = {
key: "closeToTray",
stateDefinition: DESKTOP_SETTINGS_STATE,
};
const MINIMIZE_TO_TRAY_KEY: KeyDefinitionLike = {
key: "minimizeToTray",
stateDefinition: DESKTOP_SETTINGS_STATE,
};
const START_TO_TRAY_KEY: KeyDefinitionLike = {
key: "startToTray",
stateDefinition: DESKTOP_SETTINGS_STATE,
};
const TRAY_ENABLED_KEY: KeyDefinitionLike = {
key: "trayEnabled",
stateDefinition: DESKTOP_SETTINGS_STATE,
};
const OPEN_AT_LOGIN_KEY: KeyDefinitionLike = {
key: "openAtLogin",
stateDefinition: DESKTOP_SETTINGS_STATE,
};
const ALWAYS_SHOW_DOCK_KEY: KeyDefinitionLike = {
key: "alwaysShowDock",
stateDefinition: DESKTOP_SETTINGS_STATE,
};
const ALWAYS_ON_TOP_KEY: KeyDefinitionLike = {
key: "alwaysOnTop",
stateDefinition: DESKTOP_SETTINGS_STATE,
};
export class MoveDesktopSettingsMigrator extends Migrator<46, 47> {
async migrate(helper: MigrationHelper): Promise<void> {
const legacyGlobal = await helper.get<ExpectedGlobalType>("global");
let updatedGlobal = false;
if (legacyGlobal?.window !== undefined) {
await helper.setToGlobal(WINDOW_KEY, legacyGlobal.window);
updatedGlobal = true;
delete legacyGlobal.window;
}
if (legacyGlobal?.enableCloseToTray != null) {
await helper.setToGlobal(CLOSE_TO_TRAY_KEY, legacyGlobal.enableCloseToTray);
updatedGlobal = true;
delete legacyGlobal.enableCloseToTray;
}
if (legacyGlobal?.enableMinimizeToTray != null) {
await helper.setToGlobal(MINIMIZE_TO_TRAY_KEY, legacyGlobal.enableMinimizeToTray);
updatedGlobal = true;
delete legacyGlobal.enableMinimizeToTray;
}
if (legacyGlobal?.enableStartToTray != null) {
await helper.setToGlobal(START_TO_TRAY_KEY, legacyGlobal.enableStartToTray);
updatedGlobal = true;
delete legacyGlobal.enableStartToTray;
}
if (legacyGlobal?.enableTray != null) {
await helper.setToGlobal(TRAY_ENABLED_KEY, legacyGlobal.enableTray);
updatedGlobal = true;
delete legacyGlobal.enableTray;
}
if (legacyGlobal?.openAtLogin != null) {
await helper.setToGlobal(OPEN_AT_LOGIN_KEY, legacyGlobal.openAtLogin);
updatedGlobal = true;
delete legacyGlobal.openAtLogin;
}
if (legacyGlobal?.alwaysShowDock != null) {
await helper.setToGlobal(ALWAYS_SHOW_DOCK_KEY, legacyGlobal.alwaysShowDock);
updatedGlobal = true;
delete legacyGlobal.alwaysShowDock;
}
if (legacyGlobal?.enableAlwaysOnTop != null) {
await helper.setToGlobal(ALWAYS_ON_TOP_KEY, legacyGlobal.enableAlwaysOnTop);
updatedGlobal = true;
delete legacyGlobal.enableAlwaysOnTop;
}
if (updatedGlobal) {
await helper.set("global", legacyGlobal);
}
async function migrateAccount(userId: string, account: ExpectedAccountType) {
// We only migrate the global setting for this, if we find it on the account object
// just delete it.
if (account?.settings?.enableAlwaysOnTop != null) {
delete account.settings.enableAlwaysOnTop;
await helper.set(userId, account);
}
}
const accounts = await helper.getAccounts<ExpectedAccountType>();
await Promise.all(accounts.map(({ userId, account }) => migrateAccount(userId, account)));
}
rollback(helper: MigrationHelper): Promise<void> {
throw IRREVERSIBLE;
}
}