Add the ability to switch providers where applicable

Fix issues with the context menu width?
Fix an issue with the updated at time being NaN
This commit is contained in:
jliddev
2020-11-17 13:16:43 -06:00
parent 172acd7e06
commit cced68608f
25 changed files with 356 additions and 145 deletions

View File

@@ -97,7 +97,7 @@ function createWindow(): BrowserWindow {
title: "WowUp",
titleBarStyle: "hidden",
webPreferences: {
preload: path.join(__dirname, "preload.js"),
// preload: path.join(__dirname, "preload.js"),
nodeIntegration: true,
allowRunningInsecureContent: argv.serve ? true : false,
webSecurity: false,

View File

@@ -1,80 +1,58 @@
<div class="addon-column row align-items-center">
<div class="thumbnail-container">
<div
*ngIf="listItem.hasThumbnail === true"
class="addon-logo-container bg-secondary-3"
[style.backgroundImage]="'url(' + listItem.addon.thumbnailUrl + ')'"
></div>
<div *ngIf="listItem.hasThumbnail === true" class="addon-logo-container bg-secondary-3"
[style.backgroundImage]="'url(' + listItem.addon.thumbnailUrl + ')'"></div>
<div *ngIf="listItem.hasThumbnail === false" class="addon-logo-container">
<div class="addon-logo-letter text-3">
{{ listItem.thumbnailLetter }}
</div>
</div>
<div
*ngIf="listItem.isBetaChannel || listItem.isAlphaChannel"
class="channel bg-secondary-3"
[ngClass]="{
<div *ngIf="listItem.isBetaChannel || listItem.isAlphaChannel" class="channel bg-secondary-3" [ngClass]="{
beta: listItem.isBetaChannel,
alpha: listItem.isAlphaChannel
}"
>
}">
{{ listItem.isAlphaChannel ? "Alpha" : "Beta" }}
</div>
</div>
<div>
<a class="addon-title hover-text-2 mat-subheading-2" (click)="viewDetails()" [ngClass]="{ 'text-3': listItem.isIgnored }">{{
<a class="addon-title hover-text-2 mat-subheading-2" (click)="viewDetails()"
[ngClass]="{ 'text-3': listItem.isIgnored }">{{
listItem.addon.name
}}</a>
<div class="addon-funding">
<a
*ngIf="listItem.addon.patreonFundingLink"
appExternalLink
[href]="listItem.addon.patreonFundingLink"
matTooltip="Support the author on Patreon"
>
<a *ngIf="listItem.addon.patreonFundingLink" appExternalLink [href]="listItem.addon.patreonFundingLink"
matTooltip="Support the author on Patreon">
<img class="funding-icon" src="assets/images/patreon_logo_small.png" />
</a>
<a
*ngIf="listItem.addon.githubFundingLink"
appExternalLink
[href]="listItem.addon.githubFundingLink"
matTooltip="Support the author on GitHub"
>
<a *ngIf="listItem.addon.githubFundingLink" appExternalLink [href]="listItem.addon.githubFundingLink"
matTooltip="Support the author on GitHub">
<img class="funding-icon" src="assets/images/github_logo_small.png" />
</a>
<a
*ngIf="listItem.addon.customFundingLink"
appExternalLink
[href]="listItem.addon.customFundingLink"
matTooltip="Support this author"
>
<a *ngIf="listItem.addon.customFundingLink" appExternalLink [href]="listItem.addon.customFundingLink"
matTooltip="Support this author">
<img class="funding-icon" src="assets/images/custom_funding_logo_small.png" />
</a>
</div>
<div class="addon-version text-2 row align-items-center" [ngClass]="{ ignored: listItem.isIgnored }">
<div *ngIf="this.listItem.isAutoUpdate === true" class="mr-2">
<mat-icon
class="auto-update-icon text-2"
[matTooltip]="'PAGES.MY_ADDONS.TABLE.AUTO_UPDATE_ICON_TOOLTIP' | translate"
svgIcon="far:clock"
>
<div *ngIf="addonUtils.hasMultipleProviders(listItem.addon)" class="mr-2">
<mat-icon class="auto-update-icon" svgIcon="fas:code-branch" [matTooltip]="'This addon has multiple providers'">
</mat-icon>
</div>
<div
*ngIf="hasRequiredDependencies()"
class="mr-2"
[matTooltip]="'COMMON.DEPENDENCY.TOOLTIP' | translate: dependencyTooltip"
>
<div *ngIf="this.listItem.isAutoUpdate === true" 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">
<mat-icon class="auto-update-icon" svgIcon="fas:link"></mat-icon>
</div>
{{ listItem.addon.installedVersion }}
<div
class="update-available row"
*ngIf="showUpdateToVersion && listItem.addon.latestVersion !== listItem.addon.installedVersion"
>
<div class="update-available row"
*ngIf="showUpdateToVersion && listItem.addon.latestVersion !== listItem.addon.installedVersion">
<mat-icon class="upgrade-icon" svgIcon="fas:play"></mat-icon>
<div>{{ listItem.addon.latestVersion }}</div>
</div>
</div>
</div>
</div>
</div>

View File

@@ -1,6 +1,7 @@
import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core";
import { AddonDependencyType } from "app/models/wowup/addon-dependency-type";
import { AddonViewModel } from "../../business-objects/my-addon-list-item";
import * as AddonUtils from "../../utils/addon.utils";
@Component({
selector: "app-my-addons-addon-cell",
@@ -13,6 +14,8 @@ export class MyAddonsAddonCellComponent implements OnInit {
@Output() onViewDetails: EventEmitter<AddonViewModel> = new EventEmitter();
public addonUtils = AddonUtils;
constructor() {}
ngOnInit(): void {}

View File

@@ -2,6 +2,11 @@ import { WowClientType } from "../models/warcraft/wow-client-type";
import { AddonChannelType } from "../models/wowup/addon-channel-type";
import { AddonDependency } from "../models/wowup/addon-dependency";
export interface AddonExternalId {
providerName: string;
id: string;
}
export interface Addon {
id: string;
name: string;
@@ -30,5 +35,6 @@ export interface Addon {
summary?: string;
screenshotUrls?: string[];
releasedAt?: Date;
externalIds?: AddonExternalId[];
dependencies?: AddonDependency[];
}

View File

@@ -12,4 +12,5 @@ export interface Toc {
wowInterfaceId?: string;
tukUiProjectId?: string;
tukUiProjectFolders?: string;
loadOnDemand?: string;
}

View File

@@ -30,19 +30,17 @@
<div class="button-container">
<button mat-flat-button color="primary" [matTooltip]="'PAGES.MY_ADDONS.UPDATE_ALL_BUTTON_TOOLTIP' | translate"
[disabled]="enableControls === false || enableUpdateAll === false" (click)="onUpdateAll()"
(contextmenu)="onUpdateAllContext($event)" appUserActionTracker category="MyAddons" action="UpdateAll">
(contextmenu)="onUpdateAllContext($event)">
{{ "PAGES.MY_ADDONS.UPDATE_ALL_BUTTON" | translate }}
</button>
<button mat-flat-button color="primary"
[matTooltip]="'PAGES.MY_ADDONS.CHECK_UPDATES_BUTTON_TOOLTIP' | translate"
[disabled]="enableControls === false" (click)="onRefresh()" appUserActionTracker category="MyAddons"
action="CheckUpdates">
[disabled]="enableControls === false" (click)="onRefresh()">
{{ "PAGES.MY_ADDONS.CHECK_UPDATES_BUTTON" | translate }}
</button>
<button mat-flat-button color="primary"
[matTooltip]="'PAGES.MY_ADDONS.RESCAN_FOLDERS_BUTTON_TOOLTIP' | translate"
[disabled]="enableControls === false" (click)="onReScan()" appUserActionTracker category="MyAddons"
action="ReScanFolders">
[disabled]="enableControls === false" (click)="onReScan()">
{{ "PAGES.MY_ADDONS.RESCAN_FOLDERS_BUTTON" | translate }}
</button>
</div>
@@ -135,7 +133,7 @@
{{ "PAGES.MY_ADDONS.TABLE.PROVIDER_COLUMN_HEADER" | translate }}
</th>
<td mat-cell *matCellDef="let element" class="cell-padding">
<div *ngIf="element.addon.providerName !== 'WowUp'">
<div class="row" *ngIf="element.addon.providerName !== 'WowUp'">
{{ element.addon.providerName }}
</div>
<div *ngIf="element.addon.providerName === 'WowUp'" class="addon-provider">
@@ -194,49 +192,55 @@
</div>
</div>
<mat-divider></mat-divider>
<mat-checkbox class="mat-menu-item" [checked]="listItem.addon.isIgnored"
(change)="onClickIgnoreAddon($event, listItem)" appUserActionTracker category="MyAddons" action="IgnoreAddon"
[label]="listItem.addon.name">
{{ "PAGES.MY_ADDONS.ADDON_CONTEXT_MENU.IGNORE_ADDON_BUTTON" | translate }}
</mat-checkbox>
<mat-checkbox *ngIf="listItem.addon.isIgnored === false" class="mat-menu-item"
[checked]="listItem.addon.autoUpdateEnabled" (change)="onClickAutoUpdateAddon($event, listItem)"
appUserActionTracker category="MyAddons" action="AutoUpdateAddon" [label]="listItem.addon.name">
{{ "PAGES.MY_ADDONS.ADDON_CONTEXT_MENU.AUTO_UPDATE_ADDON_BUTTON" | translate }}
</mat-checkbox>
<div class="mat-menu-item">
<mat-checkbox [checked]="listItem.addon.isIgnored" (change)="onClickIgnoreAddon($event, listItem)">
{{ "PAGES.MY_ADDONS.ADDON_CONTEXT_MENU.IGNORE_ADDON_BUTTON" | translate }}
</mat-checkbox>
</div>
<div *ngIf="listItem.addon.isIgnored === false" class="mat-menu-item">
<mat-checkbox [checked]="listItem.addon.autoUpdateEnabled" (change)="onClickAutoUpdateAddon($event, listItem)">
{{ "PAGES.MY_ADDONS.ADDON_CONTEXT_MENU.AUTO_UPDATE_ADDON_BUTTON" | translate }}
</mat-checkbox>
</div>
<button mat-menu-item [matMenuTriggerFor]="addonChannels">
{{ "PAGES.MY_ADDONS.ADDON_CONTEXT_MENU.CHANNEL_SUBMENT_TITLE" | translate }}
{{ "PAGES.MY_ADDONS.ADDON_CONTEXT_MENU.CHANNEL_SUBMENU_TITLE" | translate }}
</button>
<button mat-menu-item (click)="onShowfolder(listItem.addon)" appUserActionTracker category="MyAddons"
action="ShowAddonFolder" [label]="listItem.addon.name">
<button *ngIf="addonUtils.hasMultipleProviders(listItem.addon)" mat-menu-item [matMenuTriggerFor]="addonProviders">
{{ "PAGES.MY_ADDONS.ADDON_CONTEXT_MENU.PROVIDER_SUBMENU_TITLE" | translate }}
</button>
<button mat-menu-item (click)="onShowfolder(listItem.addon)">
{{ "PAGES.MY_ADDONS.ADDON_CONTEXT_MENU.SHOW_FOLDER" | translate }}
</button>
<button mat-menu-item (click)="onReInstallAddon(listItem)" appUserActionTracker category="MyAddons"
action="ReInstallAddon" [label]="listItem.addon.name">
<button mat-menu-item (click)="onReInstallAddon(listItem)">
{{ "PAGES.MY_ADDONS.ADDON_CONTEXT_MENU.REINSTALL_ADDON_BUTTON" | translate }}
</button>
<mat-divider></mat-divider>
<button mat-menu-item (click)="onRemoveAddon(listItem.addon)" appUserActionTracker category="MyAddons"
action="RemoveAddon" [label]="listItem.addon.name">
<button mat-menu-item (click)="onRemoveAddon(listItem.addon)">
{{ "PAGES.MY_ADDONS.ADDON_CONTEXT_MENU.REMOVE_ADDON_BUTTON" | translate }}
</button>
<mat-menu #addonChannels="matMenu" class="addon-context-menu">
<mat-radio-group class="vertical-radio-group" [ngModel]="listItem.addon.channelType"
(change)="onSelectedAddonChannelChange($event, listItem)">
<mat-radio-button class="mat-menu-item" [value]="0" appUserActionTracker category="MyAddons"
action="SetStableAddonChannel" [label]="listItem.addon.name">
<mat-radio-button class="mat-menu-item" [value]="0">
{{ "PAGES.MY_ADDONS.ADDON_CONTEXT_MENU.STABLE_ADDON_CHANNEL" | translate }}
</mat-radio-button>
<mat-radio-button class="mat-menu-item" [value]="1" appUserActionTracker category="MyAddons"
action="SetBetaAddonChannel" [label]="listItem.addon.name">
<mat-radio-button class="mat-menu-item" [value]="1">
{{ "PAGES.MY_ADDONS.ADDON_CONTEXT_MENU.BETA_ADDON_CHANNEL" | translate }}
</mat-radio-button>
<mat-radio-button class="mat-menu-item" [value]="2" appUserActionTracker category="MyAddons"
action="SetAlphaAddonChannel" [label]="listItem.addon.name">
<mat-radio-button class="mat-menu-item" [value]="2">
{{ "PAGES.MY_ADDONS.ADDON_CONTEXT_MENU.ALPHA_ADDON_CHANNEL" | translate }}
</mat-radio-button>
</mat-radio-group>
</mat-menu>
<mat-menu #addonProviders="matMenu" class="addon-context-menu">
<mat-radio-group class="vertical-radio-group" [value]="listItem.addon.providerName"
(change)="onSelectedProviderChange($event, listItem)">
<mat-radio-button *ngFor="let provider of addonUtils.getAllProviders(listItem.addon)" class="mat-menu-item"
[value]="provider.providerName">
{{ provider.providerName }}
</mat-radio-button>
</mat-radio-group>
</mat-menu>
</ng-template>
</mat-menu>
@@ -258,7 +262,7 @@
{{ "PAGES.MY_ADDONS.ADDON_CONTEXT_MENU.AUTO_UPDATE_ADDON_BUTTON" | translate }}
</mat-checkbox>
<button mat-menu-item [matMenuTriggerFor]="addonChannels">
{{ "PAGES.MY_ADDONS.ADDON_CONTEXT_MENU.CHANNEL_SUBMENT_TITLE" | translate }}
{{ "PAGES.MY_ADDONS.ADDON_CONTEXT_MENU.CHANNEL_SUBMENU_TITLE" | translate }}
</button>
<button mat-menu-item (click)="onReInstallAddons(listItems)">
{{ "PAGES.MY_ADDONS.ADDON_CONTEXT_MENU.REINSTALL_ADDON_BUTTON" | translate }}
@@ -306,12 +310,10 @@
</div>
<mat-menu #updateAllContextMenu="matMenu" class="addon-context-menu">
<ng-template matMenuContent let-columns="columns">
<button mat-menu-item (click)="onUpdateAllRetailClassic()" appUserActionTracker category="MyAddons"
action="UpdateAllClassicRetail">
<button mat-menu-item (click)="onUpdateAllRetailClassic()">
{{ "PAGES.MY_ADDONS.UPDATE_ALL_CONTEXT_MENU.UPDATE_RETAIL_CLASSIC_BUTTON" | translate }}
</button>
<button mat-menu-item (click)="onUpdateAllClients()" appUserActionTracker category="MyAddons"
action="UpdateAllClients">
<button mat-menu-item (click)="onUpdateAllClients()">
{{ "PAGES.MY_ADDONS.UPDATE_ALL_CONTEXT_MENU.UPDATE_ALL_CLIENTS_BUTTON" | translate }}
</button>
</ng-template>

View File

@@ -111,6 +111,11 @@
}
}
.status-icon {
height: 11px;
width: 11px;
}
.addon-provider {
display: flex;
align-items: center;

View File

@@ -20,8 +20,8 @@ import { MatTableDataSource } from "@angular/material/table";
import { TranslateService } from "@ngx-translate/core";
import { AddonUpdateEvent } from "app/models/wowup/addon-update-event";
import * as _ from "lodash";
import { BehaviorSubject, from, Observable, Subscription } from "rxjs";
import { filter, map } from "rxjs/operators";
import { BehaviorSubject, from, Subscription } from "rxjs";
import { map } from "rxjs/operators";
import { AddonViewModel } from "../../business-objects/my-addon-list-item";
import { AddonDetailComponent, AddonDetailModel } from "../../components/addon-detail/addon-detail.component";
import { ConfirmDialogComponent } from "../../components/confirm-dialog/confirm-dialog.component";
@@ -29,7 +29,6 @@ import { Addon } from "../../entities/addon";
import { WowClientType } from "../../models/warcraft/wow-client-type";
import { AddonInstallState } from "../../models/wowup/addon-install-state";
import { ColumnState } from "../../models/wowup/column-state";
import { SelectItem } from "../../models/wowup/select-item";
import { ElectronService } from "../../services";
import { AddonService } from "../../services/addons/addon.service";
import { SessionService } from "../../services/session/session.service";
@@ -38,6 +37,8 @@ import { WowUpService } from "../../services/wowup/wowup.service";
import { getEnumName } from "../../utils/enum.utils";
import { stringIncludes } from "../../utils/string.utils";
import { WowUpAddonService } from "../../services/wowup/wowup-addon.service";
import * as AddonUtils from "../../utils/addon.utils";
import { AlertDialogComponent } from "../../components/alert-dialog/alert-dialog.component";
@Component({
selector: "app-my-addons",
@@ -71,6 +72,7 @@ export class MyAddonsComponent implements OnInit, OnDestroy, AfterViewInit {
public enableUpdateAll = false;
public activeSort = "sortOrder";
public activeSortDirection = "asc";
public addonUtils = AddonUtils;
public columns: ColumnState[] = [
{
@@ -542,6 +544,39 @@ export class MyAddonsComponent implements OnInit, OnDestroy, AfterViewInit {
}
}
public onSelectedProviderChange(evt: MatRadioChange, listItem: AddonViewModel) {
const messageData = {
addonName: listItem.addon.name,
providerName: evt.value,
};
const dialogRef = this._dialog.open(ConfirmDialogComponent, {
data: {
title: this._translateService.instant("PAGES.MY_ADDONS.CHANGE_ADDON_PROVIDER_CONFIRMATION.TITLE"),
message: this._translateService.instant(
"PAGES.MY_ADDONS.CHANGE_ADDON_PROVIDER_CONFIRMATION.MESSAGE",
messageData
),
},
});
dialogRef.afterClosed().subscribe(async (result) => {
if (!result) {
return;
}
try {
const externalId = _.find(listItem.addon.externalIds, (extid) => extid.providerName === evt.value);
this.addonService.setProvider(listItem.addon, externalId.id, externalId.providerName, this.selectedClient);
} catch (e) {
console.error(e);
const errorTitle = this._translateService.instant("DIALOGS.ALERT.ERROR_TITLE");
const errorMessage = this._translateService.instant("COMMON.ERRORS.CHANGE_PROVIDER_ERROR", messageData);
this.showErrorMessage(errorTitle, errorMessage);
}
});
}
public onSelectedAddonChannelChange(evt: MatRadioChange, listItem: AddonViewModel) {
this.onSelectedAddonsChannelChange(evt, [listItem]);
}
@@ -566,14 +601,19 @@ export class MyAddonsComponent implements OnInit, OnDestroy, AfterViewInit {
return `COMMON.ENUM.ADDON_CHANNEL_TYPE.${channelType.toUpperCase()}`;
}
private lazyLoad() {
private async lazyLoad() {
if (this._lazyLoaded) {
return;
}
this._lazyLoaded = true;
this.isBusy = true;
this.enableControls = false;
console.debug("LAZY LOAD");
await this.addonService.backfillAddons();
const selectedClientSubscription = this._sessionService.selectedClientType$
.pipe(
map((clientType) => {
@@ -789,4 +829,15 @@ export class MyAddonsComponent implements OnInit, OnDestroy, AfterViewInit {
this.enableUpdateAll = this.sortedListItems.some((li) => !li.isIgnored && (li.needsInstall || li.needsUpdate));
this.setPageContextText();
};
private showErrorMessage(title: string, message: string) {
const dialogRef = this._dialog.open(AlertDialogComponent, {
minWidth: 250,
data: {
title,
message,
},
});
dialogRef.afterClosed().subscribe();
}
}

View File

@@ -19,6 +19,10 @@ export class RelativeDurationPipe implements PipeTransform {
return "";
}
if (isNaN(then.getTime())) {
return "";
}
const now = new Date();
const seconds = Math.round((now.getTime() - then.getTime()) / 1000);
const minutes = Math.round(seconds / 60);

View File

@@ -19,7 +19,6 @@ export class AddonProviderFactory {
private _cachingService: CachingService,
private _electronService: ElectronService,
private _httpClient: HttpClient,
private _sessionService: SessionService,
private _fileService: FileService
) {}

View File

@@ -2,6 +2,8 @@ import { Injectable } from "@angular/core";
import { AddonDependency } from "app/models/wowup/addon-dependency";
import { AddonDependencyType } from "app/models/wowup/addon-dependency-type";
import { AddonSearchResultDependency } from "app/models/wowup/addon-search-result-dependency";
import { Toc } from "app/models/wowup/toc";
import { ADDON_PROVIDER_CURSEFORGE, ADDON_PROVIDER_TUKUI, ADDON_PROVIDER_WOWINTERFACE } from "common/constants";
import * as fs from "fs";
import * as _ from "lodash";
import * as path from "path";
@@ -11,7 +13,7 @@ import * as slug from "slug";
import { v4 as uuidv4 } from "uuid";
import { AddonProvider } from "../../addon-providers/addon-provider";
import { CurseAddonProvider } from "../../addon-providers/curse-addon-provider";
import { Addon } from "../../entities/addon";
import { Addon, AddonExternalId } from "../../entities/addon";
import { WowClientType } from "../../models/warcraft/wow-client-type";
import { AddonChannelType } from "../../models/wowup/addon-channel-type";
import { AddonFolder } from "../../models/wowup/addon-folder";
@@ -19,7 +21,7 @@ import { AddonInstallState } from "../../models/wowup/addon-install-state";
import { AddonSearchResult } from "../../models/wowup/addon-search-result";
import { AddonSearchResultFile } from "../../models/wowup/addon-search-result-file";
import { AddonUpdateEvent } from "../../models/wowup/addon-update-event";
import { getEnumName } from "../../utils/enum.utils";
import { getEnumList, getEnumName } from "../../utils/enum.utils";
import { AnalyticsService } from "../analytics/analytics.service";
import { DownloadSevice } from "../download/download.service";
import { FileService } from "../files/file.service";
@@ -286,6 +288,8 @@ export class AddonService {
await this.installDependencies(addon, onUpdate);
await this.backfillAddon(addon);
queueItem.completion.resolve();
} catch (err) {
console.error(err);
@@ -581,6 +585,7 @@ export class AddonService {
}
const matchedAddonFolders = addonFolders.filter((addonFolder) => !!addonFolder.matchingAddon);
matchedAddonFolders.forEach((maf) => this.setExternalIds(maf.matchingAddon, maf.toc));
const matchedGroups = _.groupBy(
matchedAddonFolders,
(addonFolder) => `${addonFolder.matchingAddon.providerName}${addonFolder.matchingAddon.externalId}`
@@ -588,7 +593,59 @@ export class AddonService {
console.log(Object.keys(matchedGroups));
return Object.values(matchedGroups).map((value) => value[0].matchingAddon);
return Object.values(matchedGroups).map(
(value) => _.orderBy(value, (v) => v.matchingAddon.externalIds.length).reverse()[0].matchingAddon
);
}
private setExternalIds(addon: Addon, toc: Toc) {
if (!toc) {
return;
}
const externalIds: AddonExternalId[] = [];
if (toc.wowInterfaceId) {
externalIds.push({
id: toc.wowInterfaceId,
providerName: ADDON_PROVIDER_WOWINTERFACE,
});
}
if (toc.tukUiProjectId) {
externalIds.push({
id: toc.tukUiProjectId,
providerName: ADDON_PROVIDER_TUKUI,
});
}
if (toc.curseProjectId) {
externalIds.push({
id: toc.curseProjectId,
providerName: ADDON_PROVIDER_CURSEFORGE,
});
}
addon.externalIds = externalIds;
}
public async setProvider(addon: Addon, externalId: string, providerName: string, clientType: WowClientType) {
const provider = this.getProvider(providerName);
if (!provider) {
throw new Error(`Provider not found: ${providerName}`);
}
console.debug(`Setting new provider: ${providerName}`);
const externalAddon = await this.getAddon(externalId, providerName, clientType).toPromise();
if (!externalAddon) {
throw new Error(`External addon not found: ${providerName}|${externalId}`);
}
console.debug("externalAdd", externalAddon);
this.removeAddon(addon, false);
this._addonStorage.set(externalAddon.id, externalAddon);
this.installAddon(externalAddon.id);
}
public getFeaturedAddons(clientType: WowClientType): Observable<AddonSearchResult[]> {
@@ -607,60 +664,51 @@ export class AddonService {
return !!this.getByExternalId(externalId, clientType);
}
public async backfillAddons() {
console.debug("backfillAddons");
const clientTypes = getEnumList<WowClientType>(WowClientType).filter(
(clientType) => clientType !== WowClientType.None
);
for (let clientType of clientTypes) {
const addons = this._addonStorage.getAllForClientType(clientType);
for (let addon of addons) {
await this.backfillAddon(addon);
}
}
}
public async backfillAddon(addon: Addon) {
if (addon.externalIds) {
return;
}
const tocPaths = this.getTocPaths(addon);
console.debug("tocPaths", tocPaths);
const tocFiles = await Promise.all(_.map(tocPaths, (tocPath) => this._tocService.parse(tocPath)));
const orderedTocFiles = _.orderBy(tocFiles, ["wowInterfaceId", "loadOnDemand"], ["desc", "asc"]);
const primaryToc = _.first(orderedTocFiles);
this.setExternalIds(addon, primaryToc);
this.saveAddon(addon);
}
public getTocPaths(addon: Addon) {
const addonFolderPath = this._warcraftService.getAddonFolderPath(addon.clientType);
const installedFolders = this.getInstalledFolders(addon);
return _.map(installedFolders, (installedFolder) =>
path.join(addonFolderPath, installedFolder, `${installedFolder}.toc`)
);
}
private getProvider(providerName: string) {
return this._addonProviders.find((provider) => provider.name === providerName);
}
private getAllStoredAddons(clientType: WowClientType) {
const addons: Addon[] = [];
this._addonStorage.query((store) => {
for (const result of store) {
addons.push(result[1] as Addon);
}
});
return addons;
}
private async getLocalAddons(clientType: WowClientType): Promise<any> {
const addonFolders = await this._warcraftService.listAddons(clientType);
const addons: Addon[] = [];
console.log("addonFolders", addonFolders);
for (const folder of addonFolders) {
try {
let addon: Addon;
if (folder.toc.curseProjectId) {
addon = await this.getCurseAddonById(folder, clientType);
} else {
}
if (!addon) {
continue;
}
addons.push(addon);
} catch (e) {
console.error(e);
}
}
return addons;
}
private getAddonProvider(addonUri: URL): AddonProvider {
return this._addonProviders.find((provider) => provider.isValidAddonUri(addonUri));
}
private async getCurseAddonById(addonFolder: AddonFolder, clientType: WowClientType) {
const curseProvider = this._addonProviders.find((p) => p instanceof CurseAddonProvider);
const searchResult = await curseProvider.getById(addonFolder.toc.curseProjectId, clientType).toPromise();
const latestFile = this.getLatestFile(searchResult, AddonChannelType.Stable);
return this.createAddon(addonFolder.name, searchResult, latestFile, clientType);
}
private getLatestFile(searchResult: AddonSearchResult, channelType: AddonChannelType): AddonSearchResultFile {
let files = _.filter(searchResult.files, (f: AddonSearchResultFile) => f.channelType <= channelType);
files = _.orderBy(files, ["releaseDate"]).reverse();

View File

@@ -12,6 +12,8 @@ import {
faPlay,
faBug,
faLink,
faInfoCircle,
faCodeBranch,
} from "@fortawesome/free-solid-svg-icons";
import { faQuestionCircle, faClock } from "@fortawesome/free-regular-svg-icons";
import { faDiscord, faGithub } from "@fortawesome/free-brands-svg-icons";
@@ -33,6 +35,8 @@ export class IconService {
this.addSvg(faLink);
this.addSvg(faDiscord);
this.addSvg(faGithub);
this.addSvg(faInfoCircle);
this.addSvg(faCodeBranch);
}
async addSvg(icon: IconDefinition) {

View File

@@ -25,6 +25,7 @@ export class TocService {
dependencies: this.getValue("Dependencies", tocText),
tukUiProjectId: this.getValue("X-Tukui-ProjectID", tocText),
tukUiProjectFolders: this.getValue("X-Tukui-ProjectFolders", tocText),
loadOnDemand: this.getValue("LoadOnDemand", tocText),
};
}

View File

@@ -0,0 +1,14 @@
import { orderBy, filter } from "lodash";
import { Addon } from "../entities/addon";
export function getAllProviders(addon: Addon) {
return orderBy(addon.externalIds, ["providerName"], ["asc"]);
}
export function getProviders(addon: Addon) {
return filter(getAllProviders(addon), (extId) => extId.providerName !== addon.providerName);
}
export function hasMultipleProviders(addon: Addon) {
return getProviders(addon).length > 0;
}

View File

@@ -77,6 +77,9 @@
"STABLE": "Stabil"
}
},
"ERRORS": {
"CHANGE_PROVIDER_ERROR": "Failed to change provider for {addonName} to {providerName}"
},
"PROGRESS_SPINNER": {
"LOADING": "Laden..."
},
@@ -92,6 +95,7 @@
"VIEW_ON_PROVIDER_PREFIX": "Anzeigen auf"
},
"ALERT": {
"ERROR_TITLE": "Error",
"POSITIVE_BUTTON": "Okay"
},
"CONFIRM": {
@@ -156,13 +160,18 @@
"ALPHA_ADDON_CHANNEL": "Alpha",
"AUTO_UPDATE_ADDON_BUTTON": "Automatisch aktualisieren",
"BETA_ADDON_CHANNEL": "Beta",
"CHANNEL_SUBMENT_TITLE": "Kanal",
"CHANNEL_SUBMENU_TITLE": "Kanal",
"IGNORE_ADDON_BUTTON": "Ignorieren",
"PROVIDER_SUBMENU_TITLE": "Providers",
"REINSTALL_ADDON_BUTTON": "Neu installieren",
"REMOVE_ADDON_BUTTON": "Entfernen",
"SHOW_FOLDER": "Dateiordner anzeigen",
"STABLE_ADDON_CHANNEL": "Stabil"
},
"CHANGE_ADDON_PROVIDER_CONFIRMATION": {
"MESSAGE": "Do you want to change the addon provider for {addonName} to {providerName}? This operation will uninstall your existing addon and replace it with a copy from the new provider.",
"TITLE": "Change Addon Provider?"
},
"CHECK_UPDATES_BUTTON": "Updates prüfen",
"CHECK_UPDATES_BUTTON_TOOLTIP": "Nach neuen Addon-Updates suchen",
"CLIENT_TYPE_SELECT_LABEL": "World of Warcraft",

View File

@@ -77,6 +77,9 @@
"STABLE": "Stable"
}
},
"ERRORS": {
"CHANGE_PROVIDER_ERROR": "Failed to change provider for {addonName} to {providerName}"
},
"PROGRESS_SPINNER": {
"LOADING": "Loading..."
},
@@ -92,6 +95,7 @@
"VIEW_ON_PROVIDER_PREFIX": "View on"
},
"ALERT": {
"ERROR_TITLE": "Error",
"POSITIVE_BUTTON": "Okay"
},
"CONFIRM": {
@@ -156,13 +160,18 @@
"ALPHA_ADDON_CHANNEL": "Alpha",
"AUTO_UPDATE_ADDON_BUTTON": "Auto Update",
"BETA_ADDON_CHANNEL": "Beta",
"CHANNEL_SUBMENT_TITLE": "Channel",
"CHANNEL_SUBMENU_TITLE": "Channel",
"IGNORE_ADDON_BUTTON": "Ignore",
"PROVIDER_SUBMENU_TITLE": "Providers",
"REINSTALL_ADDON_BUTTON": "Re-Install",
"REMOVE_ADDON_BUTTON": "Remove",
"SHOW_FOLDER": "Show Folder",
"STABLE_ADDON_CHANNEL": "Stable"
},
"CHANGE_ADDON_PROVIDER_CONFIRMATION": {
"MESSAGE": "Do you want to change the addon provider for {addonName} to {providerName}? This operation will uninstall your existing addon and replace it with a copy from the new provider.",
"TITLE": "Change Addon Provider?"
},
"CHECK_UPDATES_BUTTON": "Check Updates",
"CHECK_UPDATES_BUTTON_TOOLTIP": "Check for latest addon updates",
"CLIENT_TYPE_SELECT_LABEL": "World of Warcraft",

View File

@@ -77,6 +77,9 @@
"STABLE": "Estable"
}
},
"ERRORS": {
"CHANGE_PROVIDER_ERROR": "Failed to change provider for {addonName} to {providerName}"
},
"PROGRESS_SPINNER": {
"LOADING": "Cargando..."
},
@@ -92,6 +95,7 @@
"VIEW_ON_PROVIDER_PREFIX": "Ver en"
},
"ALERT": {
"ERROR_TITLE": "Error",
"POSITIVE_BUTTON": "Aceptar"
},
"CONFIRM": {
@@ -156,13 +160,18 @@
"ALPHA_ADDON_CHANNEL": "Alfa",
"AUTO_UPDATE_ADDON_BUTTON": "Actualización automática",
"BETA_ADDON_CHANNEL": "Beta",
"CHANNEL_SUBMENT_TITLE": "Canal",
"CHANNEL_SUBMENU_TITLE": "Canal",
"IGNORE_ADDON_BUTTON": "Ignorar",
"PROVIDER_SUBMENU_TITLE": "Providers",
"REINSTALL_ADDON_BUTTON": "Reinstalar",
"REMOVE_ADDON_BUTTON": "Eliminar",
"SHOW_FOLDER": "Mostrar Carpeta",
"STABLE_ADDON_CHANNEL": "Estable"
},
"CHANGE_ADDON_PROVIDER_CONFIRMATION": {
"MESSAGE": "Do you want to change the addon provider for {addonName} to {providerName}? This operation will uninstall your existing addon and replace it with a copy from the new provider.",
"TITLE": "Change Addon Provider?"
},
"CHECK_UPDATES_BUTTON": "Buscar Actualizaciones",
"CHECK_UPDATES_BUTTON_TOOLTIP": "Busca las últimas actualizaciones de los addons",
"CLIENT_TYPE_SELECT_LABEL": "World of Warcraft",

View File

@@ -77,6 +77,9 @@
"STABLE": "Stable"
}
},
"ERRORS": {
"CHANGE_PROVIDER_ERROR": "Failed to change provider for {addonName} to {providerName}"
},
"PROGRESS_SPINNER": {
"LOADING": "Chargement..."
},
@@ -92,6 +95,7 @@
"VIEW_ON_PROVIDER_PREFIX": "Voir sur"
},
"ALERT": {
"ERROR_TITLE": "Error",
"POSITIVE_BUTTON": "Ok"
},
"CONFIRM": {
@@ -156,13 +160,18 @@
"ALPHA_ADDON_CHANNEL": "Alpha",
"AUTO_UPDATE_ADDON_BUTTON": "Mise à jour automatique",
"BETA_ADDON_CHANNEL": "Bêta",
"CHANNEL_SUBMENT_TITLE": "Canal",
"CHANNEL_SUBMENU_TITLE": "Canal",
"IGNORE_ADDON_BUTTON": "Ignorer",
"PROVIDER_SUBMENU_TITLE": "Providers",
"REINSTALL_ADDON_BUTTON": "Réinstaller",
"REMOVE_ADDON_BUTTON": "Désinstaller",
"SHOW_FOLDER": "Montrer le dossier",
"STABLE_ADDON_CHANNEL": "Stable"
},
"CHANGE_ADDON_PROVIDER_CONFIRMATION": {
"MESSAGE": "Do you want to change the addon provider for {addonName} to {providerName}? This operation will uninstall your existing addon and replace it with a copy from the new provider.",
"TITLE": "Change Addon Provider?"
},
"CHECK_UPDATES_BUTTON": "Vérifier les mises à jour",
"CHECK_UPDATES_BUTTON_TOOLTIP": "Vérifier les dernières mises à jour des modules complémentaires",
"CLIENT_TYPE_SELECT_LABEL": "World of Warcraft",

View File

@@ -77,6 +77,9 @@
"STABLE": "Stabile"
}
},
"ERRORS": {
"CHANGE_PROVIDER_ERROR": "Failed to change provider for {addonName} to {providerName}"
},
"PROGRESS_SPINNER": {
"LOADING": "Caricamento in corso..."
},
@@ -92,6 +95,7 @@
"VIEW_ON_PROVIDER_PREFIX": "Visualizza su"
},
"ALERT": {
"ERROR_TITLE": "Error",
"POSITIVE_BUTTON": "Ok"
},
"CONFIRM": {
@@ -156,13 +160,18 @@
"ALPHA_ADDON_CHANNEL": "Alfa",
"AUTO_UPDATE_ADDON_BUTTON": "Aggiornamento Automatico",
"BETA_ADDON_CHANNEL": "Beta",
"CHANNEL_SUBMENT_TITLE": "Canale",
"CHANNEL_SUBMENU_TITLE": "Canale",
"IGNORE_ADDON_BUTTON": "Ignora",
"PROVIDER_SUBMENU_TITLE": "Providers",
"REINSTALL_ADDON_BUTTON": "Reinstalla",
"REMOVE_ADDON_BUTTON": "Rimuovi",
"SHOW_FOLDER": "Mostra cartella",
"STABLE_ADDON_CHANNEL": "Stabile"
},
"CHANGE_ADDON_PROVIDER_CONFIRMATION": {
"MESSAGE": "Do you want to change the addon provider for {addonName} to {providerName}? This operation will uninstall your existing addon and replace it with a copy from the new provider.",
"TITLE": "Change Addon Provider?"
},
"CHECK_UPDATES_BUTTON": "Controlla Aggiornamenti",
"CHECK_UPDATES_BUTTON_TOOLTIP": "Controlla gli ultimi aggiornamenti degli addons",
"CLIENT_TYPE_SELECT_LABEL": "World of Warcraft",

View File

@@ -77,6 +77,9 @@
"STABLE": "안정"
}
},
"ERRORS": {
"CHANGE_PROVIDER_ERROR": "Failed to change provider for {addonName} to {providerName}"
},
"PROGRESS_SPINNER": {
"LOADING": "불러오는 중..."
},
@@ -92,6 +95,7 @@
"VIEW_ON_PROVIDER_PREFIX": "다음에서 보기: "
},
"ALERT": {
"ERROR_TITLE": "Error",
"POSITIVE_BUTTON": "확인"
},
"CONFIRM": {
@@ -156,13 +160,18 @@
"ALPHA_ADDON_CHANNEL": "알파",
"AUTO_UPDATE_ADDON_BUTTON": "자동 업데이트",
"BETA_ADDON_CHANNEL": "베타",
"CHANNEL_SUBMENT_TITLE": "채널",
"CHANNEL_SUBMENU_TITLE": "채널",
"IGNORE_ADDON_BUTTON": "관리제외",
"PROVIDER_SUBMENU_TITLE": "Providers",
"REINSTALL_ADDON_BUTTON": "재설치",
"REMOVE_ADDON_BUTTON": "삭제",
"SHOW_FOLDER": "디렉토리 보기",
"STABLE_ADDON_CHANNEL": "안정"
},
"CHANGE_ADDON_PROVIDER_CONFIRMATION": {
"MESSAGE": "Do you want to change the addon provider for {addonName} to {providerName}? This operation will uninstall your existing addon and replace it with a copy from the new provider.",
"TITLE": "Change Addon Provider?"
},
"CHECK_UPDATES_BUTTON": "업데이트 체크",
"CHECK_UPDATES_BUTTON_TOOLTIP": "최신 애드온 업데이트를 체크",
"CLIENT_TYPE_SELECT_LABEL": "월드 오브 워크래프트",

