Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use IndexedDB to store Thunderstore package list #1261

Merged
merged 5 commits into from
Apr 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"bulma-steps": "^2.2.1",
"bulma-switch": "^2.0.0",
"core-js": "^3.6.5",
"dexie": "^3.0.3",
"dexie": "^3.2.7",
"dot-prop": "^5.2.0",
"electron-updater": "4.2.5",
"elliptic": "^6.5.4",
Expand Down
7 changes: 4 additions & 3 deletions src/components/mixins/SplashMixin.vue
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ export default class SplashMixin extends Vue {
this.getRequestItem('ThunderstoreDownload').setProgress(100);
}

// TODO: since the mod list is stored in IndexedDB storing it
// also on a file servers no purpose, clean this up.
if (response) {
ApiCacheUtils.storeLastRequest(response.data);
} else {
Expand All @@ -75,8 +77,8 @@ export default class SplashMixin extends Vue {
}

if (response) {
ThunderstorePackages.handlePackageApiResponse(response);
await this.$store.dispatch('tsMods/updateMods', ThunderstorePackages.PACKAGES);
await ThunderstorePackages.handlePackageApiResponse(this.activeGame.internalFolderName, response);
await this.$store.dispatch('tsMods/updateMods');
await this.moveToNextScreen();
} else {
this.heroTitle = 'Failed to get mods from Thunderstore and cache';
Expand All @@ -85,7 +87,6 @@ export default class SplashMixin extends Vue {
}

async continueOffline() {
ThunderstorePackages.PACKAGES = [];
await this.moveToNextScreen();
}

Expand Down
2 changes: 1 addition & 1 deletion src/components/mixins/UtilityMixin.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export default class UtilityMixin extends Vue {

const response = await ThunderstorePackages.update(this.$store.state.activeGame);
await ApiCacheUtils.storeLastRequest(response.data);
await this.$store.dispatch("tsMods/updateMods", ThunderstorePackages.PACKAGES);
await this.$store.dispatch("tsMods/updateMods");
await this.$store.dispatch("profile/tryLoadModListFromDisk");
}

Expand Down
17 changes: 6 additions & 11 deletions src/components/views/DownloadModModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ import StatusEnum from '../../model/enums/StatusEnum';
import ThunderstoreCombo from '../../model/ThunderstoreCombo';
import ProfileInstallerProvider from '../../providers/ror2/installing/ProfileInstallerProvider';
import ProfileModList from '../../r2mm/mods/ProfileModList';
import ModBridge from '../../r2mm/mods/ModBridge';
import Profile from '../../model/Profile';
import { Progress } from '../all';
import Game from '../../model/game/Game';
Expand Down Expand Up @@ -242,27 +241,23 @@ let assignId = 0;
this.downloadHandler(refSelectedThunderstoreMod, version);
}

// TODO: rethink how this method and provider's downloadLatestOfAll()
// access the active game, local mod list and TS mod list.
async downloadLatest() {
this.closeModal();
const localMods = await ProfileModList.getModList(this.contextProfile!);
if (localMods instanceof R2Error) {
this.downloadingMod = false;
this.$store.commit('error/handleError', localMods);
return;
}
const outdatedMods = localMods.filter(mod => !ModBridge.isCachedLatestVersion(mod));
const modsWithUpdates: ThunderstoreCombo[] = this.$store.getters['profile/modsWithUpdates'];
const currentAssignId = assignId++;
const progressObject = {
progress: 0,
initialMods: outdatedMods.map(value => `${value.getName()} (${value.getVersionNumber().toString()})`),
initialMods: modsWithUpdates.map(value => `${value.getMod().getName()} (${value.getVersion().toString()})`),
modName: '',
assignId: currentAssignId,
failed: false,
};
this.downloadObject = progressObject;
DownloadModModal.allVersions.push([currentAssignId, this.downloadObject]);
this.downloadingMod = true;
ThunderstoreDownloaderProvider.instance.downloadLatestOfAll(this.activeGame, outdatedMods, this.thunderstorePackages, (progress: number, modName: string, status: number, err: R2Error | null) => {
ThunderstoreDownloaderProvider.instance.downloadLatestOfAll(this.activeGame, modsWithUpdates, this.thunderstorePackages, (progress: number, modName: string, status: number, err: R2Error | null) => {
const assignIndex = DownloadModModal.allVersions.findIndex(([number, val]) => number === currentAssignId);
if (status === StatusEnum.FAILURE) {
if (err !== null) {
Expand All @@ -277,7 +272,7 @@ let assignId = 0;
const obj = {
progress: progress,
modName: modName,
initialMods: outdatedMods.map(value => `${value.getName()} (${value.getVersionNumber().toString()})`),
initialMods: modsWithUpdates.map(value => `${value.getMod().getName()} (${value.getVersion().getVersionNumber().toString()})`),
assignId: currentAssignId,
failed: false,
}
Expand Down
13 changes: 8 additions & 5 deletions src/components/views/LocalModList/LocalModCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import ManifestV2 from '../../../model/ManifestV2';
import ThunderstoreMod from '../../../model/ThunderstoreMod';
import { LogSeverity } from '../../../providers/ror2/logging/LoggerProvider';
import Dependants from '../../../r2mm/mods/Dependants';
import ModBridge from '../../../r2mm/mods/ModBridge';

@Component({
components: {
Expand All @@ -28,16 +27,20 @@ export default class LocalModCard extends Vue {
return this.tsMod ? this.tsMod.getDonationLink() : undefined;
}

get isDeprecated() {
return this.tsMod ? this.tsMod.isDeprecated() : false;
}

get isLatestVersion() {
return ModBridge.isCachedLatestVersion(this.mod);
return this.$store.getters['tsMods/isLatestVersion'](this.mod);
}

get localModList(): ManifestV2[] {
return this.$store.state.profile.modList;
}

get tsMod() {
return ModBridge.getCachedThunderstoreModFromMod(this.mod);
get tsMod(): ThunderstoreMod | undefined {
return this.$store.getters['tsMods/tsMod'](this.mod);
}

@Watch("localModList")
Expand Down Expand Up @@ -175,7 +178,7 @@ function dependencyStringToModName(x: string) {

<template v-slot:title>
<span class="non-selectable">
<span v-if="mod.isDeprecated()"
<span v-if="isDeprecated"
class="tag is-danger margin-right margin-right--half-width"
v-tooltip.right="'This mod is deprecated and could be broken'">
Deprecated
Expand Down
7 changes: 0 additions & 7 deletions src/model/ManifestV2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import ReactiveObjectConverterInterface from './safety/ReactiveObjectConverter';
import * as path from 'path';
import PathResolver from '../r2mm/manager/PathResolver';
import R2Error from './errors/R2Error';
import ModBridge from '../r2mm/mods/ModBridge';
import ThunderstorePackages from '../r2mm/data/ThunderstorePackages';

export default class ManifestV2 implements ReactiveObjectConverterInterface {

Expand Down Expand Up @@ -268,11 +266,6 @@ export default class ManifestV2 implements ReactiveObjectConverterInterface {
return this.enabled;
}

public isDeprecated(): boolean {
const tsMod = ModBridge.getCachedThunderstoreModFromMod(this);
return tsMod !== undefined ? tsMod.isDeprecated() : false;
}

public getIcon(): string {
return this.icon;
}
Expand Down
8 changes: 8 additions & 0 deletions src/model/ThunderstoreMod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ export default class ThunderstoreMod extends ThunderstoreVersion implements Reac
this.versions = versions;
}

public getLatestVersion(): ThunderstoreVersion {
return this.getVersions().reduce(reduceToNewestVersion);
}

public getRating(): number {
return this.rating;
}
Expand Down Expand Up @@ -172,3 +176,7 @@ export default class ThunderstoreMod extends ThunderstoreVersion implements Reac
this.donationLink = url;
}
}

function reduceToNewestVersion(v1: ThunderstoreVersion, v2: ThunderstoreVersion) {
return v1.getVersionNumber().isNewerThan(v2.getVersionNumber()) ? v1 : v2;
};
13 changes: 3 additions & 10 deletions src/providers/ror2/downloading/ThunderstoreDownloaderProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,23 +45,16 @@ export default abstract class ThunderstoreDownloaderProvider {
*/
public abstract buildDependencySetUsingLatest(mod: ThunderstoreVersion, allMods: ThunderstoreMod[], builder: ThunderstoreCombo[]): ThunderstoreCombo[];

/**
* Allows a list of mods passed in to be converted to the latest version of their equivalent upload.
*
* @param mods
* @param allMods
*/
public abstract getLatestOfAllToUpdate(mods: ManifestV2[], allMods: ThunderstoreMod[]): ThunderstoreCombo[];

/**
* A top-level method to download the latest version of all mods passed in, including their dependencies.
*
* @param mods An array of ManifestV2 objects to be updated.
* @param game Currently selected game
* @param modsWithUpdate An array of ThunderstoreCombo objects to be updated.
* @param allMods An array of all mods available from the Thunderstore API.
* @param callback Callback to show the current state of the downloads.
* @param completedCallback Callback to perform final actions against. Only called if {@param callback} has not returned a failed status.
*/
public abstract downloadLatestOfAll(game: Game, mods: ManifestV2[], allMods: ThunderstoreMod[],
public abstract downloadLatestOfAll(game: Game, modsWithUpdate: ThunderstoreCombo[], allMods: ThunderstoreMod[],
callback: (progress: number, modName: string, status: number, err: R2Error | null) => void,
completedCallback: (modList: ThunderstoreCombo[]) => void): void;

Expand Down
24 changes: 12 additions & 12 deletions src/r2mm/data/ThunderstorePackages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import ThunderstoreMod from '../../model/ThunderstoreMod';
import Game from '../../model/game/Game';
import ApiResponse from '../../model/api/ApiResponse';
import ConnectionProvider from '../../providers/generic/connection/ConnectionProvider';
import ModBridge from '../mods/ModBridge';
import * as PackageDb from '../manager/PackageDexieStore';

export default class ThunderstorePackages {

public static PACKAGES: ThunderstoreMod[] = [];
public static PACKAGES_MAP: Map<String, ThunderstoreMod> = new Map();
// TODO: would IndexedDB or Vuex be more suitable place for exclusions?
public static EXCLUSIONS: string[] = [];

/**
Expand All @@ -16,7 +16,7 @@ export default class ThunderstorePackages {
public static async update(game: Game) {
this.EXCLUSIONS = await ConnectionProvider.instance.getExclusions();
const response = await ConnectionProvider.instance.getPackages(game);
this.handlePackageApiResponse(response);
await this.handlePackageApiResponse(game.internalFolderName, response);

return response;
}
Expand All @@ -25,22 +25,22 @@ export default class ThunderstorePackages {
* Transform {response.data} to ThunderstoreMod list and map.
* @param response api/v1/package data.
*/
public static handlePackageApiResponse(response: ApiResponse) {
ThunderstorePackages.PACKAGES = response.data
.map(ThunderstoreMod.parseFromThunderstoreData)
.filter((mod) => !ThunderstorePackages.EXCLUSIONS.includes(mod.getFullName()));
public static async handlePackageApiResponse(gameName: string, response: ApiResponse) {
const packages = response.data
.filter((p) => !ThunderstorePackages.EXCLUSIONS.includes(p["full_name"]));

ModBridge.clearCache();
// TODO: see if this can be hooked into the progress bar in Splash screen.
await PackageDb.updateFromApiResponse(gameName, packages);
}

ThunderstorePackages.PACKAGES_MAP = ThunderstorePackages.PACKAGES.reduce((map, pkg) => {
public static getDeprecatedPackageMap(packages: ThunderstoreMod[]): Map<string, boolean> {
ThunderstorePackages.PACKAGES_MAP = packages.reduce((map, pkg) => {
map.set(pkg.getFullName(), pkg);
return map;
}, new Map<String, ThunderstoreMod>());
}

public static getDeprecatedPackageMap(): Map<string, boolean> {
const result = new Map<string, boolean>();
this.PACKAGES.forEach(pkg => {
packages.forEach(pkg => {
this.populateDeprecatedPackageMapForModChain(pkg, result);
});
return result;
Expand Down
26 changes: 5 additions & 21 deletions src/r2mm/downloading/BetterThunderstoreDownloader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,9 @@ import * as path from 'path';
import FsProvider from '../../providers/generic/file/FsProvider';
import FileWriteError from '../../model/errors/FileWriteError';
import Profile from '../../model/Profile';
import ThunderstorePackages from '../data/ThunderstorePackages';
import ExportMod from '../../model/exports/ExportMod';
import ManagerSettings from '../manager/ManagerSettings';
import ManifestV2 from '../../model/ManifestV2';
import ModBridge from '../mods/ModBridge';
import * as PackageDb from '../manager/PackageDexieStore';
import ThunderstoreDownloaderProvider from '../../providers/ror2/downloading/ThunderstoreDownloaderProvider';
import ManagerInformation from '../../_managerinf/ManagerInformation';
import Game from '../../model/game/Game';
Expand Down Expand Up @@ -92,26 +90,12 @@ export default class BetterThunderstoreDownloader extends ThunderstoreDownloader
})
}

public getLatestOfAllToUpdate(mods: ManifestV2[], allMods: ThunderstoreMod[]): ThunderstoreCombo[] {
return mods.filter(mod => !ModBridge.isCachedLatestVersion(mod))
.map(mod => ModBridge.getCachedThunderstoreModFromMod(mod))
.filter(value => value != undefined)
.map(mod => {
const latestVersion = mod!.getVersions().sort((a, b) => a.getVersionNumber().compareToDescending(b.getVersionNumber()))[0];
const combo = new ThunderstoreCombo();
combo.setMod(mod!);
combo.setVersion(latestVersion);
return combo;
})
}

public async downloadLatestOfAll(game: Game, mods: ManifestV2[], allMods: ThunderstoreMod[],
public async downloadLatestOfAll(game: Game, modsWithUpdates: ThunderstoreCombo[], allMods: ThunderstoreMod[],
callback: (progress: number, modName: string, status: number, err: R2Error | null) => void,
completedCallback: (modList: ThunderstoreCombo[]) => void) {

const dependenciesToUpdate: ThunderstoreCombo[] = this.getLatestOfAllToUpdate(mods, allMods);
const dependencies: ThunderstoreCombo[] = [...dependenciesToUpdate];
dependenciesToUpdate.forEach(value => {
const dependencies: ThunderstoreCombo[] = [...modsWithUpdates];
modsWithUpdates.forEach(value => {
this.buildDependencySetUsingLatest(value.getVersion(), allMods, dependencies);
});

Expand Down Expand Up @@ -203,7 +187,7 @@ export default class BetterThunderstoreDownloader extends ThunderstoreDownloader
public async downloadImportedMods(game: Game, modList: ExportMod[],
callback: (progress: number, modName: string, status: number, err: R2Error | null) => void,
completedCallback: (mods: ThunderstoreCombo[]) => void) {
const tsMods: ThunderstoreMod[] = ThunderstorePackages.PACKAGES;
const tsMods = await PackageDb.getPackagesAsThunderstoreMods(game.internalFolderName);
const comboList: ThunderstoreCombo[] = [];
for (const importMod of modList) {
for (const mod of tsMods) {
Expand Down
Loading
Loading