Skip to content

Commit 0232a6e

Browse files
committed
Harden course cover upload state handling
1 parent cae8650 commit 0232a6e

2 files changed

Lines changed: 27 additions & 10 deletions

File tree

src/app/courses/add-courses/courses-add.component.ts

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ export class CoursesAddComponent implements OnInit, OnDestroy {
8585
private stepsChange$ = new Subject<any[]>();
8686
private initialState = '';
8787
private _steps = [];
88+
private preserveCoverStateUntilSubmit = false;
8889
existingCoverAttachments: ExistingAttachment[] = [];
8990
private coverState: AttachmentInputState = { retained: [], removed: [], added: [] };
9091
savedCourse: any = null;
@@ -102,6 +103,7 @@ export class CoursesAddComponent implements OnInit, OnDestroy {
102103
languageNames = languages.map(list => list.name);
103104
mockStep = { stepTitle: $localize`Add title`, description: '!!!' };
104105
@ViewChild(CoursesStepComponent) coursesStepComponent: CoursesStepComponent;
106+
@ViewChild(FileUploadComponent) coverUploadComponent?: FileUploadComponent;
105107
get steps() {
106108
return this._steps;
107109
}
@@ -152,15 +154,16 @@ export class CoursesAddComponent implements OnInit, OnDestroy {
152154
this.draftExists = draft !== undefined;
153155
const doc = draft === undefined ? saved : draft;
154156
this.setInitialTags(tags, this.documentInfo, draft);
155-
if (!continued) {
157+
if (continued) {
158+
this.preserveCoverStateUntilSubmit = !!this.coursesService.course.coverState?.added?.length;
159+
this.setFormAndSteps(this.coursesService.course);
160+
this.setCoverState(this.coursesService.course.coverState || this.coverState);
161+
this.submitAddedExam();
162+
} else {
156163
this.setFormAndSteps({ form: doc, steps: doc.steps, tags: doc.tags, initialTags: this.coursesService.course.initialTags });
157164
this.setInitialState();
158165
}
159166
});
160-
if (continued) {
161-
this.setFormAndSteps(this.coursesService.course);
162-
this.submitAddedExam();
163-
}
164167
const returnRoute = this.router.createUrlTree([ '.', { continue: true } ], { relativeTo: this.route });
165168
this.coursesService.returnUrl = this.router.serializeUrl(returnRoute);
166169
this.coursesService.course = { form: this.courseForm.value, steps: this.steps };
@@ -267,7 +270,15 @@ export class CoursesAddComponent implements OnInit, OnDestroy {
267270
}
268271

269272
onCoverStateChange(state: AttachmentInputState) {
273+
if (this.preserveCoverStateUntilSubmit && this.coverState.added.length && state.added.length === 0) {
274+
return;
275+
}
276+
this.setCoverState(state);
277+
}
278+
279+
setCoverState(state: AttachmentInputState) {
270280
this.coverState = state;
281+
this.coursesService.course = { coverState: state };
271282
}
272283

273284
setExistingCover(course: any) {
@@ -279,7 +290,7 @@ export class CoursesAddComponent implements OnInit, OnDestroy {
279290
url: couchAttachmentUrl(environment.couchAddress, this.dbName, course._id, fileName)
280291
} ] : [];
281292
// Seed cover state directly so a save can't drop the cover if it fires before the upload child emits.
282-
this.coverState = { retained: [ ...this.existingCoverAttachments ], removed: [], added: [] };
293+
this.setCoverState({ retained: [ ...this.existingCoverAttachments ], removed: [], added: [] });
283294
}
284295

285296
updateCourse(courseInfo: FormGroup<CourseFormModel>['value'], shouldNavigate: boolean) {
@@ -329,7 +340,9 @@ export class CoursesAddComponent implements OnInit, OnDestroy {
329340
courseRes;
330341
const message = (this.pageType === 'Edit' ? $localize`Edited course: ` : $localize`Added course: `) + courseInfo.courseTitle;
331342
this.courseChangeComplete(message, savedRes, shouldNavigate);
343+
this.preserveCoverStateUntilSubmit = false;
332344
}, (err) => {
345+
this.preserveCoverStateUntilSubmit = false;
333346
this.planetMessageService.showAlert($localize`There was an error saving this course`);
334347
});
335348
}
@@ -381,6 +394,7 @@ export class CoursesAddComponent implements OnInit, OnDestroy {
381394
if (!this.draftExists) {
382395
return;
383396
}
397+
this.coverUploadComponent?.clear();
384398
if (this.savedCourse) {
385399
this.setFormAndSteps({
386400
form: this.savedCourse,
@@ -395,7 +409,7 @@ export class CoursesAddComponent implements OnInit, OnDestroy {
395409
tags: []
396410
});
397411
this.existingCoverAttachments = [];
398-
this.coverState = { retained: [], removed: [], added: [] };
412+
this.setCoverState({ retained: [], removed: [], added: [] });
399413
}
400414
this.coursesStepComponent.toList();
401415
this.setInitialState();

src/app/shared/utils.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,11 @@ export const safeAttachmentName = (name: string, usedNames: string[] = []): stri
4848
return nextName;
4949
};
5050

51-
export const couchAttachmentUrl = (baseUrl: string, dbName: string, docId: string, attachmentName: string): string =>
52-
`${baseUrl}/${dbName}/${encodeURIComponent(docId)}/${encodeURIComponent(attachmentName)}`;
51+
export const couchAttachmentUrl = (baseUrl: string, dbName: string, docId: string, attachmentName: string): string => {
52+
const trimmedBaseUrl = baseUrl.replace(/\/+$/, '');
53+
const trimmedDbName = dbName.replace(/^\/+|\/+$/g, '');
54+
return `${trimmedBaseUrl}/${trimmedDbName}/${encodeURIComponent(docId)}/${encodeURIComponent(attachmentName)}`;
55+
};
5356

5457
export interface NormalizeImageOptions {
5558
maxDimension?: number;
@@ -97,7 +100,7 @@ const encodedImage = async (canvas: HTMLCanvasElement, quality: number): Promise
97100

98101
// Browser-side cover/image normalization: bounds replicated payloads while keeping upload UX permissive.
99102
export const normalizeImage = async (file: File, opts: NormalizeImageOptions = {}): Promise<NormalizedImage> => {
100-
const maxDimension = opts.maxDimension || 600;
103+
const maxDimension = opts.maxDimension ?? 600;
101104
const quality = opts.quality ?? 0.82;
102105
const fallback = (): NormalizedImage => ({
103106
file,

0 commit comments

Comments
 (0)