Start controller pattern

Various fixes
This commit is contained in:
jliddev
2026-03-04 00:01:27 -06:00
parent fda4939303
commit 28f42962ea
9 changed files with 168 additions and 102 deletions

View File

@@ -3,7 +3,16 @@ import * as Store from "electron-store";
import * as log from "electron-log/main";
import { Addon } from "wowup-lib-core";
import { IPC_ADDONS_SAVE_ALL } from "../../src/common/constants";
import {
IPC_ADDONS_GET_ALL,
IPC_ADDONS_GET_ALL_FOR_INSTALLATION,
IPC_ADDONS_GET_ALL_FOR_PROVIDER,
IPC_ADDONS_GET_AUTO_UPDATE_ENABLED,
IPC_ADDONS_GET_AVAILABLE_FOR_UPDATE,
IPC_ADDONS_GET_BY_EXTERNAL_ID,
IPC_ADDONS_GET_BY_EXTERNAL_IDS,
IPC_ADDONS_SAVE_ALL,
} from "../../src/common/constants";
import { IpcController } from "./ipc-controller";
export class AddonController implements IpcController {
@@ -11,6 +20,95 @@ export class AddonController implements IpcController {
public register(): void {
ipcMain.handle(IPC_ADDONS_SAVE_ALL, (_evt, addons: Addon[]) => this.saveAll(addons));
ipcMain.handle(IPC_ADDONS_GET_ALL, () => this.getAll());
ipcMain.handle(IPC_ADDONS_GET_ALL_FOR_INSTALLATION, (_evt, installationId: string) => this.getAllForInstallation(installationId));
ipcMain.handle(IPC_ADDONS_GET_ALL_FOR_PROVIDER, (_evt, providerName: string) => this.getAllForProvider(providerName));
ipcMain.handle(IPC_ADDONS_GET_BY_EXTERNAL_ID, (_evt, externalId: string, providerName: string, installationId: string) => this.getByExternalId(externalId, providerName, installationId));
ipcMain.handle(IPC_ADDONS_GET_BY_EXTERNAL_IDS, (_evt, externalIds: string[]) => this.getByExternalIds(externalIds));
ipcMain.handle(IPC_ADDONS_GET_AVAILABLE_FOR_UPDATE, (_evt, installationId?: string) => this.getAvailableForUpdate(installationId));
ipcMain.handle(IPC_ADDONS_GET_AUTO_UPDATE_ENABLED, () => this.getAutoUpdateEnabled());
}
private getAll(): Addon[] {
return [...this.addonStore].map(([, addon]) => addon as Addon);
}
private getAllForInstallation(installationId: string): Addon[] {
const addons: Addon[] = [];
for (const [, addon] of this.addonStore) {
if ((addon as Addon).installationId === installationId) {
addons.push(addon as Addon);
}
}
return addons;
}
private getAllForProvider(providerName: string): Addon[] {
const addons: Addon[] = [];
for (const [, addon] of this.addonStore) {
if ((addon as Addon).providerName === providerName) {
addons.push(addon as Addon);
}
}
return addons;
}
private getByExternalId(externalId: string, providerName: string, installationId: string): Addon | undefined {
for (const [, addon] of this.addonStore) {
const a = addon as Addon;
if (a.installationId === installationId && a.externalId === externalId && a.providerName === providerName) {
return a;
}
}
return undefined;
}
private getByExternalIds(externalIds: string[]): Addon[] {
const addons: Addon[] = [];
for (const [, addon] of this.addonStore) {
const a = addon as Addon;
if (a.externalId && externalIds.includes(a.externalId)) {
addons.push(a);
}
}
return addons;
}
private getAvailableForUpdate(installationId?: string): Addon[] {
const addons: Addon[] = [];
for (const [, addon] of this.addonStore) {
const a = addon as Addon;
if (installationId && a.installationId !== installationId) {
continue;
}
if (a.isIgnored !== true && this.needsUpdate(a)) {
addons.push(a);
}
}
return addons;
}
private getAutoUpdateEnabled(): Addon[] {
const addons: Addon[] = [];
for (const [, addon] of this.addonStore) {
const a = addon as Addon;
if (a.isIgnored !== true && a.autoUpdateEnabled && !!a.installationId) {
addons.push(a);
}
}
return addons;
}
private needsUpdate(addon: Addon): boolean {
if (addon.isIgnored) {
return false;
}
if (addon.externalLatestReleaseId && addon.externalLatestReleaseId !== addon.installedExternalReleaseId) {
return true;
}
const installedVer = (addon.installedVersion ?? "").replace(/^v/i, "");
const latestVer = (addon.latestVersion ?? "").replace(/^v/i, "");
return installedVer.length > 0 && installedVer !== latestVer;
}
private saveAll(addons: Addon[]): void {

View File

@@ -370,7 +370,7 @@ export function initializeIpcHandlers(window: BrowserWindow): void {
try {
await fsp.access(filePath);
} catch (e) {
if (e.code !== "ENOENT") {
if ((e as NodeJS.ErrnoException).code !== "ENOENT") {
log.error(e);
}
return false;
@@ -739,7 +739,7 @@ export function initializeIpcHandlers(window: BrowserWindow): void {
} catch (err) {
log.error(err);
status.type = DownloadStatusType.Error;
status.error = err;
status.error = err instanceof Error ? err : undefined;
window.webContents.send(arg.responseKey, status);
}
}
@@ -765,18 +765,19 @@ function handleZipFile(err: Error | null, zipfile: yauzl.ZipFile, targetDir: str
if (/\/$/.test(entry.fileName)) {
// directory file names end with '/'
const dirPath = path.join(targetDir, entry.fileName);
fs.mkdir(dirPath, { recursive: true }, function () {
if (err) throw err;
fs.mkdir(dirPath, { recursive: true }, function (mkdirErr) {
if (mkdirErr) return reject(mkdirErr);
zipfile.readEntry();
});
} else {
// ensure parent directory exists
const filePath = path.join(targetDir, entry.fileName);
const parentPath = path.join(targetDir, path.dirname(entry.fileName));
fs.mkdir(parentPath, { recursive: true }, function () {
fs.mkdir(parentPath, { recursive: true }, function (mkdirErr) {
if (mkdirErr) return reject(mkdirErr);
zipfile.openReadStream(entry, (err, readStream) => {
if (err) {
throw err;
return reject(err);
}
const filter = new Transform();

View File

@@ -279,7 +279,7 @@ function createWindow(): BrowserWindow {
transparent: false,
resizable: true,
backgroundColor: getBackgroundColor(),
title: "WowUp" + AppEnv.buildFlavor === "ow" ? " CF" : "",
title: "WowUp" + (AppEnv.buildFlavor === "ow" ? " CF" : ""),
titleBarStyle: "hidden",
webPreferences: {
preload: join(__dirname, "preload.js"),
@@ -327,8 +327,6 @@ function createWindow(): BrowserWindow {
initializeStoreIpcHandlers();
registerControllers({ window: win, addonStore: getAddonStore() });
if (AppEnv.buildFlavor === "wago") {
wagoHandler.initialize(win);
}
@@ -377,8 +375,6 @@ function createWindow(): BrowserWindow {
if (["background-sync"].includes(permission)) {
return true;
}
log.warn("[webview] setPermissionCheckHandler", permission, origin);
return false;
});
@@ -445,12 +441,10 @@ function createWindow(): BrowserWindow {
}
win?.webContents.session.setPermissionRequestHandler((contents, permission, callback) => {
log.warn("win setPermissionRequestHandler", permission);
return callback(false);
});
win?.webContents.session.setPermissionCheckHandler(() => {
// log.warn("win setPermissionCheckHandler", permission, origin);
return false;
});

View File

@@ -30,8 +30,8 @@ export function validateGpuCache(app: Electron.App) {
if (verFile === undefined) {
removeDir(cacheDir);
} else {
const verFile = readVersionFile(cacheDir);
if (verFile.version !== app.getVersion()) {
const verFileData = readVersionFile(cacheDir);
if (verFileData.version !== app.getVersion()) {
removeDir(cacheDir);
} else {
return;
@@ -39,7 +39,7 @@ export function validateGpuCache(app: Electron.App) {
}
}
fs.mkdirSync(cacheDir);
fs.mkdirSync(cacheDir, { recursive: true });
createVersionFile(app.getVersion(), cacheDir);
} catch (e) {
log.error("failed to validate GPU Cache", e);
@@ -57,7 +57,7 @@ function fileExists(path: string) {
return true;
} catch (e) {
log.warn(`File does not exist: ${path}`);
log.warn(e.message);
log.warn((e as Error).message);
return false;
}
}

View File

@@ -414,14 +414,8 @@ export class AddonService {
: [];
}
public async getAllAddonsAvailableForUpdate(wowInstallation?: WowInstallation): Promise<Addon[]> {
return await this._addonStorage.queryAllAsync((addon) => {
if (typeof wowInstallation === "object" && wowInstallation.id !== addon.installationId) {
return false;
}
return addon.isIgnored !== true && AddonUtils.needsUpdate(addon);
});
public getAllAddonsAvailableForUpdate(wowInstallation?: WowInstallation): Promise<Addon[]> {
return this._addonStorage.getAvailableForUpdate(wowInstallation?.id);
}
public async installDependencies(
@@ -500,16 +494,12 @@ export class AddonService {
return results.filter((res) => res !== undefined).map((res) => res as Addon);
}
public async getAutoUpdateEnabledAddons(): Promise<Addon[]> {
return await this._addonStorage.queryAllAsync((addon) => {
return addon.isIgnored !== true && addon.autoUpdateEnabled && !!addon.installationId;
});
public getAutoUpdateEnabledAddons(): Promise<Addon[]> {
return this._addonStorage.getAutoUpdateEnabled();
}
public async getAllByExternalAddonId(externalAddonIds: string[]): Promise<Addon[]> {
return await this._addonStorage.queryAllAsync((addon) => {
return externalAddonIds.includes(addon.externalId);
});
public getAllByExternalAddonId(externalAddonIds: string[]): Promise<Addon[]> {
return this._addonStorage.getByExternalIds(externalAddonIds);
}
public async hasAnyWithExternalAddonIds(externalAddonIds: string[]): Promise<boolean> {

View File

@@ -2,12 +2,18 @@ import { Injectable } from "@angular/core";
import { Addon } from "wowup-lib-core";
import {
IPC_ADDONS_GET_ALL,
IPC_ADDONS_GET_ALL_FOR_INSTALLATION,
IPC_ADDONS_GET_ALL_FOR_PROVIDER,
IPC_ADDONS_GET_AUTO_UPDATE_ENABLED,
IPC_ADDONS_GET_AVAILABLE_FOR_UPDATE,
IPC_ADDONS_GET_BY_EXTERNAL_ID,
IPC_ADDONS_GET_BY_EXTERNAL_IDS,
IPC_ADDONS_SAVE_ALL,
ADDON_STORE_NAME,
IPC_STORE_SET_OBJECT,
IPC_STORE_GET_OBJECT,
IPC_STORE_REMOVE_OBJECT,
IPC_STORE_GET_ALL,
} from "../../../common/constants";
import { ElectronService } from "../electron/electron.service";
@@ -16,30 +22,10 @@ import { ElectronService } from "../electron/electron.service";
})
export class AddonStorageService {
public constructor(private _electronService: ElectronService) {}
private async getStore(): Promise<Addon[]> {
return await this._electronService.invoke(IPC_STORE_GET_ALL, ADDON_STORE_NAME);
}
public async queryAsync<T>(action: (store: Addon[]) => T): Promise<T> {
const store = await this.getStore();
return action(store);
}
public async queryAllAsync(action: (item: Addon) => boolean): Promise<Addon[]> {
const addons: Addon[] = [];
const store = await this.getStore();
for (const addon of store) {
if (action(addon)) {
addons.push(addon);
}
}
return addons;
}
public async saveAll(addons: Addon[]): Promise<void> {
public saveAll(addons: Addon[]): Promise<void> {
console.debug(`[addon-storage] save all: ${addons?.length ?? 0}`);
await this._electronService.invoke(IPC_ADDONS_SAVE_ALL, addons);
return this._electronService.invoke(IPC_ADDONS_SAVE_ALL, addons);
}
public setAsync(key: string | undefined, value: Addon): Promise<void> {
@@ -71,58 +57,31 @@ export class AddonStorageService {
await this.removeAllAsync(...addons);
}
public async getByExternalIdAsync(
externalId: string,
providerName: string,
installationId: string
): Promise<Addon | undefined> {
const addons: Addon[] = [];
const store = await this.getStore();
for (const addon of store) {
if (
addon.installationId === installationId &&
addon.externalId === externalId &&
addon.providerName === providerName
) {
addons.push(addon);
break;
}
}
return addons[0];
public getAll(): Promise<Addon[]> {
return this._electronService.invoke(IPC_ADDONS_GET_ALL);
}
public async getAll(): Promise<Addon[]> {
return await this.getStore();
public getAllForInstallationIdAsync(installationId: string): Promise<Addon[]> {
return this._electronService.invoke(IPC_ADDONS_GET_ALL_FOR_INSTALLATION, installationId);
}
public async getAllForInstallationIdAsync(
installationId: string,
validator?: (addon: Addon) => boolean
): Promise<Addon[]> {
const addons: Addon[] = [];
const store = await this.getStore();
for (const addon of store) {
if (addon.installationId === installationId && (!validator || validator(addon))) {
addons.push(addon);
}
}
return addons;
public getAllForProviderAsync(providerName: string): Promise<Addon[]> {
return this._electronService.invoke(IPC_ADDONS_GET_ALL_FOR_PROVIDER, providerName);
}
public async getAllForProviderAsync(providerName: string, validator?: (addon: Addon) => boolean): Promise<Addon[]> {
const addons: Addon[] = [];
const store = await this.getStore();
public getByExternalIdAsync(externalId: string, providerName: string, installationId: string): Promise<Addon | undefined> {
return this._electronService.invoke(IPC_ADDONS_GET_BY_EXTERNAL_ID, externalId, providerName, installationId);
}
for (const addon of store) {
if (addon.providerName === providerName && (!validator || validator(addon))) {
addons.push(addon);
}
}
public getByExternalIds(externalIds: string[]): Promise<Addon[]> {
return this._electronService.invoke(IPC_ADDONS_GET_BY_EXTERNAL_IDS, externalIds);
}
return addons;
public getAvailableForUpdate(installationId?: string): Promise<Addon[]> {
return this._electronService.invoke(IPC_ADDONS_GET_AVAILABLE_FOR_UPDATE, installationId);
}
public getAutoUpdateEnabled(): Promise<Addon[]> {
return this._electronService.invoke(IPC_ADDONS_GET_AUTO_UPDATE_ENABLED);
}
}

View File

@@ -15,6 +15,16 @@ export class PatchNotesService {
}
const CHANGELOGS: ChangeLog[] = [
{
Version: "2.22.1",
html: `
<h4>Fixes</h4>
<ul>
<li>Update electron versions</li>
<li>Various bug fixes</li>
</ul>
`,
},
{
Version: "2.22.0",
html: `

View File

@@ -86,6 +86,13 @@ export const IPC_REMOVE_AS_DEFAULT_PROTOCOL_CLIENT = "remove-as-default-protocol
export const IPC_REQUEST_INSTALL_FROM_URL = "request-install-from-url";
export const IPC_CUSTOM_PROTOCOL_RECEIVED = "custom-protocol-received";
export const IPC_ADDONS_SAVE_ALL = "addons-save-all";
export const IPC_ADDONS_GET_ALL_FOR_INSTALLATION = "addons-get-all-for-installation";
export const IPC_ADDONS_GET_ALL_FOR_PROVIDER = "addons-get-all-for-provider";
export const IPC_ADDONS_GET_ALL = "addons-get-all";
export const IPC_ADDONS_GET_BY_EXTERNAL_ID = "addons-get-by-external-id";
export const IPC_ADDONS_GET_BY_EXTERNAL_IDS = "addons-get-by-external-ids";
export const IPC_ADDONS_GET_AVAILABLE_FOR_UPDATE = "addons-get-available-for-update";
export const IPC_ADDONS_GET_AUTO_UPDATE_ENABLED = "addons-get-auto-update-enabled";
export const IPC_GET_PENDING_OPEN_URLS = "get-pending-open-urls";
export const IPC_GET_LATEST_DIR_UPDATE_TIME = "get-latest-dir-update-time";
export const IPC_LIST_DIR_RECURSIVE = "list-dir-recursive";

View File

@@ -25,6 +25,13 @@ declare type MainChannels =
// Events that can be sent from renderer to main
declare type RendererChannels =
| "addons-get-all"
| "addons-get-all-for-installation"
| "addons-get-all-for-provider"
| "addons-get-auto-update-enabled"
| "addons-get-available-for-update"
| "addons-get-by-external-id"
| "addons-get-by-external-ids"
| "addons-save-all"
| "app-install-update"
| "app-update-check-for-update"