Skip to content

Commit

Permalink
Use Vuex to manage error modal
Browse files Browse the repository at this point in the history
- Main benefit is that emitted errors no longer need to bubble all the
  way to the App.vue to get shown. This should prevent situations where
  refactoring components distrupts the emit chain and leaves the error
  unshown
- Refactors the modal to a single standalone component
- Fixes the odd naming where R2Error's "name" was stored in variable
  called "message", and "message" was stored in "stack" variable
- Unifies some logged messages from "name\n-> message" format to
  "[name]: message" format
  • Loading branch information
anttimaki committed Feb 16, 2024
1 parent d3542e8 commit 80ff3c7
Show file tree
Hide file tree
Showing 16 changed files with 181 additions and 175 deletions.
29 changes: 8 additions & 21 deletions src/App.vue
Original file line number Diff line number Diff line change
@@ -1,25 +1,7 @@
<template>
<div>

<router-view @error="showError" v-if="visible"/>

<div id='errorModal' :class="['modal', 'z-top', {'is-active':(errorMessage !== '')}]">
<div class="modal-background" @click="closeErrorModal()"></div>
<div class='modal-content'>
<div class='notification is-danger'>
<h3 class='title'>Error</h3>
<h5 class="title is-5">{{errorMessage}}</h5>
<p>{{errorStack}}</p>
<div v-if="errorSolution !== ''">
<br/>
<h5 class="title is-5">Suggestion</h5>
<p>{{errorSolution}}</p>
</div>
</div>
</div>
<button class="modal-close is-large" aria-label="close" @click="closeErrorModal()"></button>
</div>

<router-view v-if="visible"/>
<ErrorModal />
</div>
</template>