View File

@@ -77,6 +77,9 @@
"STABLE": "Stable"
}
},
"ERRORS": {
"CHANGE_PROVIDER_ERROR": "Failed to change provider for {addonName} to {providerName}"
},
"PROGRESS_SPINNER": {
"LOADING": "Laster..."
},
@@ -92,6 +95,7 @@
"VIEW_ON_PROVIDER_PREFIX": "View on"
},
"ALERT": {
"ERROR_TITLE": "Error",
"POSITIVE_BUTTON": "Okey"
},
"CONFIRM": {
@@ -156,13 +160,18 @@
"ALPHA_ADDON_CHANNEL": "Alpha",
"AUTO_UPDATE_ADDON_BUTTON": "Oppdater Automatisk",
"BETA_ADDON_CHANNEL": "Beta",
"CHANNEL_SUBMENT_TITLE": "Kanal",
"CHANNEL_SUBMENU_TITLE": "Kanal",
"IGNORE_ADDON_BUTTON": "Ignorer",
"PROVIDER_SUBMENU_TITLE": "Providers",
"REINSTALL_ADDON_BUTTON": "Re-Installer",
"REMOVE_ADDON_BUTTON": "Fjern",
"SHOW_FOLDER": "Vis Mappe",
"STABLE_ADDON_CHANNEL": "Stable"
},
"CHANGE_ADDON_PROVIDER_CONFIRMATION": {
"MESSAGE": "Do you want to change the addon provider for {addonName} to {providerName}? This operation will uninstall your existing addon and replace it with a copy from the new provider.",
"TITLE": "Change Addon Provider?"
},
"CHECK_UPDATES_BUTTON": "Finn Oppdateringer",
"CHECK_UPDATES_BUTTON_TOOLTIP": "Finn de siste oppdateringene til utvidelsene dine",
"CLIENT_TYPE_SELECT_LABEL": "World of Warcraft",

