mirror of
https://github.com/WowUp/WowUp.git
synced 2026-05-24 22:46:45 -04:00
Merge branch 'develop' into pr/1010
This commit is contained in:
@@ -103,8 +103,6 @@ if (preferenceStore.get(USE_HARDWARE_ACCELERATION_PREFERENCE_KEY) === "false") {
|
||||
log.info("Hardware acceleration enabled");
|
||||
}
|
||||
|
||||
app.allowRendererProcessReuse = false;
|
||||
|
||||
// Some servers don't supply good CORS headers for us, so we ignore them.
|
||||
app.commandLine.appendSwitch("disable-features", "OutOfBlinkCors");
|
||||
|
||||
@@ -261,8 +259,11 @@ function createWindow(): BrowserWindow {
|
||||
allowRunningInsecureContent: argv.serve,
|
||||
webSecurity: false,
|
||||
nativeWindowOpen: true,
|
||||
enableRemoteModule: false, // This is only required for electron store https://github.com/sindresorhus/electron-store/issues/152,
|
||||
additionalArguments: [`--log-path=${LOG_PATH}`, `--user-data-path=${app.getPath("userData")}`],
|
||||
additionalArguments: [
|
||||
`--log-path=${LOG_PATH}`,
|
||||
`--user-data-path=${app.getPath("userData")}`,
|
||||
`--base-bg-color=${getBackgroundColor()}`,
|
||||
],
|
||||
},
|
||||
show: false,
|
||||
};
|
||||
|
||||
@@ -36,6 +36,7 @@ function getArg(argKey: string): string {
|
||||
|
||||
const LOG_PATH = getArg("log-path");
|
||||
const USER_DATA_PATH = getArg("user-data-path");
|
||||
const BASE_BG_COLOR = getArg("base-bg-color");
|
||||
|
||||
log.transports.file.resolvePath = (variables: log.PathVariables) => {
|
||||
return join(LOG_PATH, variables.fileName);
|
||||
@@ -73,25 +74,30 @@ function openPath(path: string): Promise<string> {
|
||||
return shell.openPath(path);
|
||||
}
|
||||
|
||||
if (window.opener === null) {
|
||||
window.log = log;
|
||||
window.libs = {
|
||||
handlebars: require("handlebars"),
|
||||
autoLaunch: require("auto-launch"),
|
||||
};
|
||||
window.userDataPath = USER_DATA_PATH;
|
||||
window.logPath = LOG_PATH;
|
||||
window.platform = process.platform;
|
||||
window.wowup = {
|
||||
onRendererEvent,
|
||||
onceRendererEvent,
|
||||
rendererSend,
|
||||
rendererInvoke,
|
||||
rendererOff,
|
||||
rendererOn,
|
||||
openExternal,
|
||||
openPath,
|
||||
};
|
||||
} else {
|
||||
console.log("HAS OPENER");
|
||||
try {
|
||||
if (window.opener === null) {
|
||||
window.log = log;
|
||||
window.baseBgColor = BASE_BG_COLOR;
|
||||
window.libs = {
|
||||
handlebars: require("handlebars"),
|
||||
autoLaunch: require("auto-launch"),
|
||||
};
|
||||
window.userDataPath = USER_DATA_PATH;
|
||||
window.logPath = LOG_PATH;
|
||||
window.platform = process.platform;
|
||||
window.wowup = {
|
||||
onRendererEvent,
|
||||
onceRendererEvent,
|
||||
rendererSend,
|
||||
rendererInvoke,
|
||||
rendererOff,
|
||||
rendererOn,
|
||||
openExternal,
|
||||
openPath,
|
||||
};
|
||||
} else {
|
||||
console.log("HAS OPENER");
|
||||
}
|
||||
} catch (e) {
|
||||
log.error(e);
|
||||
}
|
||||
|
||||
3539
wowup-electron/package-lock.json
generated
3539
wowup-electron/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "wowup",
|
||||
"productName": "WowUp",
|
||||
"version": "2.5.0-beta.13",
|
||||
"version": "2.5.0-beta.14",
|
||||
"description": "World of Warcraft addon updater",
|
||||
"homepage": "https://wowup.io",
|
||||
"author": {
|
||||
@@ -49,60 +49,61 @@
|
||||
"pretty": "npx prettier --write . && ng lint --fix"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-builders/custom-webpack": "12.1.0",
|
||||
"@angular-devkit/build-angular": "12.1.3",
|
||||
"@angular-builders/custom-webpack": "12.1.1",
|
||||
"@angular-devkit/build-angular": "12.2.4",
|
||||
"@angular-eslint/builder": "12.3.1",
|
||||
"@angular-eslint/eslint-plugin": "12.3.1",
|
||||
"@angular-eslint/eslint-plugin-template": "12.3.1",
|
||||
"@angular-eslint/schematics": "12.3.1",
|
||||
"@angular-eslint/template-parser": "12.3.1",
|
||||
"@angular/cli": "12.1.3",
|
||||
"@angular/cli": "12.2.4",
|
||||
"@ngx-translate/core": "13.0.0",
|
||||
"@ngx-translate/http-loader": "6.0.0",
|
||||
"@types/globrex": "0.1.1",
|
||||
"@types/jasmine": "3.8.2",
|
||||
"@types/jasmine": "3.9.0",
|
||||
"@types/jasminewd2": "2.0.10",
|
||||
"@types/lodash": "4.14.172",
|
||||
"@types/markdown-it": "12.2.0",
|
||||
"@types/markdown-it": "12.2.1",
|
||||
"@types/mocha": "9.0.0",
|
||||
"@types/node": "16.6.2",
|
||||
"@types/opossum": "4.1.2",
|
||||
"@types/node": "14.17.15",
|
||||
"@types/object-hash": "2.1.1",
|
||||
"@types/opossum": "6.2.0",
|
||||
"@types/slug": "5.0.2",
|
||||
"@types/string-similarity": "4.0.0",
|
||||
"@types/uuid": "8.3.1",
|
||||
"@typescript-eslint/eslint-plugin": "4.29.2",
|
||||
"@typescript-eslint/eslint-plugin-tslint": "4.26.0",
|
||||
"@typescript-eslint/parser": "4.29.2",
|
||||
"@typescript-eslint/eslint-plugin": "4.31.0",
|
||||
"@typescript-eslint/eslint-plugin-tslint": "4.31.0",
|
||||
"@typescript-eslint/parser": "4.31.0",
|
||||
"chai": "4.3.4",
|
||||
"conventional-changelog-cli": "2.1.1",
|
||||
"core-js": "3.17.2",
|
||||
"cross-env": "7.0.3",
|
||||
"dotenv": "10.0.0",
|
||||
"electron": "13.2.1",
|
||||
"electron": "14.0.0",
|
||||
"electron-builder": "22.11.7",
|
||||
"electron-notarize": "1.1.0",
|
||||
"electron-reload": "1.5.0",
|
||||
"electron-notarize": "1.1.1",
|
||||
"electron-reload": "2.0.0-alpha.1",
|
||||
"eslint": "7.32.0",
|
||||
"eslint-config-prettier": "8.3.0",
|
||||
"eslint-plugin-import": "2.23.4",
|
||||
"eslint-plugin-jsdoc": "35.1.3",
|
||||
"eslint-plugin-import": "2.24.2",
|
||||
"eslint-plugin-jsdoc": "36.1.0",
|
||||
"eslint-plugin-prefer-arrow": "1.2.3",
|
||||
"i18next-json-sync": "2.3.1",
|
||||
"jasmine-core": "3.8.0",
|
||||
"jasmine-core": "3.9.0",
|
||||
"jasmine-spec-reporter": "7.0.0",
|
||||
"karma": "6.3.4",
|
||||
"karma-coverage-istanbul-reporter": "3.0.3",
|
||||
"karma-electron": "7.0.0",
|
||||
"karma-jasmine": "4.0.1",
|
||||
"karma-jasmine-html-reporter": "1.7.0",
|
||||
"mocha": "9.0.3",
|
||||
"nan": "2.14.2",
|
||||
"node-addon-api": "4.0.0",
|
||||
"node-gyp": "8.1.0",
|
||||
"mocha": "9.1.1",
|
||||
"node-addon-api": "4.1.0",
|
||||
"node-gyp": "8.2.0",
|
||||
"npm-run-all": "4.1.5",
|
||||
"prettier": "2.3.2",
|
||||
"spectron": "15.0.0",
|
||||
"ts-node": "10.2.1",
|
||||
"typescript": "4.2.4",
|
||||
"typescript": "4.3.5",
|
||||
"wait-on": "6.0.0",
|
||||
"webdriver-manager": "12.1.8"
|
||||
},
|
||||
@@ -110,17 +111,17 @@
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@angular/animations": "12.1.3",
|
||||
"@angular/cdk": "12.1.3",
|
||||
"@angular/common": "12.1.3",
|
||||
"@angular/compiler": "12.1.3",
|
||||
"@angular/compiler-cli": "12.1.3",
|
||||
"@angular/core": "12.1.3",
|
||||
"@angular/forms": "12.1.3",
|
||||
"@angular/material": "12.1.3",
|
||||
"@angular/platform-browser": "12.1.3",
|
||||
"@angular/platform-browser-dynamic": "12.1.3",
|
||||
"@angular/router": "12.1.3",
|
||||
"@angular/animations": "12.2.4",
|
||||
"@angular/cdk": "12.2.4",
|
||||
"@angular/common": "12.2.4",
|
||||
"@angular/compiler": "12.2.4",
|
||||
"@angular/compiler-cli": "12.2.4",
|
||||
"@angular/core": "12.2.4",
|
||||
"@angular/forms": "12.2.4",
|
||||
"@angular/material": "12.2.4",
|
||||
"@angular/platform-browser": "12.2.4",
|
||||
"@angular/platform-browser-dynamic": "12.2.4",
|
||||
"@angular/router": "12.2.4",
|
||||
"@bbob/core": "2.7.0",
|
||||
"@bbob/html": "2.7.0",
|
||||
"@bbob/preset-html5": "2.7.0",
|
||||
@@ -133,9 +134,8 @@
|
||||
"ag-grid-angular": "26.0.0",
|
||||
"ag-grid-community": "26.0.0",
|
||||
"auto-launch": "5.0.5",
|
||||
"axios": "0.21.1",
|
||||
"axios": "0.21.4",
|
||||
"compare-versions": "3.6.0",
|
||||
"core-js": "3.13.1",
|
||||
"electron-log": "4.4.1",
|
||||
"electron-store": "8.0.0",
|
||||
"electron-updater": "4.3.9",
|
||||
@@ -150,7 +150,8 @@
|
||||
"ngx-translate-messageformat-compiler": "4.10.0",
|
||||
"node-cache": "5.1.2",
|
||||
"node-disk-info": "1.3.0",
|
||||
"opossum": "6.2.0",
|
||||
"object-hash": "2.2.0",
|
||||
"opossum": "6.2.1",
|
||||
"p-limit": "3.1.0",
|
||||
"protobufjs": "6.11.2",
|
||||
"pushy-electron": "1.0.8",
|
||||
|
||||
@@ -5,6 +5,7 @@ import { AddonInstallState } from "../models/wowup/addon-install-state";
|
||||
import { AddonStatusSortOrder } from "../models/wowup/addon-status-sort-order";
|
||||
import * as AddonUtils from "../utils/addon.utils";
|
||||
import { ADDON_PROVIDER_UNKNOWN } from "../../common/constants";
|
||||
import * as objectHash from "object-hash";
|
||||
|
||||
export class AddonViewModel {
|
||||
public addon: Addon | undefined;
|
||||
@@ -19,9 +20,12 @@ export class AddonViewModel {
|
||||
public isLoadOnDemand = false;
|
||||
public hasThumbnail = false;
|
||||
public thumbnailLetter = "";
|
||||
public showUpdate = false;
|
||||
public canonicalName = "";
|
||||
|
||||
public get isIgnored(): boolean {
|
||||
return this.addon?.isIgnored ?? false;
|
||||
}
|
||||
|
||||
public get name(): string {
|
||||
return this.addon?.name ?? "";
|
||||
}
|
||||
@@ -46,6 +50,10 @@ export class AddonViewModel {
|
||||
return this.addon?.author ?? "";
|
||||
}
|
||||
|
||||
public get hash(): string {
|
||||
return objectHash(this.addon);
|
||||
}
|
||||
|
||||
public constructor(addon: Addon | undefined) {
|
||||
this.addon = addon;
|
||||
this.installedAt = addon?.installedAt ? new Date(addon?.installedAt).getTime() : 0;
|
||||
|
||||
@@ -1,70 +1,64 @@
|
||||
<div>
|
||||
<div class="addon-column row align-items-center">
|
||||
<div class="thumbnail-container">
|
||||
<app-addon-thumbnail [url]="getThumbnailUrl()" [name]="listItem.addon?.name ?? ''"></app-addon-thumbnail>
|
||||
<app-addon-thumbnail [url]="thumbnailUrl$ | async" [name]="name$ | async"></app-addon-thumbnail>
|
||||
</div>
|
||||
<div class="version-container">
|
||||
<div class="title-container">
|
||||
<a class="addon-title hover-text-2" (click)="viewDetails()"
|
||||
[ngClass]="{ 'text-3': listItem.addon?.isIgnored, 'text-warning': hasWarning() }">{{
|
||||
listItem.addon?.name
|
||||
[ngClass]="{ 'text-3': isIgnored$ | async, 'text-warning': hasWarning$ | async }">{{
|
||||
name$ | async
|
||||
}}</a>
|
||||
</div>
|
||||
|
||||
<div class="addon-version text-2 row align-items-center" [ngClass]="{ ignored: listItem.addon?.isIgnored }">
|
||||
<div *ngIf="listItem.addon?.fundingLinks" class="addon-funding mr-2">
|
||||
<app-funding-button *ngFor="let link of listItem.addon?.fundingLinks ?? []" [funding]="link" size="small">
|
||||
<div class="addon-version text-2 row align-items-center" [ngClass]="{ 'ignored': isIgnored$ | async }">
|
||||
<div *ngIf="hasFundingLinks$ | async" class="addon-funding mr-2">
|
||||
<app-funding-button *ngFor="let link of fundingLinks$ | async" [funding]="link" size="small">
|
||||
</app-funding-button>
|
||||
</div>
|
||||
<div *ngIf="listItem.isBetaChannel() || listItem.isAlphaChannel()" class="channel bg-secondary-3 mr-2"
|
||||
[ngClass]="{
|
||||
beta: listItem.isBetaChannel(),
|
||||
alpha: listItem.isAlphaChannel()
|
||||
}">
|
||||
{{ channelTranslationKey | translate }}
|
||||
<div *ngIf="showChannel$ | async" class="channel bg-secondary-3 mr-2" [ngClass]="channelClass$ | async">
|
||||
{{ channelTranslationKey$ | async | translate }}
|
||||
</div>
|
||||
<div *ngIf="hasMultipleProviders === true" class="mr-2">
|
||||
<div *ngIf="hasMultipleProviders$ | async" class="mr-2">
|
||||
<mat-icon class="auto-update-icon" svgIcon="fas:code-branch"
|
||||
[matTooltip]="'PAGES.MY_ADDONS.MULTIPLE_PROVIDERS_TOOLTIP' | translate">
|
||||
</mat-icon>
|
||||
</div>
|
||||
<div *ngIf="listItem.addon?.autoUpdateEnabled === true" class="mr-2">
|
||||
<div *ngIf="autoUpdateEnabled$ | async" class="mr-2">
|
||||
<mat-icon class="auto-update-icon text-2"
|
||||
[matTooltip]="'PAGES.MY_ADDONS.TABLE.AUTO_UPDATE_ICON_TOOLTIP' | translate" svgIcon="far:clock">
|
||||
</mat-icon>
|
||||
</div>
|
||||
<div *ngIf="hasRequiredDependencies()" class="mr-2"
|
||||
[matTooltip]="'COMMON.DEPENDENCY.TOOLTIP' | translate: dependencyTooltip">
|
||||
<div *ngIf="hasRequiredDependencies$ | async" class="mr-2"
|
||||
[matTooltip]="'COMMON.DEPENDENCY.TOOLTIP' | translate: (dependencyTooltip$ | async)">
|
||||
<mat-icon class="auto-update-icon" svgIcon="fas:link"></mat-icon>
|
||||
</div>
|
||||
<div *ngIf="listItem.isLoadOnDemand === true" class="mr-2">
|
||||
<div *ngIf="isLoadOnDemand$ | async" class="mr-2">
|
||||
<mat-icon class="auto-update-icon text-warning"
|
||||
[matTooltip]="'PAGES.MY_ADDONS.REQUIRED_DEPENDENCY_MISSING_TOOLTIP' | translate"
|
||||
svgIcon="fas:exclamation-triangle">
|
||||
</mat-icon>
|
||||
</div>
|
||||
<div *ngIf="hasWarning() === true" class="mr-2">
|
||||
<mat-icon class="auto-update-icon text-warning" [matTooltip]="getWarningText()"
|
||||
<div *ngIf="hasWarning$ | async" class="mr-2">
|
||||
<mat-icon class="auto-update-icon text-warning" [matTooltip]="getWarningText(listItem)"
|
||||
svgIcon="fas:exclamation-triangle">
|
||||
</mat-icon>
|
||||
</div>
|
||||
<div *ngIf="hasIgnoreReason() === true" class="mr-2">
|
||||
<mat-icon class="auto-update-icon" [matTooltip]="getIgnoreTooltipKey() | translate" [style.color]="'#ff9800'"
|
||||
[svgIcon]="getIgnoreIcon()">
|
||||
<div *ngIf="hasIgnoreReason$ | async" class="mr-2">
|
||||
<mat-icon class="auto-update-icon" [matTooltip]="ignoreTooltipKey$ | async | translate"
|
||||
[style.color]="'#ff9800'" [svgIcon]="ignoreIcon$ | async">
|
||||
</mat-icon>
|
||||
</div>
|
||||
<!-- If no warning and not ignored for some specific reason, default to this -->
|
||||
<div
|
||||
*ngIf="listItem.isLoadOnDemand === false && hasIgnoreReason() === false && hasWarning() === false && listItem.addon?.providerName === unknownProviderName"
|
||||
class="mr-2">
|
||||
<div *ngIf="isUnknownAddon$ | async" class="mr-2">
|
||||
<mat-icon class="auto-update-icon" [matTooltip]="'PAGES.MY_ADDONS.UNKNOWN_ADDON_INFO_TOOLTIP' | translate"
|
||||
[matTooltipClass]="['text-center']" [style.color]="'#ff9800'" svgIcon="fas:exclamation-triangle">
|
||||
</mat-icon>
|
||||
</div>
|
||||
{{ listItem.addon?.installedVersion }}
|
||||
<div *ngIf="showUpdateToVersion && listItem.needsUpdate()" class="text-1 row">
|
||||
{{ installedVersion$ | async }}
|
||||
<div *ngIf="showUpdateVersion$ | async" class="text-1 row">
|
||||
<mat-icon class="upgrade-icon" svgIcon="fas:play"></mat-icon>
|
||||
<div class="bg-secondary-4 text-2 rounded px-1">{{ listItem.addon?.latestVersion }}</div>
|
||||
<div class="bg-secondary-4 text-2 rounded px-1">{{ latestVersion$ | async }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -8,12 +8,16 @@ import { AddonViewModel } from "../../business-objects/addon-view-model";
|
||||
import { Addon } from "../../../common/entities/addon";
|
||||
import { MatModule } from "../../mat-module";
|
||||
import { NoopAnimationsModule } from "@angular/platform-browser/animations";
|
||||
import { SessionService } from "../../services/session/session.service";
|
||||
|
||||
describe("MyAddonsAddonCellComponent", () => {
|
||||
let component: MyAddonsAddonCellComponent;
|
||||
let fixture: ComponentFixture<MyAddonsAddonCellComponent>;
|
||||
let sessionService: SessionService;
|
||||
|
||||
beforeEach(async () => {
|
||||
sessionService = jasmine.createSpyObj("SessionService", [""], {});
|
||||
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [MyAddonsAddonCellComponent],
|
||||
imports: [
|
||||
@@ -32,7 +36,13 @@ describe("MyAddonsAddonCellComponent", () => {
|
||||
},
|
||||
}),
|
||||
],
|
||||
}).compileComponents();
|
||||
})
|
||||
.overrideComponent(MyAddonsAddonCellComponent, {
|
||||
set: {
|
||||
providers: [{ provide: SessionService, useValue: sessionService }],
|
||||
},
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(MyAddonsAddonCellComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
import { AgRendererComponent } from "ag-grid-angular";
|
||||
import { ICellRendererParams } from "ag-grid-community";
|
||||
import { BehaviorSubject, combineLatest } from "rxjs";
|
||||
import { filter, map } from "rxjs/operators";
|
||||
|
||||
import { Component, Input } from "@angular/core";
|
||||
import { Component } from "@angular/core";
|
||||
import { TranslateService } from "@ngx-translate/core";
|
||||
|
||||
import { ADDON_PROVIDER_UNKNOWN } from "../../../common/constants";
|
||||
import { AddonChannelType, AddonDependencyType, AddonWarningType } from "../../../common/wowup/models";
|
||||
import { AddonViewModel } from "../../business-objects/addon-view-model";
|
||||
import { DialogFactory } from "../../services/dialog/dialog.factory";
|
||||
import { SessionService } from "../../services/session/session.service";
|
||||
import * as AddonUtils from "../../utils/addon.utils";
|
||||
import { capitalizeString } from "../../utils/string.utils";
|
||||
|
||||
interface MyAddonsAddonCellComponentParams extends ICellRendererParams {
|
||||
showUpdateToVersion: boolean;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: "app-my-addons-addon-cell",
|
||||
@@ -21,40 +19,114 @@ interface MyAddonsAddonCellComponentParams extends ICellRendererParams {
|
||||
styleUrls: ["./my-addons-addon-cell.component.scss"],
|
||||
})
|
||||
export class MyAddonsAddonCellComponent implements AgRendererComponent {
|
||||
@Input("addon") public listItem!: AddonViewModel;
|
||||
private readonly _listItemSrc = new BehaviorSubject<AddonViewModel | undefined>(undefined);
|
||||
|
||||
public readonly capitalizeString = capitalizeString;
|
||||
public readonly unknownProviderName = ADDON_PROVIDER_UNKNOWN;
|
||||
public readonly listItem$ = this._listItemSrc.asObservable().pipe(filter((item) => item !== undefined));
|
||||
|
||||
public showUpdateToVersion = false;
|
||||
public warningType?: AddonWarningType;
|
||||
public warningText?: string;
|
||||
public hasMultipleProviders = false;
|
||||
public readonly name$ = this.listItem$.pipe(map((item) => item.name));
|
||||
|
||||
public get dependencyTooltip(): any {
|
||||
return {
|
||||
dependencyCount: this.getRequireDependencyCount(),
|
||||
};
|
||||
public readonly isIgnored$ = this.listItem$.pipe(map((item) => item.isIgnored));
|
||||
|
||||
public readonly hasWarning$ = this.listItem$.pipe(map((item) => this.hasWarning(item)));
|
||||
|
||||
public readonly hasFundingLinks$ = this.listItem$.pipe(
|
||||
map((item) => Array.isArray(item.addon?.fundingLinks) && item.addon.fundingLinks.length > 0)
|
||||
);
|
||||
|
||||
public readonly fundingLinks$ = this.listItem$.pipe(map((item) => item.addon?.fundingLinks ?? []));
|
||||
|
||||
public readonly showChannel$ = this.listItem$.pipe(map((item) => item.isBetaChannel() || item.isAlphaChannel()));
|
||||
|
||||
public readonly channelClass$ = this.listItem$.pipe(
|
||||
map((item) => {
|
||||
if (item.isBetaChannel()) {
|
||||
return "beta";
|
||||
}
|
||||
if (item.isAlphaChannel()) {
|
||||
return "alpha";
|
||||
}
|
||||
return "";
|
||||
})
|
||||
);
|
||||
|
||||
public readonly channelTranslationKey$ = this.listItem$.pipe(
|
||||
map((item) => {
|
||||
const channelType = item.addon?.channelType ?? AddonChannelType.Stable;
|
||||
return channelType === AddonChannelType.Alpha
|
||||
? "COMMON.ENUM.ADDON_CHANNEL_TYPE.ALPHA"
|
||||
: "COMMON.ENUM.ADDON_CHANNEL_TYPE.BETA";
|
||||
})
|
||||
);
|
||||
|
||||
public readonly hasMultipleProviders$ = this.listItem$.pipe(
|
||||
map((item) => (item.addon === undefined ? false : AddonUtils.hasMultipleProviders(item.addon)))
|
||||
);
|
||||
|
||||
public readonly autoUpdateEnabled$ = this.listItem$.pipe(map((item) => item.addon?.autoUpdateEnabled ?? false));
|
||||
|
||||
public readonly hasIgnoreReason$ = this.listItem$.pipe(map((item) => this.hasIgnoreReason(item)));
|
||||
|
||||
public readonly hasRequiredDependencies$ = this.listItem$.pipe(
|
||||
map((item) => this.getRequireDependencyCount(item) > 0)
|
||||
);
|
||||
|
||||
public readonly dependencyTooltip$ = this.listItem$.pipe(
|
||||
map((item) => {
|
||||
return {
|
||||
dependencyCount: this.getRequireDependencyCount(item),
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
public readonly isLoadOnDemand$ = this.listItem$.pipe(map((item) => item.isLoadOnDemand));
|
||||
|
||||
public readonly ignoreTooltipKey$ = this.listItem$.pipe(map((item) => this.getIgnoreTooltipKey(item)));
|
||||
|
||||
public readonly ignoreIcon$ = this.listItem$.pipe(map((item) => this.getIgnoreIcon(item)));
|
||||
|
||||
public readonly warningText$ = this.listItem$.pipe(
|
||||
filter((item) => this.hasWarning(item)),
|
||||
map((item) => this.getWarningText(item))
|
||||
);
|
||||
|
||||
public readonly isUnknownAddon$ = this.listItem$.pipe(
|
||||
map((item) => {
|
||||
return (
|
||||
!item.isLoadOnDemand &&
|
||||
!this.hasIgnoreReason(item) &&
|
||||
!this.hasWarning(item) &&
|
||||
item.addon.providerName === ADDON_PROVIDER_UNKNOWN
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
public readonly installedVersion$ = this.listItem$.pipe(map((item) => item.addon.installedVersion));
|
||||
|
||||
public readonly latestVersion$ = this.listItem$.pipe(map((item) => item.addon.latestVersion));
|
||||
|
||||
public readonly thumbnailUrl$ = this.listItem$.pipe(map((item) => item.addon.thumbnailUrl));
|
||||
|
||||
public readonly showUpdateVersion$ = combineLatest([
|
||||
this.listItem$,
|
||||
this.sessionService.myAddonsCompactVersion$,
|
||||
]).pipe(
|
||||
map(([item, compactVersion]) => {
|
||||
return compactVersion && item.needsUpdate();
|
||||
})
|
||||
);
|
||||
|
||||
public set listItem(item: AddonViewModel) {
|
||||
this._listItemSrc.next(item);
|
||||
}
|
||||
|
||||
public get channelTranslationKey(): string {
|
||||
const channelType = this.listItem.addon?.channelType ?? AddonChannelType.Stable;
|
||||
return channelType === AddonChannelType.Alpha
|
||||
? "COMMON.ENUM.ADDON_CHANNEL_TYPE.ALPHA"
|
||||
: "COMMON.ENUM.ADDON_CHANNEL_TYPE.BETA";
|
||||
}
|
||||
public constructor(
|
||||
private _translateService: TranslateService,
|
||||
private _dialogFactory: DialogFactory,
|
||||
public sessionService: SessionService
|
||||
) {}
|
||||
|
||||
public constructor(private _translateService: TranslateService, private _dialogFactory: DialogFactory) {}
|
||||
|
||||
public agInit(params: MyAddonsAddonCellComponentParams): void {
|
||||
this.listItem = params.data;
|
||||
this.showUpdateToVersion = this.listItem.showUpdate;
|
||||
|
||||
this.warningType = this.listItem.addon?.warningType;
|
||||
this.warningText = this.getWarningText();
|
||||
|
||||
this.hasMultipleProviders =
|
||||
this.listItem.addon === undefined ? false : AddonUtils.hasMultipleProviders(this.listItem.addon);
|
||||
public agInit(params: ICellRendererParams): void {
|
||||
this._listItemSrc.next(params.data);
|
||||
}
|
||||
|
||||
public refresh(): boolean {
|
||||
@@ -64,27 +136,19 @@ export class MyAddonsAddonCellComponent implements AgRendererComponent {
|
||||
public afterGuiAttached?(): void {}
|
||||
|
||||
public viewDetails(): void {
|
||||
this._dialogFactory.getAddonDetailsDialog(this.listItem);
|
||||
this._dialogFactory.getAddonDetailsDialog(this._listItemSrc.value);
|
||||
}
|
||||
|
||||
public getThumbnailUrl(): string {
|
||||
return this.listItem?.addon?.thumbnailUrl ?? "";
|
||||
public getRequireDependencyCount(item: AddonViewModel): number {
|
||||
return item.getDependencies(AddonDependencyType.Required).length;
|
||||
}
|
||||
|
||||
public getRequireDependencyCount(): number {
|
||||
return this.listItem.getDependencies(AddonDependencyType.Required).length;
|
||||
public hasIgnoreReason(item: AddonViewModel): boolean {
|
||||
return !!item?.addon?.ignoreReason;
|
||||
}
|
||||
|
||||
public hasRequiredDependencies(): boolean {
|
||||
return this.getRequireDependencyCount() > 0;
|
||||
}
|
||||
|
||||
public hasIgnoreReason(): boolean {
|
||||
return !!this.listItem?.addon?.ignoreReason;
|
||||
}
|
||||
|
||||
public getIgnoreTooltipKey(): string {
|
||||
switch (this.listItem.addon?.ignoreReason) {
|
||||
public getIgnoreTooltipKey(item: AddonViewModel): string {
|
||||
switch (item.addon?.ignoreReason) {
|
||||
case "git_repo":
|
||||
return "PAGES.MY_ADDONS.ADDON_IS_CODE_REPOSITORY";
|
||||
case "missing_dependency":
|
||||
@@ -94,8 +158,8 @@ export class MyAddonsAddonCellComponent implements AgRendererComponent {
|
||||
}
|
||||
}
|
||||
|
||||
public getIgnoreIcon(): string {
|
||||
switch (this.listItem.addon?.ignoreReason) {
|
||||
public getIgnoreIcon(item: AddonViewModel): string {
|
||||
switch (item.addon?.ignoreReason) {
|
||||
case "git_repo":
|
||||
return "fas:code";
|
||||
case "missing_dependency":
|
||||
@@ -105,20 +169,20 @@ export class MyAddonsAddonCellComponent implements AgRendererComponent {
|
||||
}
|
||||
}
|
||||
|
||||
public hasWarning(): boolean {
|
||||
return this.warningType !== undefined;
|
||||
public hasWarning(item: AddonViewModel): boolean {
|
||||
return item?.addon?.warningType !== undefined;
|
||||
}
|
||||
|
||||
public getWarningText(): string {
|
||||
if (!this.warningType) {
|
||||
public getWarningText(item: AddonViewModel): string {
|
||||
if (!this.hasWarning(item)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const toolTipParams = {
|
||||
providerName: this.listItem.providerName,
|
||||
providerName: item.providerName,
|
||||
};
|
||||
|
||||
switch (this.warningType) {
|
||||
switch (item.addon.warningType) {
|
||||
case AddonWarningType.MissingOnProvider:
|
||||
return this._translateService.instant("COMMON.ADDON_WARNING.MISSING_ON_PROVIDER_TOOLTIP", toolTipParams);
|
||||
case AddonWarningType.NoProviderFiles:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AgGridModule } from "ag-grid-angular";
|
||||
import { LIGHTBOX_CONFIG, LightboxModule } from "ng-gallery/lightbox";
|
||||
import { LightboxModule } from "ng-gallery/lightbox";
|
||||
|
||||
import { CommonModule, DatePipe } from "@angular/common";
|
||||
import { NgModule } from "@angular/core";
|
||||
@@ -40,18 +40,19 @@ import { MatModule } from "../../mat-module";
|
||||
import { DownloadCountPipe } from "../../pipes/download-count.pipe";
|
||||
import { GetAddonListItemFilePropPipe } from "../../pipes/get-addon-list-item-file-prop.pipe";
|
||||
import { InterfaceFormatPipe } from "../../pipes/interface-format.pipe";
|
||||
import { InvertBoolPipe } from "../../pipes/inverse-bool.pipe";
|
||||
import { NgxDatePipe } from "../../pipes/ngx-date.pipe";
|
||||
import { RelativeDurationPipe } from "../../pipes/relative-duration-pipe";
|
||||
import { SizeDisplayPipe } from "../../pipes/size-display.pipe";
|
||||
import { TrustHtmlPipe } from "../../pipes/trust-html.pipe";
|
||||
import { SharedModule } from "../../shared.module";
|
||||
import { AboutComponent } from "../about/about.component";
|
||||
import { AccountPageComponent } from "../account-page/account-page.component";
|
||||
import { GetAddonsComponent } from "../get-addons/get-addons.component";
|
||||
import { MyAddonsComponent } from "../my-addons/my-addons.component";
|
||||
import { OptionsComponent } from "../options/options.component";
|
||||
import { HomeRoutingModule } from "./home-routing.module";
|
||||
import { HomeComponent } from "./home.component";
|
||||
import { AccountPageComponent } from "../account-page/account-page.component";
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@@ -66,6 +67,7 @@ import { AccountPageComponent } from "../account-page/account-page.component";
|
||||
PotentialAddonTableColumnComponent,
|
||||
DownloadCountPipe,
|
||||
InterfaceFormatPipe,
|
||||
InvertBoolPipe,
|
||||
NgxDatePipe,
|
||||
GetAddonListItemFilePropPipe,
|
||||
RelativeDurationPipe,
|
||||
|
||||
@@ -7,8 +7,9 @@
|
||||
<div class="select-container">
|
||||
<mat-form-field>
|
||||
<mat-label>{{ "PAGES.MY_ADDONS.CLIENT_TYPE_SELECT_LABEL" | translate }}</mat-label>
|
||||
<mat-select class="select pointer" [(value)]="selectedInstallationId" (selectionChange)="onClientChange()"
|
||||
[disabled]="enableControls === false" [disableOptionCentering]="true">
|
||||
<mat-select class="select pointer" [value]="selectedWowInstallationId$ | async"
|
||||
(selectionChange)="onClientChange($event)" [disabled]="enableControls$ | async | invertBool"
|
||||
[disableOptionCentering]="true">
|
||||
<mat-option [value]="installation.id" *ngFor="let installation of wowInstallations$ | async">
|
||||
{{ installation.label }}
|
||||
</mat-option>
|
||||
@@ -16,12 +17,12 @@
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="right-container">
|
||||
<div class="filter-container" *ngIf="selectedInstallation !== undefined">
|
||||
<div class="filter-container" *ngIf="hasSelectedWowInstallationId$ | async">
|
||||
<mat-form-field>
|
||||
<mat-label>{{ "PAGES.MY_ADDONS.FILTER_LABEL" | translate }}</mat-label>
|
||||
<input matInput (input)="filterInput$.next($event.target.value)" [(ngModel)]="filter" />
|
||||
<button mat-button color="accent" *ngIf="filter" matSuffix mat-icon-button aria-label="Clear"
|
||||
(click)="onClearFilter()">
|
||||
<input #addonFilter matInput />
|
||||
<button mat-button color="accent" *ngIf="filterInput$ | async" matSuffix mat-icon-button aria-label="Clear"
|
||||
(click)="onClickClearFilter()">
|
||||
<mat-icon svgIcon="fas:times"></mat-icon>
|
||||
</button>
|
||||
</mat-form-field>
|
||||
@@ -31,13 +32,12 @@
|
||||
<div class="row">
|
||||
<button mat-flat-button color="primary" class="menu-button"
|
||||
[matTooltip]="'PAGES.MY_ADDONS.UPDATE_ALL_BUTTON_TOOLTIP' | translate"
|
||||
[disabled]="enableControls === false || enableUpdateAll === false" (click)="onUpdateAll()">
|
||||
[disabled]="enableUpdateAll$ | async | invertBool" (click)="onUpdateAll()">
|
||||
{{ "PAGES.MY_ADDONS.UPDATE_ALL_BUTTON" | translate }}
|
||||
</button>
|
||||
<div class="menu-button-divider"></div>
|
||||
<button mat-flat-button color="primary" class="chip col justify-content-center"
|
||||
[matMenuTriggerFor]="updateAllMenu"
|
||||
[disabled]="enableControls === false || (addonService.anyUpdatesAvailable$ | async) === false">
|
||||
[matMenuTriggerFor]="updateAllMenu" [disabled]="enableUpdateExtra$ | async | invertBool">
|
||||
<mat-icon svgIcon="fas:caret-down"></mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
@@ -52,35 +52,34 @@
|
||||
<!-- CHECK UPDATES -->
|
||||
<button mat-flat-button color="primary"
|
||||
[matTooltip]="'PAGES.MY_ADDONS.CHECK_UPDATES_BUTTON_TOOLTIP' | translate"
|
||||
[disabled]="enableControls === false" (click)="onRefresh()">
|
||||
[disabled]="enableControls$ | async | invertBool" (click)="onRefresh()">
|
||||
{{ "PAGES.MY_ADDONS.CHECK_UPDATES_BUTTON" | translate }}
|
||||
</button>
|
||||
<!-- RESCAN -->
|
||||
<button mat-flat-button color="primary"
|
||||
[matTooltip]="'PAGES.MY_ADDONS.RESCAN_FOLDERS_BUTTON_TOOLTIP' | translate"
|
||||
[disabled]="enableControls === false" (click)="onReScan()">
|
||||
[disabled]="enableControls$ | async | invertBool" (click)="onReScan()">
|
||||
{{ "PAGES.MY_ADDONS.RESCAN_FOLDERS_BUTTON" | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="spinner-container flex-grow-1" *ngIf="(isBusy$ | async) === true">
|
||||
<app-progress-spinner [message]="spinnerMessage"> </app-progress-spinner>
|
||||
<div class="spinner-container flex-grow-1" *ngIf="isBusy$ | async">
|
||||
<app-progress-spinner [message]="spinnerMessage$ | async"> </app-progress-spinner>
|
||||
</div>
|
||||
|
||||
<div *ngIf="(isBusy$ | async) === false && hasData === false" class="no-addons-container text-1 flex-grow-1">
|
||||
<div *ngIf="showNoAddons$ | async" class="no-addons-container text-1 flex-grow-1">
|
||||
<h1>{{ "COMMON.SEARCH.NO_ADDONS" | translate }}</h1>
|
||||
</div>
|
||||
|
||||
<ag-grid-angular class="wu-ag-table ag-theme-material" [hidden]="(isBusy$ | async) === true || hasData === false"
|
||||
[rowData]="rowData" [columnDefs]="columnDefs" [rowSelection]="'multiple'" [getRowNodeId]="getRowNodeId"
|
||||
[frameworkComponents]="frameworkComponents" [rowHeight]="63" [immutableData]="true" [rowClassRules]="rowClassRules"
|
||||
<ag-grid-angular class="wu-ag-table ag-theme-material" [hidden]="hideGrid$ | async" [rowData]="rowDataG" [columnDefs]="columnDefs"
|
||||
[rowSelection]="'multiple'" [getRowNodeId]="getRowNodeId" [frameworkComponents]="frameworkComponents"
|
||||
[rowHeight]="63" [immutableData]="true" [rowClassRules]="rowClassRules"
|
||||
[overlayNoRowsTemplate]="overlayNoRowsTemplate" (gridReady)="onGridReady($event)"
|
||||
(rowDoubleClicked)="onRowDoubleClicked($event)" (rowClicked)="onRowClicked($event)"
|
||||
(rowDataUpdated)="onRowDataChanged()" (sortChanged)="onSortChanged($event)"
|
||||
(cellContextMenu)="onCellContext($event)" (keydown)="handleKeyboardEvent($event)"
|
||||
(firstDataRendered)="onFirstDataRendered()">
|
||||
(sortChanged)="onSortChanged($event)" (cellContextMenu)="onCellContext($event)"
|
||||
(keydown)="handleKeyboardEvent($event)" (firstDataRendered)="onFirstDataRendered()">
|
||||
</ag-grid-angular>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { BehaviorSubject, Subject } from "rxjs";
|
||||
|
||||
import { OverlayModule } from "@angular/cdk/overlay";
|
||||
import { HttpClient, HttpClientModule } from "@angular/common/http";
|
||||
import { CUSTOM_ELEMENTS_SCHEMA } from "@angular/core";
|
||||
import { CUSTOM_ELEMENTS_SCHEMA, ElementRef } from "@angular/core";
|
||||
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||
import { MatDialog } from "@angular/material/dialog";
|
||||
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
||||
@@ -25,6 +25,17 @@ import { overrideIconModule } from "../../tests/mock-mat-icon";
|
||||
import { WarcraftInstallationService } from "../../services/warcraft/warcraft-installation.service";
|
||||
import { RelativeDurationPipe } from "../../pipes/relative-duration-pipe";
|
||||
import { PushService } from "../../services/push/push.service";
|
||||
import { InvertBoolPipe } from "../../pipes/inverse-bool.pipe";
|
||||
|
||||
export class MockElementRef extends ElementRef {
|
||||
public constructor() {
|
||||
super(null);
|
||||
}
|
||||
}
|
||||
|
||||
export function mockElementFactory(): ElementRef {
|
||||
return new ElementRef({ nativeElement: jasmine.createSpyObj("nativeElement", ["value"]) });
|
||||
}
|
||||
|
||||
describe("MyAddonsComponent", () => {
|
||||
let component: MyAddonsComponent;
|
||||
@@ -72,6 +83,7 @@ describe("MyAddonsComponent", () => {
|
||||
selectedClientType$: new BehaviorSubject(WowClientType.Retail).asObservable(),
|
||||
targetFileInstallComplete$: new Subject<boolean>(),
|
||||
addonsChanged$: new BehaviorSubject([]),
|
||||
selectedWowInstallation$: new BehaviorSubject(undefined),
|
||||
});
|
||||
warcraftServiceSpy = jasmine.createSpyObj("WarcraftService", [""], {
|
||||
installedClientTypesSelectItems$: new BehaviorSubject<WowClientType[] | undefined>(undefined).asObservable(),
|
||||
@@ -87,7 +99,7 @@ describe("MyAddonsComponent", () => {
|
||||
});
|
||||
|
||||
let testBed = TestBed.configureTestingModule({
|
||||
declarations: [MyAddonsComponent],
|
||||
declarations: [MyAddonsComponent, InvertBoolPipe],
|
||||
imports: [
|
||||
MatModule,
|
||||
OverlayModule,
|
||||
@@ -129,6 +141,11 @@ describe("MyAddonsComponent", () => {
|
||||
fixture = TestBed.createComponent(MyAddonsComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
||||
component.addonFilter = {
|
||||
nativeElement: jasmine.createSpyObj("nativeElement", ["value"]),
|
||||
};
|
||||
console.debug("addonFilter", component.addonFilter);
|
||||
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
@@ -138,6 +155,7 @@ describe("MyAddonsComponent", () => {
|
||||
});
|
||||
|
||||
it("should create", () => {
|
||||
console.debug("addonFilter", component.addonFilter);
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,11 +12,20 @@ import {
|
||||
} from "ag-grid-community";
|
||||
import * as _ from "lodash";
|
||||
import { join } from "path";
|
||||
import { BehaviorSubject, from, Observable, of, Subject, Subscription, zip } from "rxjs";
|
||||
import { catchError, debounceTime, delay, filter, first, map, switchMap, tap } from "rxjs/operators";
|
||||
import { BehaviorSubject, combineLatest, from, fromEvent, Observable, of, Subject, Subscription, zip } from "rxjs";
|
||||
import { catchError, debounceTime, distinctUntilChanged, filter, first, map, switchMap, tap } from "rxjs/operators";
|
||||
|
||||
import { Overlay, OverlayRef } from "@angular/cdk/overlay";
|
||||
import { AfterViewInit, ChangeDetectorRef, Component, Input, OnDestroy, OnInit, ViewChild } from "@angular/core";
|
||||
import {
|
||||
AfterViewInit,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
ElementRef,
|
||||
Input,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
ViewChild,
|
||||
} from "@angular/core";
|
||||
import { MatCheckboxChange } from "@angular/material/checkbox";
|
||||
import { MatDialog, MatDialogRef } from "@angular/material/dialog";
|
||||
import { MatMenuTrigger } from "@angular/material/menu";
|
||||
@@ -57,45 +66,95 @@ import { PushService } from "../../services/push/push.service";
|
||||
styleUrls: ["./my-addons.component.scss"],
|
||||
})
|
||||
export class MyAddonsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
@Input("tabIndex") public tabIndex!: number;
|
||||
@Input("tabIndex") public set tabIndex(value: number) {
|
||||
this._tabIndexSrc.next(value);
|
||||
}
|
||||
|
||||
@ViewChild("addonContextMenuTrigger", { static: false }) public contextMenu!: MatMenuTrigger;
|
||||
@ViewChild("addonMultiContextMenuTrigger", { static: false }) public multiContextMenu!: MatMenuTrigger;
|
||||
@ViewChild("columnContextMenuTrigger", { static: false }) public columnContextMenu!: MatMenuTrigger;
|
||||
@ViewChild("addonFilter") public addonFilter: ElementRef;
|
||||
|
||||
// @HostListener("window:keydown", ["$event"])
|
||||
private readonly _operationErrorSrc = new Subject<Error>();
|
||||
private readonly _isBusySrc = new BehaviorSubject<boolean>(true);
|
||||
private readonly _enableControlsSrc = new BehaviorSubject<boolean>(false);
|
||||
private readonly _tabIndexSrc = new BehaviorSubject<number | undefined>(undefined);
|
||||
private readonly _baseRowDataSrc = new BehaviorSubject<AddonViewModel[]>([]);
|
||||
private readonly _filterInputSrc = new BehaviorSubject<string>("");
|
||||
private readonly _spinnerMessageSrc = new BehaviorSubject<string>("");
|
||||
|
||||
public readonly enableControls$ = this._enableControlsSrc.asObservable();
|
||||
public readonly spinnerMessage$ = this._spinnerMessageSrc.asObservable();
|
||||
|
||||
public readonly selectedWowInstallationId$ = this._sessionService.selectedWowInstallation$.pipe(
|
||||
map((wowInstall) => wowInstall?.id)
|
||||
);
|
||||
|
||||
public readonly hasSelectedWowInstallationId$ = this._sessionService.selectedWowInstallation$.pipe(
|
||||
map((wowInstall) => wowInstall !== undefined)
|
||||
);
|
||||
|
||||
public readonly isBusy$ = this._isBusySrc.asObservable();
|
||||
public readonly filterInput$ = this._filterInputSrc.asObservable();
|
||||
|
||||
private _subscriptions: Subscription[] = [];
|
||||
private isSelectedTab = false;
|
||||
private _lazyLoaded = false;
|
||||
private _isRefreshing = false;
|
||||
private _baseRowData: AddonViewModel[] = [];
|
||||
private _lastSelectionState: RowNode[] = [];
|
||||
public readonly rowData$ = combineLatest([this._baseRowDataSrc, this._filterInputSrc]).pipe(
|
||||
map(([rowData, filterVal]) => {
|
||||
return this.filterAddons(rowData, filterVal);
|
||||
})
|
||||
);
|
||||
|
||||
public readonly hasData$ = this.rowData$.pipe(map((data) => data.length > 0));
|
||||
public readonly enableUpdateAll$ = combineLatest([this.enableControls$, this.rowData$]).pipe(
|
||||
map(([enableControls, rowData]) => {
|
||||
return enableControls && rowData.some((row) => AddonUtils.needsUpdate(row.addon));
|
||||
})
|
||||
);
|
||||
|
||||
public readonly enableUpdateExtra$ = combineLatest([
|
||||
this.enableControls$,
|
||||
this.addonService.anyUpdatesAvailable$,
|
||||
]).pipe(
|
||||
map(([enableControls, updatesAvailable]) => {
|
||||
return enableControls && updatesAvailable;
|
||||
})
|
||||
);
|
||||
|
||||
public readonly hideGrid$ = combineLatest([this.isBusy$, this.hasData$]).pipe(
|
||||
map(([isBusy, hasData]) => {
|
||||
return isBusy || !hasData;
|
||||
})
|
||||
);
|
||||
|
||||
public readonly showNoAddons$ = combineLatest([this.isBusy$, this.hasData$]).pipe(
|
||||
map(([isBusy, hasData]) => {
|
||||
return !isBusy && !hasData;
|
||||
})
|
||||
);
|
||||
|
||||
public readonly isSelectedTab$ = combineLatest([this._sessionService.selectedHomeTab$, this._tabIndexSrc]).pipe(
|
||||
map(([selectedTab, ownTab]) => {
|
||||
return selectedTab !== undefined && ownTab !== undefined && selectedTab === ownTab;
|
||||
})
|
||||
);
|
||||
|
||||
public readonly operationError$ = this._operationErrorSrc.asObservable();
|
||||
|
||||
private _subscriptions: Subscription[] = [];
|
||||
private _lazyLoaded = false;
|
||||
private _isRefreshing = false;
|
||||
private _lastSelectionState: RowNode[] = [];
|
||||
|
||||
public updateAllContextMenu!: MatMenuTrigger;
|
||||
public spinnerMessage = "";
|
||||
public contextMenuPosition = { x: "0px", y: "0px" };
|
||||
public filter = "";
|
||||
public overlayNoRowsTemplate = "";
|
||||
public addonUtils = AddonUtils;
|
||||
public selectedClient = WowClientType.None;
|
||||
public selectedInstallation: WowInstallation | undefined = undefined;
|
||||
public wowClientType = WowClientType;
|
||||
public overlayRef: OverlayRef | null = null;
|
||||
public enableControls = true;
|
||||
|
||||
public wowInstallations$: Observable<WowInstallation[]>;
|
||||
public selectedInstallationId!: string;
|
||||
public rowData: AddonViewModel[] = [];
|
||||
public filterInput$ = new Subject<string>();
|
||||
public rowDataChange$ = new Subject<boolean>();
|
||||
|
||||
// Grid
|
||||
public rowDataG: any[] = [];
|
||||
public columnDefs: ColDef[] = [];
|
||||
public frameworkComponents = {};
|
||||
public gridApi!: GridApi;
|
||||
@@ -165,14 +224,6 @@ export class MyAddonsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
return this.columns.filter((col) => col.visible).map((col) => col.name);
|
||||
}
|
||||
|
||||
public get enableUpdateAll(): boolean {
|
||||
return _.some(this._baseRowData, (row) => AddonUtils.needsUpdate(row.addon));
|
||||
}
|
||||
|
||||
public get hasData(): boolean {
|
||||
return this._baseRowData.length > 0;
|
||||
}
|
||||
|
||||
public constructor(
|
||||
private _sessionService: SessionService,
|
||||
private _dialog: MatDialog,
|
||||
@@ -196,11 +247,6 @@ export class MyAddonsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
|
||||
this.wowInstallations$ = warcraftInstallationService.wowInstallations$;
|
||||
|
||||
// When the search input changes debounce it a little before searching
|
||||
const filterInputSub = this.filterInput$.pipe(debounceTime(200)).subscribe(() => {
|
||||
this.filterAddons();
|
||||
});
|
||||
|
||||
const addonInstalledSub = this.addonService.addonInstalled$
|
||||
.pipe(
|
||||
map((evt) => this.onAddonInstalledEvent(evt)),
|
||||
@@ -228,13 +274,17 @@ export class MyAddonsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
.subscribe();
|
||||
|
||||
this._subscriptions.push(
|
||||
this._sessionService.selectedHomeTab$.subscribe(this.onSelectedTabChange),
|
||||
this.isSelectedTab$
|
||||
.pipe(
|
||||
filter((isSelected) => isSelected === true),
|
||||
switchMap(this.onSelectedTabChange)
|
||||
)
|
||||
.subscribe(),
|
||||
this._sessionService.addonsChanged$.pipe(switchMap(() => from(this.onRefresh()))).subscribe(),
|
||||
this._sessionService.targetFileInstallComplete$.pipe(switchMap(() => from(this.onRefresh()))).subscribe(),
|
||||
pushUpdateSub,
|
||||
addonInstalledSub,
|
||||
addonRemovedSub,
|
||||
filterInputSub
|
||||
addonRemovedSub
|
||||
);
|
||||
|
||||
this.frameworkComponents = {
|
||||
@@ -273,14 +323,17 @@ export class MyAddonsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
columnDef.hide = !col.visible;
|
||||
}
|
||||
});
|
||||
|
||||
this.onSelectedTabChange(this._sessionService.getSelectedHomeTab());
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
this._subscriptions.forEach((sub) => sub.unsubscribe());
|
||||
}
|
||||
|
||||
public onClickClearFilter(): void {
|
||||
this.addonFilter.nativeElement.value = "";
|
||||
this._filterInputSrc.next("");
|
||||
}
|
||||
|
||||
public handleKeyboardEvent(event: KeyboardEvent): void {
|
||||
if (this.selectAllRows(event)) {
|
||||
return;
|
||||
@@ -299,10 +352,6 @@ export class MyAddonsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
this.wowUpService.setMyAddonsSortOrder(minimalState);
|
||||
}
|
||||
|
||||
public onRowDataChanged(): void {
|
||||
this.rowDataChange$.next(true);
|
||||
}
|
||||
|
||||
public onFirstDataRendered(): void {
|
||||
this.autoSizeColumns();
|
||||
}
|
||||
@@ -324,43 +373,60 @@ export class MyAddonsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
|
||||
this.loadSortOrder();
|
||||
|
||||
this.rowDataChange$.pipe(debounceTime(500)).subscribe(() => {
|
||||
this.redrawRows();
|
||||
});
|
||||
this.rowData$
|
||||
.pipe(
|
||||
tap((data) => {
|
||||
this.gridApi.setRowData(data);
|
||||
this.setPageContextText();
|
||||
})
|
||||
)
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
public ngAfterViewInit(): void {
|
||||
this._sessionService.myAddonsCompactVersion = !this.getLatestVersionColumnVisible();
|
||||
|
||||
if (this.addonFilter?.nativeElement !== undefined) {
|
||||
const addonFilterSub = fromEvent(this.addonFilter.nativeElement, "keyup")
|
||||
.pipe(
|
||||
filter(Boolean),
|
||||
debounceTime(200),
|
||||
distinctUntilChanged(),
|
||||
tap(() => {
|
||||
console.debug(this.addonFilter.nativeElement.value);
|
||||
this._filterInputSrc.next(this.addonFilter.nativeElement.value);
|
||||
})
|
||||
)
|
||||
.subscribe();
|
||||
|
||||
this._subscriptions.push(addonFilterSub);
|
||||
}
|
||||
|
||||
this._sessionService.autoUpdateComplete$
|
||||
.pipe(
|
||||
tap(() => console.log("Checking for addon updates...")),
|
||||
switchMap(() => from(this.loadAddons(this.selectedInstallation)))
|
||||
switchMap(() => from(this.loadAddons()))
|
||||
)
|
||||
.subscribe(() => {
|
||||
this._cdRef.markForCheck();
|
||||
});
|
||||
}
|
||||
|
||||
public onSelectedTabChange = (tabIndex: number): void => {
|
||||
this.isSelectedTab = tabIndex === this.tabIndex;
|
||||
if (!this.isSelectedTab) {
|
||||
return;
|
||||
}
|
||||
|
||||
public onSelectedTabChange = (): Observable<void> => {
|
||||
this.setPageContextText();
|
||||
|
||||
from(this.lazyLoad())
|
||||
.pipe(
|
||||
first(),
|
||||
// delay(400),
|
||||
// map(() => {
|
||||
// this.redrawRows();
|
||||
// }),
|
||||
catchError((e) => {
|
||||
console.error(e);
|
||||
return of(undefined);
|
||||
})
|
||||
)
|
||||
.subscribe();
|
||||
return from(this.lazyLoad()).pipe(
|
||||
first(),
|
||||
tap(() => console.debug("TAP IT")),
|
||||
// delay(400),
|
||||
// map(() => {
|
||||
// this.redrawRows();
|
||||
// }),
|
||||
catchError((e) => {
|
||||
console.error(e);
|
||||
return of(undefined);
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
// Get the translated value of the provider name (unknown)
|
||||
@@ -382,24 +448,26 @@ export class MyAddonsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
|
||||
this._isRefreshing = true;
|
||||
this._isBusySrc.next(true);
|
||||
this.enableControls = false;
|
||||
this._enableControlsSrc.next(false);
|
||||
|
||||
try {
|
||||
console.debug("onRefresh");
|
||||
await this.addonService.syncAllClients();
|
||||
|
||||
if (this.selectedInstallation) {
|
||||
await this._wowUpAddonService.updateForInstallation(this.selectedInstallation);
|
||||
const selectedWowInstall = this._sessionService.getSelectedWowInstallation();
|
||||
if (selectedWowInstall !== undefined) {
|
||||
await this._wowUpAddonService.updateForInstallation(selectedWowInstall);
|
||||
}
|
||||
|
||||
await this.loadAddons(this.selectedInstallation);
|
||||
await this.loadAddons();
|
||||
await this.updateBadgeCount();
|
||||
} catch (e) {
|
||||
console.error(`Failed to refresh addons`, e);
|
||||
} finally {
|
||||
this._isBusySrc.next(false);
|
||||
|
||||
this.enableControls = true;
|
||||
this._enableControlsSrc.next(true);
|
||||
|
||||
this._isRefreshing = false;
|
||||
}
|
||||
};
|
||||
@@ -433,37 +501,25 @@ export class MyAddonsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
return listItem.addon.warningType === undefined && this.addonService.canReinstall(listItem.addon);
|
||||
}
|
||||
|
||||
/** Handle when the user enters new text into the filter box */
|
||||
public filterAddons(): void {
|
||||
if (this.filter.length === 0) {
|
||||
this.rowData = this._baseRowData;
|
||||
this._cdRef.detectChanges();
|
||||
return;
|
||||
public filterAddons(rowData: AddonViewModel[], filterVal: string): AddonViewModel[] {
|
||||
if (filterVal.length === 0) {
|
||||
return rowData;
|
||||
}
|
||||
|
||||
const filter = this.filter.trim().toLowerCase();
|
||||
const filtered = _.filter(this._baseRowData, (row) => this.filterListItem(row, filter));
|
||||
|
||||
this.rowData = filtered;
|
||||
|
||||
this._cdRef.detectChanges();
|
||||
}
|
||||
|
||||
// Handle when the user clicks the clear button on the filter input box
|
||||
public onClearFilter(): void {
|
||||
this.filter = "";
|
||||
this.filterInput$.next(this.filter);
|
||||
const filter = filterVal.trim().toLowerCase();
|
||||
return rowData.filter((row) => this.filterListItem(row, filter));
|
||||
}
|
||||
|
||||
// Handle when the user clicks the update all button
|
||||
public async onUpdateAll(): Promise<void> {
|
||||
if (!this.selectedInstallation) {
|
||||
const selectedWowInstall = this._sessionService.getSelectedWowInstallation();
|
||||
if (selectedWowInstall === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.enableControls = false;
|
||||
this._enableControlsSrc.next(false);
|
||||
|
||||
const addons = await this.addonService.getAddons(this.selectedInstallation, false);
|
||||
const addons = await this.addonService.getAddons(selectedWowInstall, false);
|
||||
try {
|
||||
const filteredAddons = _.filter(addons, (addon) => AddonUtils.needsUpdate(addon));
|
||||
|
||||
@@ -480,7 +536,7 @@ export class MyAddonsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
this.enableControls = this.calculateControlState();
|
||||
this._enableControlsSrc.next(this.calculateControlState());
|
||||
}
|
||||
|
||||
// Handle when the user clicks the update all retail/classic button
|
||||
@@ -542,6 +598,7 @@ export class MyAddonsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
|
||||
public async onReInstallAddons(listItems: AddonViewModel[]): Promise<void> {
|
||||
try {
|
||||
console.debug("onReInstallAddons", listItems);
|
||||
const tasks = _.map(listItems, (listItem) => this.addonService.installAddon(listItem.addon?.id ?? ""));
|
||||
await Promise.all(tasks);
|
||||
} catch (e) {
|
||||
@@ -573,9 +630,7 @@ export class MyAddonsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
this.gridColumnApi.setColumnVisible(column.name, event.checked);
|
||||
|
||||
if (column.name === "latestVersion") {
|
||||
const updates = [...this._baseRowData];
|
||||
updates.forEach((update) => (update.showUpdate = !event.checked));
|
||||
this.rowData = updates;
|
||||
this._sessionService.myAddonsCompactVersion = !event.checked;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -598,14 +653,14 @@ export class MyAddonsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
}
|
||||
|
||||
console.log("Performing re-scan");
|
||||
return from(this.loadAddons(this.selectedInstallation, true)).pipe(switchMap(() => from(this.onRefresh())));
|
||||
return from(this.loadAddons(true)).pipe(switchMap(() => from(this.onRefresh())));
|
||||
})
|
||||
)
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
public onClientChange(): void {
|
||||
this._sessionService.setSelectedWowInstallation(this.selectedInstallationId);
|
||||
public onClientChange(evt: any): void {
|
||||
this._sessionService.setSelectedWowInstallation(evt.value);
|
||||
}
|
||||
|
||||
public onRemoveAddon(addon: Addon): void {
|
||||
@@ -640,7 +695,7 @@ export class MyAddonsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
},
|
||||
});
|
||||
}),
|
||||
switchMap(() => from(this.loadAddons(this.selectedInstallation)))
|
||||
switchMap(() => from(this.loadAddons()))
|
||||
);
|
||||
}
|
||||
})
|
||||
@@ -722,7 +777,7 @@ export class MyAddonsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
|
||||
public onClickIgnoreAddons(listItems: AddonViewModel[]): void {
|
||||
const isIgnored = _.every(listItems, (listItem) => listItem.addon?.isIgnored === false);
|
||||
const rows = [...this._baseRowData];
|
||||
const rows = _.cloneDeep(this._baseRowDataSrc.value);
|
||||
try {
|
||||
for (const listItem of listItems) {
|
||||
const row = _.find(rows, (r) => r.addon?.id === listItem.addon?.id);
|
||||
@@ -739,7 +794,7 @@ export class MyAddonsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
this.addonService.saveAddon(row.addon);
|
||||
}
|
||||
|
||||
this.rowData = rows;
|
||||
this._baseRowDataSrc.next(rows);
|
||||
} catch (e) {
|
||||
console.error(`Failed to ignore addon(s)`, e);
|
||||
} finally {
|
||||
@@ -771,12 +826,16 @@ export class MyAddonsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
evt.node.setSelected(true);
|
||||
}
|
||||
|
||||
private getModelById(rows: AddonViewModel[], model: AddonViewModel): AddonViewModel | undefined {
|
||||
return rows.find((row) => row.addon?.id == model.addon?.id);
|
||||
}
|
||||
|
||||
public onClickAutoUpdateAddons(listItems: AddonViewModel[]): void {
|
||||
const isAutoUpdate = _.every(listItems, (listItem) => listItem.addon?.autoUpdateEnabled === false);
|
||||
const rows = [...this._baseRowData];
|
||||
const rows = _.cloneDeep(this._baseRowDataSrc.value);
|
||||
try {
|
||||
for (const listItem of listItems) {
|
||||
const row = _.find(rows, (r) => r.addon?.id === listItem.addon?.id);
|
||||
const row = this.getModelById(rows, listItem);
|
||||
if (!row || !row.addon) {
|
||||
console.warn("Invalid row data");
|
||||
continue;
|
||||
@@ -790,7 +849,7 @@ export class MyAddonsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
this.addonService.saveAddon(row.addon);
|
||||
}
|
||||
|
||||
this.rowData = rows;
|
||||
this._baseRowDataSrc.next(rows);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
this._operationErrorSrc.next(e);
|
||||
@@ -822,18 +881,14 @@ export class MyAddonsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
return of(undefined);
|
||||
}
|
||||
|
||||
const selectedWowInstall = this._sessionService.getSelectedWowInstallation();
|
||||
const externalId = _.find(listItem.addon?.externalIds, (extId) => extId.providerName === evt.value);
|
||||
if (!externalId || !this.selectedInstallation) {
|
||||
if (!externalId || !selectedWowInstall) {
|
||||
throw new Error("External id not found");
|
||||
}
|
||||
|
||||
return from(
|
||||
this.addonService.setProvider(
|
||||
listItem.addon,
|
||||
externalId.id,
|
||||
externalId.providerName,
|
||||
this.selectedInstallation
|
||||
)
|
||||
this.addonService.setProvider(listItem.addon, externalId.id, externalId.providerName, selectedWowInstall)
|
||||
);
|
||||
}),
|
||||
catchError((e) => {
|
||||
@@ -909,7 +964,7 @@ export class MyAddonsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
this._lazyLoaded = true;
|
||||
this._isBusySrc.next(true);
|
||||
|
||||
this.enableControls = false;
|
||||
this._enableControlsSrc.next(false);
|
||||
|
||||
// TODO this shouldn't be here
|
||||
await this.addonService.backfillAddons();
|
||||
@@ -923,9 +978,7 @@ export class MyAddonsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
return of(undefined);
|
||||
}
|
||||
|
||||
this.selectedInstallation = installation;
|
||||
this.selectedInstallationId = installation.id;
|
||||
return from(this.loadAddons(this.selectedInstallation));
|
||||
return from(this.loadAddons());
|
||||
}),
|
||||
catchError((e) => {
|
||||
console.error(`selectedInstallationSub failed`, e);
|
||||
@@ -940,8 +993,8 @@ export class MyAddonsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
private async updateAllWithSpinner(...installations: WowInstallation[]): Promise<void> {
|
||||
this._isBusySrc.next(true);
|
||||
|
||||
this.spinnerMessage = this._translateService.instant("PAGES.MY_ADDONS.SPINNER.GATHERING_ADDONS");
|
||||
this.enableControls = false;
|
||||
this._spinnerMessageSrc.next(this._translateService.instant("PAGES.MY_ADDONS.SPINNER.GATHERING_ADDONS"));
|
||||
this._enableControlsSrc.next(false);
|
||||
|
||||
let addons: Addon[] = [];
|
||||
let updatedCt = 0;
|
||||
@@ -957,14 +1010,16 @@ export class MyAddonsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
);
|
||||
|
||||
if (addons.length === 0) {
|
||||
await this.loadAddons(this.selectedInstallation);
|
||||
await this.loadAddons();
|
||||
return;
|
||||
}
|
||||
|
||||
this.spinnerMessage = this._translateService.instant("PAGES.MY_ADDONS.SPINNER.UPDATING", {
|
||||
updateCount: updatedCt,
|
||||
addonCount: addons.length,
|
||||
});
|
||||
this._spinnerMessageSrc.next(
|
||||
this._translateService.instant("PAGES.MY_ADDONS.SPINNER.UPDATING", {
|
||||
updateCount: updatedCt,
|
||||
addonCount: addons.length,
|
||||
})
|
||||
);
|
||||
|
||||
for (const addon of addons) {
|
||||
if (!addon.id) {
|
||||
@@ -980,25 +1035,27 @@ export class MyAddonsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.spinnerMessage = this._translateService.instant("PAGES.MY_ADDONS.SPINNER.UPDATING_WITH_ADDON_NAME", {
|
||||
updateCount: updatedCt,
|
||||
addonCount: addons.length,
|
||||
clientType: installation.label,
|
||||
addonName: addon.name,
|
||||
});
|
||||
this._spinnerMessageSrc.next(
|
||||
this._translateService.instant("PAGES.MY_ADDONS.SPINNER.UPDATING_WITH_ADDON_NAME", {
|
||||
updateCount: updatedCt,
|
||||
addonCount: addons.length,
|
||||
clientType: installation.label,
|
||||
addonName: addon.name,
|
||||
})
|
||||
);
|
||||
|
||||
await this.addonService.updateAddon(addon.id);
|
||||
}
|
||||
|
||||
await this.loadAddons(this.selectedInstallation);
|
||||
await this.loadAddons();
|
||||
} catch (err) {
|
||||
console.error("Failed to update classic/retail", err);
|
||||
this._isBusySrc.next(false);
|
||||
|
||||
this._cdRef.detectChanges();
|
||||
} finally {
|
||||
this.spinnerMessage = "";
|
||||
this.enableControls = this.calculateControlState();
|
||||
this._spinnerMessageSrc.next("");
|
||||
this._enableControlsSrc.next(this.calculateControlState());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1007,21 +1064,21 @@ export class MyAddonsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
this.contextMenuPosition.y = `${event.clientY as number}px`;
|
||||
}
|
||||
|
||||
private loadAddons = async (installation: WowInstallation | undefined, reScan = false): Promise<void> => {
|
||||
private loadAddons = async (reScan = false): Promise<void> => {
|
||||
const installation = this._sessionService.getSelectedWowInstallation();
|
||||
if (!installation) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._isBusySrc.next(true);
|
||||
|
||||
this.enableControls = false;
|
||||
this._enableControlsSrc.next(false);
|
||||
|
||||
if (!installation) {
|
||||
console.warn("Skipping addon load installation unknown");
|
||||
return;
|
||||
}
|
||||
|
||||
this.rowData = this._baseRowData = [];
|
||||
this._cdRef.detectChanges();
|
||||
|
||||
try {
|
||||
@@ -1032,25 +1089,26 @@ export class MyAddonsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
}
|
||||
|
||||
const rowData = this.formatAddons(addons);
|
||||
this.enableControls = this.calculateControlState();
|
||||
|
||||
this._baseRowData = rowData;
|
||||
this.rowData = this._baseRowData;
|
||||
this._baseRowDataSrc.next(rowData);
|
||||
|
||||
this.setPageContextText();
|
||||
|
||||
this._cdRef.detectChanges();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
this.enableControls = this.calculateControlState();
|
||||
} finally {
|
||||
this._isBusySrc.next(false);
|
||||
this._cdRef.detectChanges();
|
||||
this._enableControlsSrc.next(this.calculateControlState());
|
||||
}
|
||||
};
|
||||
|
||||
private getLatestVersionColumnVisible(): boolean {
|
||||
return this.columns.find((col) => col.name === "latestVersion")?.visible ?? true;
|
||||
}
|
||||
|
||||
private formatAddons(addons: Addon[]): AddonViewModel[] {
|
||||
const showUpdate = !(this.columns.find((col) => col.name === "latestVersion")?.visible ?? true);
|
||||
const viewModels = addons.map((addon) => {
|
||||
const listItem = new AddonViewModel(addon);
|
||||
|
||||
@@ -1058,7 +1116,6 @@ export class MyAddonsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
listItem.addon.installedVersion = "";
|
||||
}
|
||||
|
||||
listItem.showUpdate = showUpdate;
|
||||
return listItem;
|
||||
});
|
||||
|
||||
@@ -1077,54 +1134,58 @@ export class MyAddonsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
};
|
||||
|
||||
private setPageContextText() {
|
||||
const itemsLength = this.rowData.length;
|
||||
if (itemsLength === 0) {
|
||||
return;
|
||||
}
|
||||
this.rowData$
|
||||
.pipe(
|
||||
first(),
|
||||
map((data) => {
|
||||
if (data.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._sessionService.setContextText(
|
||||
this.tabIndex,
|
||||
this._translateService.instant("PAGES.MY_ADDONS.PAGE_CONTEXT_FOOTER.ADDONS_INSTALLED", {
|
||||
count: itemsLength,
|
||||
})
|
||||
);
|
||||
this._sessionService.setContextText(
|
||||
this._tabIndexSrc.value,
|
||||
this._translateService.instant("PAGES.MY_ADDONS.PAGE_CONTEXT_FOOTER.ADDONS_INSTALLED", {
|
||||
count: data.length,
|
||||
})
|
||||
);
|
||||
})
|
||||
)
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
private onAddonInstalledEvent = (evt: AddonUpdateEvent) => {
|
||||
try {
|
||||
if (evt.addon.installationId !== this.selectedInstallationId) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ([AddonInstallState.Complete, AddonInstallState.Error].includes(evt.installState) === false) {
|
||||
this.enableControls = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const idx = this._baseRowData.findIndex((r) => r.addon?.id === evt.addon.id);
|
||||
|
||||
// If we have a new addon, just put it at the end
|
||||
if (idx === -1) {
|
||||
this._baseRowData.push(new AddonViewModel(evt.addon));
|
||||
this._baseRowData = _.orderBy(this._baseRowData, (row) => row.addon?.name);
|
||||
} else {
|
||||
this._baseRowData.splice(idx, 1, new AddonViewModel(evt.addon));
|
||||
}
|
||||
|
||||
this.rowData = [...this._baseRowData];
|
||||
|
||||
this.enableControls = this.calculateControlState();
|
||||
} finally {
|
||||
this._cdRef.detectChanges();
|
||||
if (evt.addon.installationId !== this._sessionService.getSelectedWowInstallation()?.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ([AddonInstallState.Complete, AddonInstallState.Error].includes(evt.installState) === false) {
|
||||
this._enableControlsSrc.next(false);
|
||||
return;
|
||||
}
|
||||
|
||||
let rowData = _.cloneDeep(this._baseRowDataSrc.value);
|
||||
const idx = rowData.findIndex((r) => r.addon?.id === evt.addon.id);
|
||||
|
||||
// If we have a new addon, just put it at the end
|
||||
if (idx === -1) {
|
||||
console.debug("Adding new addon to list");
|
||||
rowData.push(new AddonViewModel(evt.addon));
|
||||
rowData = _.orderBy(rowData, (row) => row.canonicalName);
|
||||
} else {
|
||||
rowData.splice(idx, 1, new AddonViewModel(evt.addon));
|
||||
}
|
||||
|
||||
this._baseRowDataSrc.next(rowData);
|
||||
this._enableControlsSrc.next(this.calculateControlState());
|
||||
};
|
||||
|
||||
private onAddonRemoved = (addonId: string) => {
|
||||
const listItemIdx = this._baseRowData.findIndex((li) => li.addon?.id === addonId);
|
||||
this._baseRowData.splice(listItemIdx, 1);
|
||||
const rowData = _.cloneDeep(this._baseRowDataSrc.value);
|
||||
|
||||
this.rowData = [...this._baseRowData];
|
||||
this._cdRef.detectChanges();
|
||||
const listItemIdx = rowData.findIndex((li) => li.addon?.id === addonId);
|
||||
rowData.splice(listItemIdx, 1);
|
||||
|
||||
this._baseRowDataSrc.next(rowData);
|
||||
};
|
||||
|
||||
private showErrorMessage(title: string, message: string) {
|
||||
@@ -1170,7 +1231,7 @@ export class MyAddonsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
}
|
||||
|
||||
private redrawRows() {
|
||||
// this.gridApi?.redrawRows();
|
||||
this.gridApi?.redrawRows();
|
||||
// this.gridApi?.resetRowHeights();
|
||||
// this.autoSizeColumns();
|
||||
this._cdRef.detectChanges();
|
||||
@@ -1204,26 +1265,24 @@ export class MyAddonsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
|
||||
return [
|
||||
{
|
||||
field: "name",
|
||||
cellRenderer: "myAddonRenderer",
|
||||
field: "hash",
|
||||
flex: 2,
|
||||
minWidth: 300,
|
||||
headerName: this._translateService.instant("PAGES.MY_ADDONS.TABLE.ADDON_COLUMN_HEADER"),
|
||||
sortable: true,
|
||||
// autoHeight: true,
|
||||
cellRenderer: "myAddonRenderer",
|
||||
colId: "name",
|
||||
valueGetter: (params) => {
|
||||
return params.data.canonicalName;
|
||||
},
|
||||
comparator: (va, vb, na, nb) => this.compareElement(na, nb, "canonicalName"),
|
||||
...baseColumn,
|
||||
},
|
||||
{
|
||||
field: "sortOrder",
|
||||
width: 150,
|
||||
sortable: true,
|
||||
headerName: this._translateService.instant("PAGES.MY_ADDONS.TABLE.STATUS_COLUMN_HEADER"),
|
||||
cellRenderer: "myAddonStatus",
|
||||
comparator: (va, vb, na, nb) => this.compareElement(na, nb, "sortOrder"),
|
||||
field: "sortOrder",
|
||||
headerName: this._translateService.instant("PAGES.MY_ADDONS.TABLE.STATUS_COLUMN_HEADER"),
|
||||
sortable: true,
|
||||
width: 150,
|
||||
...baseColumn,
|
||||
},
|
||||
{
|
||||
|
||||
11
wowup-electron/src/app/pipes/inverse-bool.pipe.ts
Normal file
11
wowup-electron/src/app/pipes/inverse-bool.pipe.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Pipe, PipeTransform } from "@angular/core";
|
||||
import { getGameVersion } from "../utils/addon.utils";
|
||||
|
||||
@Pipe({
|
||||
name: "invertBool",
|
||||
})
|
||||
export class InvertBoolPipe implements PipeTransform {
|
||||
public transform(value: boolean): boolean {
|
||||
return !value;
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ export class SessionService {
|
||||
private readonly _addonsChangedSrc = new Subject<boolean>();
|
||||
private readonly _myAddonsColumnsSrc = new BehaviorSubject<ColumnState[]>([]);
|
||||
private readonly _targetFileInstallCompleteSrc = new Subject<boolean>();
|
||||
private readonly _myAddonsCompactVersionSrc = new BehaviorSubject<boolean>(false);
|
||||
|
||||
private readonly _getAddonsColumnsSrc = new Subject<ColumnState>();
|
||||
|
||||
@@ -42,9 +43,14 @@ export class SessionService {
|
||||
public readonly wowUpAuthToken$ = this._wowUpAccountService.wowUpAuthTokenSrc.asObservable();
|
||||
public readonly wowUpAccount$ = this._wowUpAccountService.wowUpAccountSrc.asObservable();
|
||||
public readonly wowUpAccountPushEnabled$ = this._wowUpAccountService.accountPushSrc.asObservable();
|
||||
public readonly myAddonsCompactVersion$ = this._myAddonsCompactVersionSrc.asObservable();
|
||||
|
||||
public readonly wowUpAuthenticated$ = this.wowUpAccount$.pipe(map((account) => account !== undefined));
|
||||
|
||||
public set myAddonsCompactVersion(val: boolean) {
|
||||
this._myAddonsCompactVersionSrc.next(val);
|
||||
}
|
||||
|
||||
public constructor(
|
||||
private _warcraftInstallationService: WarcraftInstallationService,
|
||||
private _preferenceStorageService: PreferenceStorageService,
|
||||
|
||||
@@ -41,9 +41,11 @@ const CHANGELOGS: ChangeLog[] = [
|
||||
<li>Spanish locale updates (SkollVargr)</li>
|
||||
<li>Chinese locale updates (CyanoHao)</li>
|
||||
<li>Tweak the tabs some more</li>
|
||||
<li>Performance improvements for My Addons page</li>
|
||||
<li>Images details tab is now Previews</li>
|
||||
<li>Use a different lightbox library for previews</li>
|
||||
<li>Expanded system proxy support</li>`,
|
||||
<li>Expanded system proxy support</li>
|
||||
</ul>`,
|
||||
},
|
||||
{
|
||||
Version: "2.4.4",
|
||||
|
||||
@@ -175,7 +175,7 @@
|
||||
}
|
||||
},
|
||||
"ERRORS": {
|
||||
"ACCOUNT_PUSH_TOGGLE_FAILED_ERROR": "Failed to toggle instant updates for your account. Please try again later, or reach out on Discord.",
|
||||
"ACCOUNT_PUSH_TOGGLE_FAILED_ERROR": "No se pudieron activar las actualizaciones instantáneas en su cuenta. Por favor, inténtelo de nuevo más tarde o contacte con nosotros en Discord.",
|
||||
"ADDON_INSTALL_ERROR": "Falló la instalación del addon {addonName}. Por favor, inténtelo de nuevo más tarde.",
|
||||
"ADDON_SCAN_ERROR": "Ocurrió un error al comparar sus carpetas de addons con {providerName}. Por favor, inténtelo de nuevo más tarde.",
|
||||
"ADDON_SYNC_ERROR": "Ocurrió un error al comprobar actualizaciones desde: {providerName}",
|
||||
@@ -277,12 +277,12 @@
|
||||
},
|
||||
"ACCOUNT": {
|
||||
"BETA": "Beta",
|
||||
"LOGIN_BUTTON": "Login Now!",
|
||||
"LOGOUT_BUTTON": "Logout",
|
||||
"LOGOUT_CONFIRMATION_MESSAGE": "Are you sure you want to log out? All of your local account data will be removed, until you login again.",
|
||||
"LOGOUT_CONFIRMATION_TITLE": "Logout?",
|
||||
"MANAGE_ACCOUNT_BUTTON": "Manage Account",
|
||||
"TITLE": "Account"
|
||||
"LOGIN_BUTTON": "¡Iniciar sesión ahora!",
|
||||
"LOGOUT_BUTTON": "Cerrar sesión",
|
||||
"LOGOUT_CONFIRMATION_MESSAGE": "¿Quiere cerrar la sesión? Toda la información local sobre su cuenta será eliminada hasta que inicie sesión de nuevo.",
|
||||
"LOGOUT_CONFIRMATION_TITLE": "¿Cerrar sesión?",
|
||||
"MANAGE_ACCOUNT_BUTTON": "Administrar cuenta",
|
||||
"TITLE": "Cuenta"
|
||||
},
|
||||
"GET_ADDONS": {
|
||||
"ADDON_CATEGORIES_BUTTON": "Categorías",
|
||||
@@ -307,7 +307,7 @@
|
||||
},
|
||||
"HOME": {
|
||||
"ABOUT_TAB_TITLE": "Acerca de",
|
||||
"ACCOUNT_TAB_TITLE": "Account",
|
||||
"ACCOUNT_TAB_TITLE": "Cuenta",
|
||||
"GET_ADDONS_TAB_TITLE": "Obtener addons",
|
||||
"GUIDE_TAB_TITLE": "Guía",
|
||||
"MIGRATING_ADDONS": "Migrando lista de addons...",
|
||||
@@ -463,7 +463,7 @@
|
||||
"TITLE": "Depuración"
|
||||
},
|
||||
"TABS": {
|
||||
"ABOUT": "About",
|
||||
"ABOUT": "Acerca de",
|
||||
"ADDONS": "Addons",
|
||||
"APPLICATION": "Aplicación",
|
||||
"CLIENTS": "Clientes",
|
||||
|
||||
1
wowup-electron/src/common/wowup.d.ts
vendored
1
wowup-electron/src/common/wowup.d.ts
vendored
@@ -88,6 +88,7 @@ declare global {
|
||||
handlebars: any;
|
||||
autoLaunch: any;
|
||||
};
|
||||
baseBgColor: string;
|
||||
platform: string;
|
||||
userDataPath: string;
|
||||
logPath: string;
|
||||
|
||||
@@ -230,6 +230,7 @@ body {
|
||||
--background-secondary-4: #333333;
|
||||
--background-secondary-5: #222222;
|
||||
--control-color: #536dfe;
|
||||
--epic-color: #a335ee;
|
||||
--rare-color: #0070dd;
|
||||
--scrollbar-track-color: #333333;
|
||||
--text-1: #ffffff;
|
||||
|
||||
@@ -28,12 +28,19 @@
|
||||
</div>
|
||||
</app-root>
|
||||
<script>
|
||||
document.body.classList.add(window.platform)
|
||||
if (window.platform === 'darwin') {
|
||||
document.getElementById('preload-logo').style.borderRadius = '23px';
|
||||
}
|
||||
try {
|
||||
window.global = window;
|
||||
|
||||
document.body.classList.add(window.platform)
|
||||
if (window.platform === 'darwin') {
|
||||
document.getElementById('preload-logo').style.borderRadius = '23px';
|
||||
}
|
||||
|
||||
document.body.style.backgroundColor = window.baseBgColor;
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
@@ -2,7 +2,6 @@
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../out-tsc/spec",
|
||||
"module": "commonjs",
|
||||
"types": [
|
||||
"jasmine",
|
||||
"node"
|
||||
|
||||
Reference in New Issue
Block a user