Merge pull request #543 from WowUp/raiderio-client

Add new Raider.io provider to handle when their client is used.
This commit is contained in:
jliddev
2020-11-28 09:05:09 -08:00
committed by GitHub
14 changed files with 223 additions and 31 deletions

View File

@@ -8,6 +8,9 @@ import { AddonSearchResult } from "../models/wowup/addon-search-result";
export interface AddonProvider {
name: AddonProviderType;
enabled: boolean;
forceIgnore: boolean;
allowReinstall: boolean;
allowChannelChange: boolean;
getAll(clientType: WowClientType, addonIds: string[]): Promise<AddonSearchResult[]>;
@@ -34,4 +37,4 @@ export interface AddonProvider {
scan(clientType: WowClientType, addonChannelType: AddonChannelType, addonFolders: AddonFolder[]): Promise<void>;
}
export type AddonProviderType = "Curse" | "GitHub" | "TukUI" | "WowInterface" | "WowUp";
export type AddonProviderType = "Curse" | "GitHub" | "TukUI" | "WowInterface" | "WowUp" | "RaiderIO";

View File

@@ -9,6 +9,7 @@ import { from, Observable } from "rxjs";
import { map } from "rxjs/operators";
import { v4 as uuidv4 } from "uuid";
import {
ADDON_PROVIDER_CURSEFORGE,
CURSE_GET_SCAN_RESULTS,
NO_LATEST_SEARCH_RESULT_FILES_ERROR,
NO_SEARCH_RESULTS_ERROR,
@@ -41,7 +42,10 @@ export class CurseAddonProvider implements AddonProvider {
return this._circuitBreaker as CircuitBreaker<[clientType: () => Promise<T>], T>;
}
public readonly name = "Curse";
public readonly name = ADDON_PROVIDER_CURSEFORGE;
public readonly forceIgnore = false;
public readonly allowReinstall = true;
public readonly allowChannelChange = true;
public enabled = true;
constructor(

View File

@@ -1,4 +1,5 @@
import { HttpClient } from "@angular/common/http";
import { ADDON_PROVIDER_GITHUB } from "../../common/constants";
import * as _ from "lodash";
import { forkJoin, Observable } from "rxjs";
import { map } from "rxjs/operators";
@@ -22,7 +23,10 @@ const API_URL = "https://api.github.com/repos";
const RELEASE_CONTENT_TYPES = ["application/x-zip-compressed", "application/zip"];
export class GitHubAddonProvider implements AddonProvider {
public readonly name = "GitHub";
public readonly name = ADDON_PROVIDER_GITHUB;
public readonly forceIgnore = false;
public readonly allowReinstall = true;
public readonly allowChannelChange = false;
public enabled = true;
constructor(private _httpClient: HttpClient) {}

View File

@@ -0,0 +1,122 @@
import * as _ from "lodash";
import { Observable } from "rxjs";
import { find, filter } from "lodash";
import { v4 as uuidv4 } from "uuid";
import { Addon } 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";
import { AddonSearchResult } from "../models/wowup/addon-search-result";
import { AddonProvider } from "./addon-provider";
import { ADDON_PROVIDER_RAIDERIO } from "../../common/constants";
export class RaiderIoAddonProvider implements AddonProvider {
private readonly _scanWebsite = "https://raider.io";
private readonly _scanAddonProvider = "raiderio-client";
private readonly _scanFolderName = "RaiderIO";
public readonly name = ADDON_PROVIDER_RAIDERIO;
public readonly forceIgnore = true;
public readonly allowReinstall = false;
public readonly allowChannelChange = false;
public enabled = true;
constructor() {}
public async getAll(clientType: WowClientType, addonIds: string[]): Promise<AddonSearchResult[]> {
return [];
}
public async getFeaturedAddons(clientType: WowClientType): Promise<AddonSearchResult[]> {
return [];
}
public async searchByQuery(query: string, clientType: WowClientType): Promise<AddonSearchResult[]> {
return [];
}
public async searchByUrl(addonUri: URL, clientType: WowClientType): Promise<AddonSearchResult> {
return undefined;
}
public async searchByName(
addonName: string,
folderName: string,
clientType: WowClientType,
nameOverride?: string
): Promise<AddonSearchResult[]> {
return [];
}
public getById(addonId: string, clientType: WowClientType): Observable<AddonSearchResult> {
return undefined;
}
public isValidAddonUri(addonUri: URL): boolean {
return false;
}
public isValidAddonId(addonId: string): boolean {
return false;
}
public onPostInstall(addon: Addon): void {}
public async scan(
clientType: WowClientType,
addonChannelType: AddonChannelType,
addonFolders: AddonFolder[]
): Promise<void> {
console.debug("RAIDER IO CLIENT SCAN");
const raiderIo = find(addonFolders, (addonFolder) => this.isRaiderIo(addonFolder));
if (!raiderIo) {
return;
}
const dependencies = filter(addonFolders, (addonFolder) => this.isRaiderIoDependant(addonFolder));
console.debug("RAIDER IO CLIENT FOUND", dependencies);
const rioAddonFolders = [raiderIo, ...dependencies];
const installedFolders = rioAddonFolders.map((addonFolder) => addonFolder.name).join(",");
for (const rioAddonFolder of rioAddonFolders) {
rioAddonFolder.matchingAddon = {
autoUpdateEnabled: false,
channelType: AddonChannelType.Stable,
clientType,
id: uuidv4(),
isIgnored: true,
name: raiderIo.toc.title,
author: rioAddonFolder.toc.author,
downloadUrl: "",
externalId: this.name,
externalUrl: this._scanWebsite,
gameVersion: rioAddonFolder.toc.interface,
installedAt: new Date(),
installedFolders: installedFolders,
installedVersion: rioAddonFolder.toc.version || raiderIo.toc.version,
latestVersion: raiderIo.toc.version,
providerName: this.name,
thumbnailUrl: "http://cdnassets.raider.io/images/fb_app_image.jpg?2019-11-18",
updatedAt: new Date(),
summary: rioAddonFolder.toc.notes,
downloadCount: 0,
screenshotUrls: [],
releasedAt: new Date(),
isLoadOnDemand: rioAddonFolder.toc.loadOnDemand === "1",
};
}
}
private isRaiderIo(addonFolder: AddonFolder) {
return (
addonFolder.name === this._scanFolderName &&
addonFolder.toc?.website === this._scanWebsite &&
addonFolder.toc?.addonProvider === this._scanAddonProvider
);
}
private isRaiderIoDependant(addonFolder: AddonFolder) {
return addonFolder.toc?.dependencies.indexOf(this._scanFolderName) !== -1;
}
}

View File

@@ -1,4 +1,5 @@
import { HttpClient } from "@angular/common/http";
import { ADDON_PROVIDER_TUKUI } from "../../common/constants";
import * as _ from "lodash";
import * as CircuitBreaker from "opossum";
import { from, Observable } from "rxjs";
@@ -22,7 +23,10 @@ const CLIENT_API_URL = "https://www.tukui.org/client-api.php";
export class TukUiAddonProvider implements AddonProvider {
private readonly _circuitBreaker: CircuitBreaker<[clientType: WowClientType], TukUiAddon[]>;
public readonly name = "TukUI";
public readonly name = ADDON_PROVIDER_TUKUI;
public readonly forceIgnore = false;
public readonly allowReinstall = true;
public readonly allowChannelChange = false;
public enabled = true;
constructor(

View File

@@ -1,4 +1,5 @@
import { HttpClient } from "@angular/common/http";
import { ADDON_PROVIDER_WOWINTERFACE } from "../../common/constants";
import * as _ from "lodash";
import * as CircuitBreaker from "opossum";
import { from, Observable } from "rxjs";
@@ -22,7 +23,10 @@ const ADDON_URL = "https://www.wowinterface.com/downloads/info";
export class WowInterfaceAddonProvider implements AddonProvider {
private readonly _circuitBreaker: CircuitBreaker<[addonId: string], AddonDetailsResponse>;
public readonly name = "WowInterface";
public readonly name = ADDON_PROVIDER_WOWINTERFACE;
public readonly forceIgnore = false;
public readonly allowReinstall = true;
public readonly allowChannelChange = false;
public enabled = true;
constructor(

View File

@@ -1,7 +1,7 @@
import { HttpClient } from "@angular/common/http";
import { Observable, of } from "rxjs";
import { v4 as uuidv4 } from "uuid";
import { WOWUP_GET_SCAN_RESULTS } from "../../common/constants";
import { ADDON_PROVIDER_WOWUP, WOWUP_GET_SCAN_RESULTS } from "../../common/constants";
import { WowUpScanResult } from "../../common/wowup/wowup-scan-result";
import { AppConfig } from "../../environments/environment";
import { Addon } from "../entities/addon";
@@ -20,7 +20,10 @@ import { AddonProvider } from "./addon-provider";
const API_URL = AppConfig.wowUpHubUrl;
export class WowUpAddonProvider implements AddonProvider {
public readonly name = "WowUp";
public readonly name = ADDON_PROVIDER_WOWUP;
public readonly forceIgnore = false;
public readonly allowReinstall = true;
public readonly allowChannelChange = false;
public enabled = true;
constructor(private _httpClient: HttpClient, private _electronService: ElectronService) {}

View File

@@ -110,7 +110,7 @@ export class AppComponent implements OnInit, AfterViewInit {
console.debug("Auto update");
const updatedAddons = await this._addonService.processAutoUpdates();
if (updatedAddons.length === 0) {
if (!updatedAddons || updatedAddons.length === 0) {
this.checkQuitEnabled();
return;
}

View File

@@ -14,4 +14,6 @@ export interface Toc {
tukUiProjectFolders?: string;
loadOnDemand?: string;
dependencyList: string[];
addonProvider?: string;
notes?: string;
}

View File

@@ -209,19 +209,23 @@
</div>
</div>
<mat-divider></mat-divider>
<div *ngIf="listItem.isUnMatched() === false" class="mat-menu-item">
<!-- IGNORE -->
<div *ngIf="addonService.isForceIgnore(listItem.addon) === false" 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>
<!-- AUTO UPDATE -->
<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 *ngIf="listItem.isUnMatched() === false" mat-menu-item [matMenuTriggerFor]="addonChannels">
<!-- CHANNEL SUBMENU -->
<button *ngIf="addonService.canChangeChannel(listItem.addon)" mat-menu-item [matMenuTriggerFor]="addonChannels">
{{ "PAGES.MY_ADDONS.ADDON_CONTEXT_MENU.CHANNEL_SUBMENU_TITLE" | translate }}
</button>
<!-- PROVIDER SUBMENU -->
<button *ngIf="addonUtils.hasMultipleProviders(listItem.addon)" mat-menu-item [matMenuTriggerFor]="addonProviders">
{{ "PAGES.MY_ADDONS.ADDON_CONTEXT_MENU.PROVIDER_SUBMENU_TITLE" | translate }}
</button>
@@ -240,10 +244,12 @@
</div>
</div>
</a>
<button *ngIf="listItem.isUnMatched() === false" mat-menu-item (click)="onReInstallAddon(listItem)">
<!-- REINSTALL -->
<button *ngIf="addonService.canReinstall(listItem.addon)" mat-menu-item (click)="onReInstallAddon(listItem)">
{{ "PAGES.MY_ADDONS.ADDON_CONTEXT_MENU.REINSTALL_ADDON_BUTTON" | translate }}
</button>
<mat-divider></mat-divider>
<!-- REMOVE -->
<button mat-menu-item (click)="onRemoveAddon(listItem.addon)">
{{ "PAGES.MY_ADDONS.ADDON_CONTEXT_MENU.REMOVE_ADDON_BUTTON" | translate }}
</button>

View File

@@ -6,6 +6,7 @@ import { GitHubAddonProvider } from "../../addon-providers/github-addon-provider
import { TukUiAddonProvider } from "../../addon-providers/tukui-addon-provider";
import { WowInterfaceAddonProvider } from "../../addon-providers/wow-interface-addon-provider";
import { WowUpAddonProvider } from "../../addon-providers/wowup-addon-provider";
import { RaiderIoAddonProvider } from "../../addon-providers/raiderio-provider";
import { CachingService } from "../caching/caching-service";
import { ElectronService } from "../electron/electron.service";
import { WowUpService } from "../wowup/wowup.service";
@@ -25,6 +26,10 @@ export class AddonProviderFactory {
private _wowupService: WowUpService
) {}
public createRaiderIoAddonProvider(): RaiderIoAddonProvider {
return new RaiderIoAddonProvider();
}
public createCurseAddonProvider(): CurseAddonProvider {
return new CurseAddonProvider(this._httpClient, this._cachingService, this._electronService);
}
@@ -53,6 +58,7 @@ export class AddonProviderFactory {
public getAll(): AddonProvider[] {
if (this._providers.length === 0) {
this._providers = [
this.createRaiderIoAddonProvider(),
this.createCurseAddonProvider(),
this.createTukUiAddonProvider(),
this.createWowInterfaceAddonProvider(),

View File

@@ -6,6 +6,7 @@ import { AddonSearchResultDependency } from "../../models/wowup/addon-search-res
import { Toc } from "../../models/wowup/toc";
import {
ADDON_PROVIDER_CURSEFORGE,
ADDON_PROVIDER_RAIDERIO,
ADDON_PROVIDER_TUKUI,
ADDON_PROVIDER_UNKNOWN,
ADDON_PROVIDER_WOWINTERFACE,
@@ -103,6 +104,18 @@ export class AddonService {
});
}
public isForceIgnore(addon: Addon) {
return addon.providerName === ADDON_PROVIDER_UNKNOWN || this.getProvider(addon.providerName).forceIgnore;
}
public canReinstall(addon: Addon) {
return addon.providerName !== ADDON_PROVIDER_UNKNOWN && this.getProvider(addon.providerName).allowReinstall;
}
public canChangeChannel(addon: Addon) {
return addon.providerName !== ADDON_PROVIDER_UNKNOWN && this.getProvider(addon.providerName).allowChannelChange;
}
public getAddonProviderStates(): AddonProviderState[] {
return _.map(this._addonProviders, (provider) => {
return {
@@ -901,7 +914,7 @@ export class AddonService {
}
private getProvider(providerName: string) {
return this.getEnabledAddonProviders().find((provider) => provider.name === providerName);
return this._addonProviders.find((provider) => provider.name === providerName);
}
public async backfillAddons() {

View File

@@ -2,8 +2,24 @@ import { Injectable } from "@angular/core";
import { Toc } from "../../models/wowup/toc";
import { FileService } from "../files/file.service";
const TOC_AUTHOR = "Author";
const TOC_DEPENDENCIES = "Dependencies";
const TOC_INTERFACE = "Interface";
const TOC_NOTES = "Notes";
const TOC_REQUIRED_DEPS = "RequiredDeps";
const TOC_TITLE = "Title";
const TOC_VERSION = "Version";
const TOC_WEBSITE = "Website";
const TOC_X_ADDON_PROVIDER = "X-AddonProvider"; // Raider.IO
const TOC_X_CATEGORY = "X-Category";
const TOC_X_CURSE_PROJECT_ID = "X-Curse-Project-ID"; // CurseForge
const TOC_X_LOADONDEMAND = "LoadOnDemand";
const TOC_X_LOCALIZATIONS = "X-Localizations";
const TOC_X_PART_OF = "X-Part-Of";
const TOC_X_TUKUI_PROJECTID = "X-Tukui-ProjectID"; // WowInterface
const TOC_X_TUKUI_PROJECTFOLDERS = "X-Tukui-ProjectFolders"; // WowInterface
const TOC_X_WEBSITE = "X-Website";
const TOC_X_WOWI_ID = "X-WoWI-ID"; // WowInterface
@Injectable({
providedIn: "root",
@@ -15,38 +31,40 @@ export class TocService {
let tocText = await this._fileService.readFile(tocPath);
tocText = tocText.trim();
const dependencies =
this.getValue(TOC_DEPENDENCIES, tocText) ||
this.getValue(TOC_REQUIRED_DEPS, tocText);
const dependencies = this.getValue(TOC_DEPENDENCIES, tocText) || this.getValue(TOC_REQUIRED_DEPS, tocText);
const dependencyList: string[] = this.getDependencyList(tocText);
return {
author: this.getValue("Author", tocText),
curseProjectId: this.getValue("X-Curse-Project-ID", tocText),
interface: this.getValue("Interface", tocText),
title: this.getValue("Title", tocText),
website: this.getValue("Website", tocText),
version: this.getValue("Version", tocText),
partOf: this.getValue("X-Part-Of", tocText),
category: this.getValue("X-Category", tocText),
localizations: this.getValue("X-Localizations", tocText),
wowInterfaceId: this.getValue("X-WoWI-ID", tocText),
author: this.getValue(TOC_AUTHOR, tocText),
curseProjectId: this.getValue(TOC_X_CURSE_PROJECT_ID, tocText),
interface: this.getValue(TOC_INTERFACE, tocText),
title: this.getValue(TOC_TITLE, tocText),
website: this.getWebsite(tocText),
version: this.getValue(TOC_VERSION, tocText),
partOf: this.getValue(TOC_X_PART_OF, tocText),
category: this.getValue(TOC_X_CATEGORY, tocText),
localizations: this.getValue(TOC_X_LOCALIZATIONS, tocText),
wowInterfaceId: this.getValue(TOC_X_WOWI_ID, tocText),
dependencies,
dependencyList,
tukUiProjectId: this.getValue("X-Tukui-ProjectID", tocText),
tukUiProjectFolders: this.getValue("X-Tukui-ProjectFolders", tocText),
loadOnDemand: this.getValue("LoadOnDemand", tocText),
tukUiProjectId: this.getValue(TOC_X_TUKUI_PROJECTID, tocText),
tukUiProjectFolders: this.getValue(TOC_X_TUKUI_PROJECTFOLDERS, tocText),
loadOnDemand: this.getValue(TOC_X_LOADONDEMAND, tocText),
addonProvider: this.getValue(TOC_X_ADDON_PROVIDER, tocText),
notes: this.getValue(TOC_NOTES, tocText),
};
}
private getWebsite(tocText: string) {
return this.getValue(TOC_WEBSITE, tocText) || this.getValue(TOC_X_WEBSITE, tocText);
}
private getDependencyList(tocText: string) {
const dependencies = this.getValue(TOC_DEPENDENCIES, tocText);
const requiredDeps = this.getValue(TOC_REQUIRED_DEPS, tocText);
const deps = []
.concat(...dependencies.split(","), ...requiredDeps.split(","))
.filter((dep) => !!dep);
const deps = [].concat(...dependencies.split(","), ...requiredDeps.split(",")).filter((dep) => !!dep);
console.debug("deps", deps);

View File

@@ -1,7 +1,10 @@
export const ADDON_PROVIDER_WOWINTERFACE = "WowInterface";
export const ADDON_PROVIDER_CURSEFORGE = "Curse";
export const ADDON_PROVIDER_GITHUB = "GitHub";
export const ADDON_PROVIDER_RAIDERIO = "RaiderIO";
export const ADDON_PROVIDER_TUKUI = "TukUI";
export const ADDON_PROVIDER_UNKNOWN = "Unknown";
export const ADDON_PROVIDER_WOWUP = "WowUp";
// IPC CHANNELS
export const DOWNLOAD_FILE_CHANNEL = "download-file";