View File

@@ -77,6 +77,9 @@
"STABLE": "Estável"
}
},
"ERRORS": {
"CHANGE_PROVIDER_ERROR": "Failed to change provider for {addonName} to {providerName}"
},
"PROGRESS_SPINNER": {
"LOADING": "Carregando..."
},
@@ -92,6 +95,7 @@
"VIEW_ON_PROVIDER_PREFIX": "View on"
},
"ALERT": {
"ERROR_TITLE": "Error",
"POSITIVE_BUTTON": "Ok"
},
"CONFIRM": {
@@ -156,13 +160,18 @@
"ALPHA_ADDON_CHANNEL": "Alfa",
"AUTO_UPDATE_ADDON_BUTTON": "Atualização Automática",
"BETA_ADDON_CHANNEL": "Beta",
"CHANNEL_SUBMENT_TITLE": "Canal",
"CHANNEL_SUBMENU_TITLE": "Canal",
"IGNORE_ADDON_BUTTON": "Ignorar",
"PROVIDER_SUBMENU_TITLE": "Providers",
"REINSTALL_ADDON_BUTTON": "Reinstalar",
"REMOVE_ADDON_BUTTON": "Remover",
"SHOW_FOLDER": "Mostrar Pasta",
"STABLE_ADDON_CHANNEL": "Estável"
},
"CHANGE_ADDON_PROVIDER_CONFIRMATION": {
"MESSAGE": "Do you want to change the addon provider for {addonName} to {providerName}? This operation will uninstall your existing addon and replace it with a copy from the new provider.",
"TITLE": "Change Addon Provider?"
},
"CHECK_UPDATES_BUTTON": "Verificar Atualizações",
"CHECK_UPDATES_BUTTON_TOOLTIP": "Verificar atualizações recentes",
"CLIENT_TYPE_SELECT_LABEL": "World of Warcraft",

