Skip to content

Commit b5e6b49

Browse files
committed
- Improved profile verification to allow specific error messages.
- Default game plugins are now shown in base profiles. - An icon is now shown when a config file is inherited from a base profile. - Prevented ability to add new mods with same name as mod in base profile. - Profile verification error is now triggered if a child profile has a mod with same name as parent profile. - Mods can no longer be renamed to a mod with a name that already exists.
1 parent b77bbbc commit b5e6b49

File tree

11 files changed

+285
-159
lines changed

11 files changed

+285
-159
lines changed

src/app/components/profile-mod-list/profile-mod-list.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@
2727
<mat-icon class="mod-base-profile-icon" color="accent">arrow_outward</mat-icon>
2828
</span>
2929

30-
<mat-icon *ngIf="!!modEntry.modRef.verificationError"
30+
<mat-icon *ngIf="!!modEntry.modRef.verificationError?.error"
3131
class="mod-verification-error-icon"
32-
matTooltip="This mod has a verification error">
32+
[matTooltip]="modEntry.modRef.verificationError.reason ?? 'This mod has a verification error'">
3333
error
3434
</mat-icon>
3535
</div>

src/app/modals/profile-verification-results/profile-verification-results.modal.ts

Lines changed: 41 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import { LangUtils } from "../../util/lang-utils";
2121
})
2222
export class AppProfileVerificationResultsModal extends BaseComponent {
2323

24-
private static readonly VERIFICATION_ERROR_DESCS: Record<keyof AppProfile.VerificationResults, string> = {
24+
private static readonly PROFILE_VERIFICATION_ERROR_DESCS: Record<keyof AppProfile, string> = {
2525
name: "Invalid profile name",
2626
gameId: "Invalid Game ID",
2727
mods: "Invalid or missing mod files",
@@ -49,42 +49,62 @@ export class AppProfileVerificationResultsModal extends BaseComponent {
4949
externalFilesCache: "Invalid external files",
5050
baseProfile: "Invalid Base Profile",
5151
customGameActions: "Invalid custom game actions",
52-
activeGameAction: "Invalid active game action",
53-
error: "Invalid Profile",
54-
found: "Invalid Profile"
52+
activeGameAction: "Invalid active game action"
5553
};
5654

57-
private static readonly BLACKLISTED_ERROR_KEYS: Array<keyof AppProfile.VerificationResults> = [
58-
"error",
59-
"found"
60-
];
55+
private static readonly VERIFICATION_ERROR_DESCS: Record<string, string> = {
56+
...AppProfileVerificationResultsModal.PROFILE_VERIFICATION_ERROR_DESCS
57+
};
58+
59+
private static readonly BLACKLISTED_ERROR_KEYS: Array<string> = [];
6160

6261
protected readonly errors: string[];
6362

6463
constructor(
6564
cdRef: ChangeDetectorRef,
6665
protected readonly snackBarRef: MatSnackBarRef<AppProfileVerificationResultsModal>,
67-
@Inject(MAT_SNACK_BAR_DATA) verificationResults: AppProfile.VerificationResults
66+
@Inject(MAT_SNACK_BAR_DATA) verificationResults: AppProfile.VerificationResultRecord<string>
6867
) {
6968
super({ cdRef });
7069

71-
this.errors = LangUtils.entries(verificationResults).reduce<string[]>((errors, [propertyKey, verificationResult]) => {
72-
const hasError = [
73-
propertyKey === "error" && verificationResult,
74-
propertyKey === "found" && !verificationResult,
75-
typeof verificationResult !== "boolean" && verificationResult.error,
76-
].some(Boolean);
70+
this.errors = this.collectErrors(verificationResults);
71+
}
72+
73+
private collectErrors<K extends string>(results: AppProfile.VerificationResultRecord<K>, logOnly: boolean = false): string[] {
74+
return LangUtils.entries(results).reduce<string[]>((errors, [propertyKey, verificationResult]) => {
75+
if (!AppProfileVerificationResultsModal.BLACKLISTED_ERROR_KEYS.includes(propertyKey)) {
76+
const errorText = this.mapVerificationResultToError(propertyKey, verificationResult);
77+
78+
if (errorText && !errors.includes(errorText)) {
79+
log.error(`Profile verification error:`, propertyKey, errorText, verificationResult);
7780

78-
if (hasError && !AppProfileVerificationResultsModal.BLACKLISTED_ERROR_KEYS.includes(propertyKey)) {
79-
const errorText = AppProfileVerificationResultsModal.VERIFICATION_ERROR_DESCS[propertyKey];
81+
if (!logOnly) {
82+
errors.push(errorText);
83+
}
84+
}
8085

81-
if (!errors.includes(errorText)) {
82-
log.error(`Profile verification error:`, errorText, verificationResult);
83-
errors.push(errorText);
86+
if ("results" in verificationResult) {
87+
errors.push(...this.collectErrors(verificationResult.results, true));
8488
}
8589
}
86-
90+
8791
return errors;
8892
}, []);
8993
}
94+
95+
private mapVerificationResultToError(propertyName: string, result: AppProfile.VerificationResult): string | undefined {
96+
if (result.error) {
97+
return this.getErrorText(propertyName, result);
98+
}
99+
100+
return undefined;
101+
}
102+
103+
private getErrorText(propertyName: string, result: AppProfile.VerificationResult): string {
104+
if (!!result.reason) {
105+
return result.reason;
106+
} else {
107+
return AppProfileVerificationResultsModal.VERIFICATION_ERROR_DESCS[propertyName as keyof AppProfile] ?? "Error";
108+
}
109+
}
90110
}

src/app/models/app-message.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,7 @@ export namespace AppMessage {
509509
export interface ReadConfigFile extends Base {
510510
id: `${ProfileMessage.Prefix}:readConfigFile`;
511511
data: {
512-
profile: AppProfile;
512+
profile: AppBaseProfile;
513513
fileName: string;
514514
loadDefaults: boolean;
515515
};

src/app/models/app-profile.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,9 @@ export interface AppProfile extends AppBaseProfile {
4141
export type AppProfileModList = AppProfile.ModList;
4242
export type AppProfileForm = AppProfile.Form;
4343
export type AppProfileDefaultablePaths = AppProfile.DefaultablePaths;
44+
export type AppProfileCollectedVerificationResult = AppProfile.CollectedVerificationResult;
4445
export type AppProfileVerificationResult = AppProfile.VerificationResult;
4546
export type AppProfileVerificationResults = AppProfile.VerificationResults;
46-
export type AppProfileModVerificationResults = AppProfile.ModVerificationResults;
4747
export type AppProfilePluginBackupEntry = AppProfile.PluginBackupEntry;
4848
export type AppProfileDescription = AppProfile.Description;
4949
export type AppProfileExternalFiles = AppProfile.ExternalFiles;
@@ -77,24 +77,27 @@ export namespace AppProfile {
7777
export interface VerificationResult {
7878
error: boolean;
7979
found: boolean;
80+
reason?: string;
8081
}
8182

83+
export type VerificationResultRecord<K extends string | number | symbol = string> = Record<
84+
K,
85+
VerificationResult | CollectedVerificationResult
86+
>;
87+
8288
export interface CollectedVerificationResult<K extends string | number | symbol = string> extends VerificationResult {
83-
results: Record<K, VerificationResult>;
89+
results: VerificationResultRecord<K>;
8490
}
8591

86-
export type BaseVerificationResults = VerificationResult & {
87-
[K in keyof Required<AppProfile>]: VerificationResult;
88-
};
89-
90-
export interface VerificationResults extends BaseVerificationResults {
92+
export interface PropertiesVerificationResult extends VerificationResultRecord<keyof AppProfile> {
9193
mods: CollectedVerificationResult;
9294
rootMods: CollectedVerificationResult;
9395
plugins: CollectedVerificationResult;
9496
}
9597

96-
export type ModVerificationResults = VerificationResults["mods"];
97-
export type PluginVerificationResults = VerificationResults["plugins"];
98+
export interface VerificationResults extends VerificationResult {
99+
properties: PropertiesVerificationResult;
100+
}
98101

99102
export interface PluginBackupEntry {
100103
filePath: string;

src/app/models/mod-import-status.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { AppProfile } from "./app-profile";
22
import { ModProfileRef } from "./mod-profile-ref";
33
import { ModInstaller } from "./mod-installer";
44

5-
export type ModImportStatus = "FAILED" | "PENDING" | "MANUALINSTALL" | "CANCELED";
5+
export type ModImportStatus = "FAILED" | "PENDING" | "MANUALINSTALL" | "CANCELED" | "RESTART";
66
export type ModImportMergeStrategy = "REPLACE" | "OVERWRITE" | "ADD";
77

88
export interface ModImportRequest {

src/app/pages/mods-overview/mods-overview.page.html

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -181,15 +181,28 @@
181181
@case ("config") {
182182
<!-- Config File Editor -->
183183
<ng-container *ngIf="gameDetails?.gameConfigFiles as configFiles">
184+
@let lastUpdateTime = configFileUpdate$ | async;
185+
@let profileConfigData = activeProfile! | appProfileConfigFile$:activeConfigFileModel.value:lastUpdateTime | async;
186+
@let baseProfileConfigData = activeProfile!.baseProfile | appProfileConfigFile$:activeConfigFileModel.value:lastUpdateTime | async;
187+
@let resolvedConfigData = activeProfile! | appProfileConfigFile$:activeConfigFileModel.value:null:true | async;
188+
184189
<!-- File selector -->
185190
<mat-form-field [attr.subscript]="false">
186191
<mat-select #activeConfigFileModel="ngModel"
187192
[ngModel]="(configFiles | keyvalue)[0].key"
188193
(ngModelChange)="configFileDataModel.reset()">
189194
<mat-select-trigger>
190195
<span class="config-file-name"
191-
[attr.new-file]="(activeProfile! | appProfileConfigFile$:activeConfigFileModel.value:(configFileUpdate$ | async) | async) === undefined">
196+
[attr.new-file]="profileConfigData === undefined">
192197
{{ activeConfigFileModel.value }}
198+
199+
@if (!!baseProfileConfigData && !profileConfigData) {
200+
<mat-icon color="accent"
201+
class="base-profile-marker"
202+
matTooltip="Inherited from {{activeProfile!.baseProfile!.name}}">
203+
arrow_outward
204+
</mat-icon>
205+
}
193206
</span>
194207
<span *ngIf="configFileDataModel.dirty" matColor="accent">*</span>
195208
</mat-select-trigger>
@@ -204,12 +217,12 @@
204217
class="config-file-editor"
205218
autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
206219
[disabled]="!activeConfigFileModel.value"
207-
[ngModel]="activeProfile! | appProfileConfigFile$:activeConfigFileModel.value:null:true | async">
220+
[ngModel]="resolvedConfigData">
208221
</textarea>
209222

210223
<!-- File actions -->
211224
<div class="config-file-actions">
212-
<button *ngIf="(activeProfile! | appProfileConfigFile$:activeConfigFileModel.value:(configFileUpdate$ | async) | async) !== undefined"
225+
<button *ngIf="profileConfigData !== undefined"
213226
mat-icon-button
214227
matTooltip="Open in external file editor"
215228
matColor="accent"

src/app/pages/mods-overview/mods-overview.page.scss

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,20 @@
184184
white-space: pre;
185185
}
186186

187-
.config-file-name[new-file="true"] {
188-
font-style: italic;
187+
.config-file-name {
188+
display: flex;
189+
align-items: center;
190+
191+
&[new-file="true"] {
192+
font-style: italic;
193+
}
194+
195+
.base-profile-marker {
196+
margin-left: 0.5rem;
197+
font-style: normal;
198+
font-weight: 600;
199+
@include mixins.mat-icon-size(0.95rem);
200+
}
189201
}
190202

191203
.config-file-actions {

src/app/pipes/profile-config-file.pipe.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import { Pipe, PipeTransform } from "@angular/core";
22
import { NEVER, Observable } from "rxjs";
33
import { ElectronUtils } from "../util/electron-utils";
4-
import { AppProfile } from "../models/app-profile";
4+
import { AppBaseProfile } from "../models/app-profile";
55

66
@Pipe({
77
name: "appProfileConfigFile$"
88
})
99
export class AppProfileConfigFilePipe implements PipeTransform {
1010

1111
public transform(
12-
profile: AppProfile | undefined | null,
12+
profile: AppBaseProfile | undefined | null,
1313
configFileName: string | undefined,
1414
_lastUpdate?: Date | null,
1515
loadDefaults: boolean = false

0 commit comments

Comments
 (0)