Expand Down Expand Up @@ -64,8 +46,13 @@ import GenericProfileInstaller from './r2mm/installing/profile_installers/Generi
import ConnectionProviderImpl from './r2mm/connection/ConnectionProviderImpl';
import ConnectionProvider from './providers/generic/connection/ConnectionProvider';
import UtilityMixin from './components/mixins/UtilityMixin.vue';
import ErrorModal from './components/modals/ErrorModal.vue';
@Component
@Component({
components: {
ErrorModal,
}
})
export default class App extends mixins(UtilityMixin) {
private settings: ManagerSettings | null = null;
private visible: boolean = false;
Expand Down
4 changes: 2 additions & 2 deletions src/components/config-components/ConfigSelectionLayout.vue
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,9 @@ import ProfileModList from '../../r2mm/mods/ProfileModList';
this.configFiles = this.configFiles.filter(value => value.getName() !== file.getName());
this.textChanged();
} catch (e) {
this.$emit("error", new R2Error(
this.$store.commit("openErrorModal", R2Error.fromThrownValue(
e,
"Failed to delete config file",
(e as Error).message,
`Try running ${ManagerInformation.APP_NAME} as an administrator.`
));
}
Expand Down
8 changes: 2 additions & 6 deletions src/components/importing/LocalFileImportModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -289,12 +289,12 @@ export default class LocalFileImportModal extends Vue {
const installCallback = (async (success: boolean, error: any | null) => {
if (!success && error !== null) {
this.showError(error);
this.$store.commit("openErrorModal", R2Error.fromThrownValue(error));
return;
}
const updatedModListResult = await ProfileModList.getModList(this.contextProfile!);
if (updatedModListResult instanceof R2Error) {
this.showError(updatedModListResult);
this.$store.commit("openErrorModal", updatedModListResult);
return;
}
await this.$store.dispatch("updateModList", updatedModListResult);
Expand All @@ -308,10 +308,6 @@ export default class LocalFileImportModal extends Vue {
}
}
private showError(err: R2Error) {
this.$emit("error", err);
}
created() {
this.contextProfile = Profile.getActiveProfile();
}
Expand Down
19 changes: 1 addition & 18 deletions src/components/mixins/UtilityMixin.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,15 @@ import R2Error from '../../model/errors/R2Error';
import GameManager from '../../model/game/GameManager';
import Profile from '../../model/Profile';
import CdnProvider from '../../providers/generic/connection/CdnProvider';
import LoggerProvider, { LogSeverity } from '../../providers/ror2/logging/LoggerProvider';
import ThunderstorePackages from '../../r2mm/data/ThunderstorePackages';
import ProfileModList from '../../r2mm/mods/ProfileModList';
import ApiCacheUtils from '../../utils/ApiCacheUtils';
@Component
export default class UtilityMixin extends Vue {
private errorMessage: string = '';
private errorStack: string = '';
private errorSolution: string = '';
readonly REFRESH_INTERVAL = 5 * 60 * 1000;
private tsRefreshFailed = false;
showError(error: R2Error) {
this.errorMessage = error.name;
this.errorStack = error.message;
this.errorSolution = error.solution;
LoggerProvider.instance.Log(LogSeverity.ERROR, `[${error.name}]: ${error.message}`);
}
closeErrorModal() {
this.errorMessage = '';
this.errorStack = '';
this.errorSolution = '';
}
hookProfileModListRefresh() {
setInterval(this.refreshProfileModList, this.REFRESH_INTERVAL);
}
Expand Down Expand Up @@ -101,7 +84,7 @@ export default class UtilityMixin extends Vue {
await CdnProvider.checkCdnConnection();
} catch (error: unknown) {
if (error instanceof R2Error) {
this.showError(error);
this.$store.commit('openErrorModal', error);
} else {
console.error(error);
}
Expand Down
55 changes: 55 additions & 0 deletions src/components/modals/ErrorModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<script lang="ts">
import { Component, Vue } from "vue-property-decorator";
import R2Error from "../../model/errors/R2Error";
@Component
export default class ErrorModal extends Vue {
get error(): R2Error | null {
return this.$store.state.modals.errorModalError;
}
get isOpen(): boolean {
return this.$store.state.modals.isErrorModalOpen && this.error !== null;
}
get name() {
return this.error ? this.error.name : '';
}
get message() {
return this.error ? this.error.message : '';
}
get solution() {
return this.error ? this.error.solution : '';
}
close() {
this.$store.commit('closeErrorModal');
}
}
</script>

<template>
<div v-if="isOpen" id="errorModal" class="modal z-top is-active">
<div class="modal-background" @click="close"></div>
<div class="modal-content">
<div class="notification is-danger">
<h3 class="title">Error</h3>
<h5 class="title is-5">{{name}}</h5>
<p>{{message}}</p>
<div v-if="solution">
<h5 class="title is-5">Suggestion</h5>
<p>{{solution}}</p>
</div>
</div>
</div>
<button class="modal-close is-large" aria-label="close" @click="close"></button>
</div>
</template>

<style scoped lang="scss">
p + div {
margin-top: 1.5rem;
}
</style>
4 changes: 2 additions & 2 deletions src/components/navigation/NavigationLayout.vue
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<template>
<div id="content" class="columns">
<div class="column non-selectable is-one-quarter">
<NavigationMenu @error="$emit('error', $event)" />
<NavigationMenu />
</div>
<div class="column">
<router-view @error="$emit('error', $event)" />
<router-view />
</div>
<GameRunningModal :activeGame="activeGame" />
</div>
Expand Down
9 changes: 2 additions & 7 deletions src/components/navigation/NavigationMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -111,14 +111,9 @@ export default class NavigationMenu extends Vue {
this.$store.commit("openGameRunningModal");
await launch(this.activeGame, this.contextProfile!, mode);
} catch (error) {
if (error instanceof R2Error) {
this.$store.commit("closeGameRunningModal");
this.$emit("error", error);
} else {
throw error;
}
this.$store.commit("closeGameRunningModal");
this.$store.commit("openErrorModal", R2Error.fromThrownValue(error));
}
}
created() {
Expand Down
10 changes: 5 additions & 5 deletions src/components/views/DownloadModModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ let assignId = 0;
const localMods = await ProfileModList.getModList(this.contextProfile!);
if (localMods instanceof R2Error) {
this.downloadingMod = false;
this.$emit('error', localMods);
this.$store.commit('openErrorModal', localMods);
return;
}
const outdatedMods = localMods.filter(mod => !ModBridge.isCachedLatestVersion(mod));
Expand All @@ -270,7 +270,7 @@ let assignId = 0;
const existing = DownloadModModal.allVersions[assignIndex]
existing[1].failed = true;
this.$set(DownloadModModal.allVersions, assignIndex, [currentAssignId, existing[1]]);
this.$emit('error', err);
this.$store.commit('openErrorModal', err);
return;
}
} else if (status === StatusEnum.PENDING) {
Expand Down Expand Up @@ -302,7 +302,7 @@ let assignId = 0;
await this.$store.dispatch('updateModList', modList);
const err = await ConflictManagementProvider.instance.resolveConflicts(modList, this.contextProfile!);
if (err instanceof R2Error) {
this.$emit('error', err);
this.$store.commit('openErrorModal', err);
}
}
});
Expand Down Expand Up @@ -331,7 +331,7 @@ let assignId = 0;
const existing = DownloadModModal.allVersions[assignIndex]
existing[1].failed = true;
this.$set(DownloadModModal.allVersions, assignIndex, [currentAssignId, existing[1]]);
this.$emit('error', err);
this.$store.commit('openErrorModal', err);
return;
}
} else if (status === StatusEnum.PENDING) {
Expand Down Expand Up @@ -363,7 +363,7 @@ let assignId = 0;
await this.$store.dispatch('updateModList', modList);
const err = await ConflictManagementProvider.instance.resolveConflicts(modList, this.contextProfile!);
if (err instanceof R2Error) {
this.$emit('error', err);
this.$store.commit('openErrorModal', err);
}
}
});
Expand Down
4 changes: 1 addition & 3 deletions src/components/views/InstalledModView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@
</div>
</div>
<template v-else-if="localModList.length > 0">
<LocalModList
@error="$emit('error', $event)">
<LocalModList>
<template v-slot:above-list v-if="numberOfModsWithUpdates > 0 && !dismissedUpdateAll">
<div class="margin-bottom">
<div class="notification is-warning margin-right">
Expand All @@ -42,7 +41,6 @@ import Component from "vue-class-component";
import ManifestV2 from "../../model/ManifestV2";
import LocalModListProvider from "../../providers/components/loaders/LocalModListProvider";
import ThunderstoreDownloaderProvider from "../../providers/ror2/downloading/ThunderstoreDownloaderProvider";
@Component({
components: {
Expand Down
Loading

0 comments on commit 80ff3c7

Please sign in to comment.