View File

@@ -77,6 +77,9 @@
"STABLE": "Стабильная"
}
},
"ERRORS": {
"CHANGE_PROVIDER_ERROR": "Failed to change provider for {addonName} to {providerName}"
},
"PROGRESS_SPINNER": {
"LOADING": "Загрузка..."
},
@@ -92,6 +95,7 @@
"VIEW_ON_PROVIDER_PREFIX": "Посмотреть на"
},
"ALERT": {
"ERROR_TITLE": "Error",
"POSITIVE_BUTTON": "Окей"
},
"CONFIRM": {
@@ -156,13 +160,18 @@
"ALPHA_ADDON_CHANNEL": "Альфа",
"AUTO_UPDATE_ADDON_BUTTON": "Автообновление",
"BETA_ADDON_CHANNEL": "Бета",
"CHANNEL_SUBMENT_TITLE": "Тип выпуска",
"CHANNEL_SUBMENU_TITLE": "Тип выпуска",
"IGNORE_ADDON_BUTTON": "Пропускать",
"PROVIDER_SUBMENU_TITLE": "Providers",
"REINSTALL_ADDON_BUTTON": "Переустановить",
"REMOVE_ADDON_BUTTON": "Удалить",
"SHOW_FOLDER": "Показать папку",
"STABLE_ADDON_CHANNEL": "Стабильная"
},
"CHANGE_ADDON_PROVIDER_CONFIRMATION": {
"MESSAGE": "Do you want to change the addon provider for {addonName} to {providerName}? This operation will uninstall your existing addon and replace it with a copy from the new provider.",
"TITLE": "Change Addon Provider?"
},
"CHECK_UPDATES_BUTTON": "Проверить обновления",
"CHECK_UPDATES_BUTTON_TOOLTIP": "Проверить наличие последних обновлений модификации",
"CLIENT_TYPE_SELECT_LABEL": "World of Warcraft",

