Skip to content

Commit

Permalink
Further improve installation progress tracking
Browse files Browse the repository at this point in the history
  • Loading branch information
VilppeRiskidev committed Jan 28, 2025
1 parent a7b06bd commit 03af2b9
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 61 deletions.
31 changes: 25 additions & 6 deletions src/components/mixins/DownloadMixin.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
<script lang='ts'>
import Vue from 'vue';
import Component from 'vue-class-component';
import { Store } from "vuex";
import StatusEnum from "../../model/enums/StatusEnum";
import R2Error from "../../model/errors/R2Error";
import R2Error, { throwForR2Error } from "../../model/errors/R2Error";
import Game from "../../model/game/Game";
import Profile from "../../model/Profile";
import ManifestV2 from "../../model/ManifestV2";
import Profile, { ImmutableProfile } from "../../model/Profile";
import ThunderstoreCombo from "../../model/ThunderstoreCombo";
import ThunderstoreMod from "../../model/ThunderstoreMod";
import { installModsAndResolveConflicts } from "../../utils/ProfileUtils";
import { Store } from "vuex";
import ConflictManagementProvider from "../../providers/generic/installing/ConflictManagementProvider";
import ProfileModList from "../../r2mm/mods/ProfileModList";
import { installModsToProfile } from "../../utils/ProfileUtils";
@Component
Expand Down Expand Up @@ -46,8 +49,9 @@ export default class DownloadMixin extends Vue {
async downloadCompletedCallback(downloadedMods: ThunderstoreCombo[], assignId: number): Promise<void> {
try {
await installModsAndResolveConflicts(downloadedMods, this.profile.asImmutableProfile(), this.$store, assignId);
await this.installModsAndResolveConflicts(downloadedMods, this.profile.asImmutableProfile(), this.$store, assignId);
} catch (e) {
this.$store.commit('download/updateDownload', {assignId, failed: true});
this.$store.commit('error/handleError', R2Error.fromThrownValue(e));
}
}
Expand All @@ -61,7 +65,7 @@ export default class DownloadMixin extends Vue {
DownloadMixin.addSolutionsToError(err);
throw err;
}
} else if (status === StatusEnum.PENDING) {
} else if (status === StatusEnum.PENDING || status === StatusEnum.SUCCESS) {
this.$store.commit('download/updateDownload', {assignId, downloadProgress, modName});
}
} catch (e) {
Expand All @@ -81,6 +85,21 @@ export default class DownloadMixin extends Vue {
}
}
async installModsAndResolveConflicts(
downloadedMods: ThunderstoreCombo[],
profile: ImmutableProfile,
store: Store<any>,
assignId: number
): Promise<void> {
await ProfileModList.requestLock(async () => {
const modList: ManifestV2[] = await installModsToProfile(downloadedMods, profile, (status, progress) => {
store.commit('download/updateDownload', {assignId, installProgress: progress});
});
await store.dispatch('profile/updateModList', modList);
throwForR2Error(await ConflictManagementProvider.instance.resolveConflicts(modList, profile));
});
}
static addSolutionsToError(err: R2Error): void {
// Sanity check typing.
if (!(err instanceof R2Error)) {
Expand Down
6 changes: 2 additions & 4 deletions src/components/profiles-modals/ImportProfileModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ export default class ImportProfileModal extends mixins(ProfilesMixin) {
async importProfile(targetProfileName: string, mods: ExportMod[], zipPath: string) {
this.activeStep = 'PROFILE_IS_BEING_IMPORTED';
this.importPhaseDescription = 'Downloading mods: 0%';
const progressCallback = (progressText: string) => this.importPhaseDescription = progressText;
const progressCallback = (status: string) => this.importPhaseDescription = status;
const game = this.$store.state.activeGame;
const settings = this.$store.getters['settings'];
const ignoreCache = settings.getContext().global.ignoreCache;
Expand All @@ -223,9 +223,7 @@ export default class ImportProfileModal extends mixins(ProfilesMixin) {
await ThunderstoreDownloaderProvider.instance.downloadImportedMods(comboList, ignoreCache, (progress) => {
progressCallback(`Downloading mods: ${Math.floor(progress)}%`)
});
await ProfileUtils.populateImportedProfile(comboList, mods, targetProfileName, isUpdate, zipPath, (progress) => {
progressCallback(`Copying mods to profile: ${progress}%`)
});
await ProfileUtils.populateImportedProfile(comboList, mods, targetProfileName, isUpdate, zipPath, progressCallback);
} catch (e) {
await this.$store.dispatch('profiles/ensureProfileExists');
this.closeModal();
Expand Down
6 changes: 3 additions & 3 deletions src/components/views/DownloadModModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
:value="$store.getters['download/currentDownload'].downloadProgress"
:className="['is-dark']"
/>
<p v-if="$store.getters['download/currentDownload'].installationProgress">Installation: {{$store.getters['download/currentDownload'].installationProgress}}% complete</p>
<p v-else>Installation: waiting for download to finish</p>
<p v-if="$store.getters['download/currentDownload'].installProgress">Installing: {{$store.getters['download/currentDownload'].installProgress}}% complete</p>
<p v-else>Installing: waiting for download to finish</p>
<Progress
:max='100'
:value="$store.getters['download/currentDownload'].installationProgress"
:value="$store.getters['download/currentDownload'].installProgress"
:className="['is-dark']"
/>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/components/views/UpdateAllInstalledModsModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export default class UpdateAllInstalledModsModal extends mixins(DownloadMixin)
throw err;
}
} else if (status === StatusEnum.PENDING) {
this.$store.commit('download/updateDownload', {assignId, progress, modName});
this.$store.commit('download/updateDownload', {assignId, downloadProgress: progress, modName});
}
} catch (e) {
this.$store.commit('error/handleError', R2Error.fromThrownValue(e));
Expand Down
42 changes: 24 additions & 18 deletions src/pages/DownloadMonitor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,28 +20,34 @@
<div class="card is-shadowless">
<p><strong>{{ downloadObject.initialMods.join(", ") }}</strong></p>

<div v-if="!downloadObject.failed && downloadObject.downloadProgress < 100">
<p>Downloading: {{ downloadObject.modName }}</p>
<p>{{Math.min(Math.floor(downloadObject.downloadProgress), 100)}}% complete</p>
<Progress
:max='100'
:value='downloadObject.downloadProgress'
:className="['is-info']"
/>
</div>
<div class="row" v-if="!downloadObject.failed">

<div class="col">
<p v-if="downloadObject.downloadProgress < 100">Downloading: {{ downloadObject.modName }}</p>
<p v-else-if="downloadObject.installProgress < 100 && !downloadObject.failed">Downloading:</p>
<p>{{Math.min(Math.floor(downloadObject.downloadProgress), 100)}}% complete</p>
<Progress
:max='100'
:value='downloadObject.downloadProgress'
:className="['is-info']"
/>
</div>

<div v-if="downloadObject.installProgress < 100 && !downloadObject.failed" class="col">
<p v-if="downloadObject.installProgress < 100">Installing: {{ downloadObject.modName }}</p>
<p v-else>Installing:</p>
<p>{{Math.min(Math.floor(downloadObject.installProgress), 100)}}% complete</p>
<Progress
:max='100'
:value='downloadObject.installProgress'
:className="['is-info']"
/>
</div>

<div v-else-if="!downloadObject.failed && downloadObject.installationProgress < 100">
<p v-if="downloadObject.installationProgress < 100">Installing: {{ downloadObject.modName }}</p>
<p>{{Math.min(Math.floor(downloadObject.installationProgress), 100)}}% complete</p>
<Progress
:max='100'
:value='downloadObject.installationProgress'
:className="['is-info']"
/>
</div>

<div v-else-if="downloadObject.failed">
<p>{{downloadObject.failed ? "Download failed" : `${Math.min(Math.floor(downloadObject.installationProgress), 100)}% complete`}}</p>
<p>{{downloadObject.failed ? "Download failed" : `${Math.min(Math.floor(downloadObject.installProgress), 100)}% complete`}}</p>
<Progress
:max='100'
:value='100'
Expand Down
6 changes: 3 additions & 3 deletions src/store/modules/DownloadModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ interface DownloadProgress {
initialMods: string[];
modName: string;
downloadProgress: number;
installationProgress: number;
installProgress: number;
failed: boolean;
}

Expand Down Expand Up @@ -43,7 +43,7 @@ export const DownloadModule = {
initialMods,
modName: '',
downloadProgress: 0,
installationProgress: 0,
installProgress: 0,
failed: false,
};
state.allDownloads = [...state.allDownloads, downloadObject];
Expand All @@ -54,7 +54,7 @@ export const DownloadModule = {
getters: <GetterTree<State, RootState>>{
activeDownloadCount(state) {
const active = state.allDownloads.filter(
dl => !dl.failed && dl.downloadProgress < 100 && dl.installationProgress < 100
dl => !dl.failed && dl.downloadProgress < 100 && dl.installProgress < 100
);
return active.length;
},
Expand Down
34 changes: 8 additions & 26 deletions src/utils/ProfileUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export async function exportModsToCombos(exportMods: ExportMod[], game: Game): P
async function extractConfigsToImportedProfile(
file: string,
profileName: string,
progressCallback: (status: number) => void
progressCallback: (status: string) => void
) {
const zipEntries = await ZipProvider.instance.getEntries(file);
const excludedFiles = ["export.r2x", "mods.yml"];
Expand All @@ -54,25 +54,10 @@ async function extractConfigsToImportedProfile(
}

const progress = Math.floor(((index + 1) / zipEntries.length) * 100);
progressCallback(progress);
progressCallback(`Copying configs to profile: ${progress}%`);
}
}

export async function installModsAndResolveConflicts(
downloadedMods: ThunderstoreCombo[],
profile: ImmutableProfile,
store: Store<any>,
assignId: number
): Promise<void> {
await ProfileModList.requestLock(async () => {
const modList: ManifestV2[] = await installModsToProfile(downloadedMods, profile, undefined, (installationProgress) => {
store.commit('download/updateDownload', {assignId, installationProgress});
});
await store.dispatch('profile/updateModList', modList);
throwForR2Error(await ConflictManagementProvider.instance.resolveConflicts(modList, profile));
});
}

/**
* Install mods to target profile and sync the changes to mods.yml file
* This is more performant than calling ProfileModList.addMod() on a
Expand All @@ -81,8 +66,8 @@ export async function installModsAndResolveConflicts(
export async function installModsToProfile(
comboList: ThunderstoreCombo[],
profile: ImmutableProfile,
disabledModsOverride?: string[],
progressCallback?: (status: number) => void
progressCallback: (status: string, progress: number) => void,
disabledModsOverride?: string[]
): Promise<ManifestV2[]> {
const profileMods = await ProfileModList.getModList(profile);
if (profileMods instanceof R2Error) {
Expand Down Expand Up @@ -123,7 +108,7 @@ export async function installModsToProfile(

if (typeof progressCallback === "function") {
const progress = Math.floor(((index + 1) / comboList.length) * 100);
progressCallback(progress);
progressCallback(`Copying mods to profile: ${progress}%`, progress);
}
}
} catch (e) {
Expand All @@ -137,6 +122,7 @@ export async function installModsToProfile(
}

throwForR2Error(await ProfileModList.saveModList(profile, profileMods));
progressCallback('', 100);
return profileMods;
}

Expand Down Expand Up @@ -203,12 +189,8 @@ export async function populateImportedProfile(

try {
const disabledMods = exportModList.filter((m) => !m.isEnabled()).map((m) => m.getName());
await installModsToProfile(comboList, profile, disabledMods, (progress) => {
progressCallback(`Copying mods to profile: ${progress}%`);
});
await extractConfigsToImportedProfile(zipPath, profile.getProfileName(), (progress) => {
progressCallback(`Copying configs to profile: ${progress}%`);
});
await installModsToProfile(comboList, profile, progressCallback, disabledMods);
await extractConfigsToImportedProfile(zipPath, profile.getProfileName(), progressCallback);
} catch (e) {
await FileUtils.recursiveRemoveDirectoryIfExists(profile.getProfilePath());
throw e;
Expand Down

0 comments on commit 03af2b9

Please sign in to comment.