View File

@@ -77,6 +77,9 @@
"STABLE": "稳定版"
}
},
"ERRORS": {
"CHANGE_PROVIDER_ERROR": "Failed to change provider for {addonName} to {providerName}"
},
"PROGRESS_SPINNER": {
"LOADING": "正在加载..."
},
@@ -92,6 +95,7 @@
"VIEW_ON_PROVIDER_PREFIX": "在该网站上查看:"
},
"ALERT": {
"ERROR_TITLE": "Error",
"POSITIVE_BUTTON": "确定"
},
"CONFIRM": {
@@ -156,13 +160,18 @@
"ALPHA_ADDON_CHANNEL": "Alpha",
"AUTO_UPDATE_ADDON_BUTTON": "自动更新",
"BETA_ADDON_CHANNEL": "Beta",
"CHANNEL_SUBMENT_TITLE": "更新通道",
"CHANNEL_SUBMENU_TITLE": "更新通道",
"IGNORE_ADDON_BUTTON": "忽略",
"PROVIDER_SUBMENU_TITLE": "Providers",
"REINSTALL_ADDON_BUTTON": "重新安装",
"REMOVE_ADDON_BUTTON": "删除",
"SHOW_FOLDER": "打开文件夹",
"STABLE_ADDON_CHANNEL": "稳定版"
},
"CHANGE_ADDON_PROVIDER_CONFIRMATION": {
"MESSAGE": "Do you want to change the addon provider for {addonName} to {providerName}? This operation will uninstall your existing addon and replace it with a copy from the new provider.",
"TITLE": "Change Addon Provider?"
},
"CHECK_UPDATES_BUTTON": "检查更新",
"CHECK_UPDATES_BUTTON_TOOLTIP": "检查最新的插件更新",
"CLIENT_TYPE_SELECT_LABEL": "魔兽世界",

View File

@@ -1,3 +1,8 @@
export const ADDON_PROVIDER_WOWINTERFACE = "WowInterface";
export const ADDON_PROVIDER_CURSEFORGE = "Curse";
export const ADDON_PROVIDER_TUKUI = "TukUI";
// IPC CHANNELS
export const DOWNLOAD_FILE_CHANNEL = "download-file";
export const COPY_DIRECTORY_CHANNEL = "copy-directory";
export const CREATE_DIRECTORY_CHANNEL = "create-directory";