forked from EVOgeek/Vmeda.Online
		
	MOBILE-4263 quiz: Remove non-null assertions
This commit is contained in:
		
							parent
							
								
									4792e47737
								
							
						
					
					
						commit
						9744eb5137
					
				| @ -333,15 +333,16 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp | |||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         const bestGrade = this.bestGrade.grade; | ||||||
|         const formattedGradebookGrade = AddonModQuiz.formatGrade(this.gradebookData.grade, quiz.decimalpoints); |         const formattedGradebookGrade = AddonModQuiz.formatGrade(this.gradebookData.grade, quiz.decimalpoints); | ||||||
|         const formattedBestGrade = AddonModQuiz.formatGrade(this.bestGrade.grade, quiz.decimalpoints); |         const formattedBestGrade = AddonModQuiz.formatGrade(bestGrade, quiz.decimalpoints); | ||||||
|         let gradeToShow = formattedGradebookGrade; // By default we show the grade in the gradebook.
 |         let gradeToShow = formattedGradebookGrade; // By default we show the grade in the gradebook.
 | ||||||
| 
 | 
 | ||||||
|         this.showResults = true; |         this.showResults = true; | ||||||
|         this.gradeOverridden = formattedGradebookGrade != formattedBestGrade; |         this.gradeOverridden = formattedGradebookGrade != formattedBestGrade; | ||||||
|         this.gradebookFeedback = this.gradebookData.feedback; |         this.gradebookFeedback = this.gradebookData.feedback; | ||||||
| 
 | 
 | ||||||
|         if (this.bestGrade.grade! > this.gradebookData.grade && this.gradebookData.grade == quiz.grade) { |         if (bestGrade && bestGrade > this.gradebookData.grade && this.gradebookData.grade == quiz.grade) { | ||||||
|             // The best grade is higher than the max grade for the quiz.
 |             // The best grade is higher than the max grade for the quiz.
 | ||||||
|             // We'll do like Moodle web and show the best grade instead of the gradebook grade.
 |             // We'll do like Moodle web and show the best grade instead of the gradebook grade.
 | ||||||
|             this.gradeOverridden = false; |             this.gradeOverridden = false; | ||||||
| @ -556,8 +557,16 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp | |||||||
|      * |      * | ||||||
|      * @returns Promise resolved when done. |      * @returns Promise resolved when done. | ||||||
|      */ |      */ | ||||||
|     protected sync(): Promise<AddonModQuizSyncResult> { |     protected async sync(): Promise<AddonModQuizSyncResult> { | ||||||
|         return AddonModQuizSync.syncQuiz(this.candidateQuiz!, true); |         if (!this.candidateQuiz) { | ||||||
|  |             return { | ||||||
|  |                 warnings: [], | ||||||
|  |                 attemptFinished: false, | ||||||
|  |                 updated: false, | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return AddonModQuizSync.syncQuiz(this.candidateQuiz, true); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -579,36 +588,31 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         const lastFinished = AddonModQuiz.getLastFinishedAttemptFromList(attempts); |         const lastFinished = AddonModQuiz.getLastFinishedAttemptFromList(attempts); | ||||||
|         const promises: Promise<unknown>[] = []; |         let openReview = false; | ||||||
| 
 | 
 | ||||||
|         if (this.autoReview && lastFinished && lastFinished.id >= this.autoReview.attemptId) { |         if (this.autoReview && lastFinished && lastFinished.id >= this.autoReview.attemptId) { | ||||||
|             // User just finished an attempt in offline and it seems it's been synced, since it's finished in online.
 |             // User just finished an attempt in offline and it seems it's been synced, since it's finished in online.
 | ||||||
|             // Go to the review of this attempt if the user hasn't left this view.
 |             // Go to the review of this attempt if the user hasn't left this view.
 | ||||||
|             if (!this.isDestroyed && this.isCurrentView) { |             if (!this.isDestroyed && this.isCurrentView) { | ||||||
|                 promises.push(this.goToAutoReview()); |                 openReview = true; | ||||||
|             } |             } | ||||||
|             this.autoReview = undefined; |             this.autoReview = undefined; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // Get combined review options.
 |         const [options] = await Promise.all([ | ||||||
|         promises.push(AddonModQuiz.getCombinedReviewOptions(quiz.id, { cmId: this.module.id }).then((options) => { |             AddonModQuiz.getCombinedReviewOptions(quiz.id, { cmId: this.module.id }), | ||||||
|  |             this.getQuizGrade(), | ||||||
|  |             openReview ? this.goToAutoReview() : undefined, | ||||||
|  |         ]); | ||||||
|  | 
 | ||||||
|         this.options = options; |         this.options = options; | ||||||
| 
 |  | ||||||
|             return; |  | ||||||
|         })); |  | ||||||
| 
 |  | ||||||
|         // Get best grade.
 |  | ||||||
|         promises.push(this.getQuizGrade()); |  | ||||||
| 
 |  | ||||||
|         await Promise.all(promises); |  | ||||||
| 
 |  | ||||||
|         const grade = this.gradebookData?.grade !== undefined ? this.gradebookData.grade : this.bestGrade?.grade; |         const grade = this.gradebookData?.grade !== undefined ? this.gradebookData.grade : this.bestGrade?.grade; | ||||||
|         const quizGrade = AddonModQuiz.formatGrade(grade, quiz.decimalpoints); |         const quizGrade = AddonModQuiz.formatGrade(grade, quiz.decimalpoints); | ||||||
| 
 | 
 | ||||||
|         // Calculate data to construct the header of the attempts table.
 |         // Calculate data to construct the header of the attempts table.
 | ||||||
|         AddonModQuizHelper.setQuizCalculatedData(quiz, this.options!); |         AddonModQuizHelper.setQuizCalculatedData(quiz, this.options); | ||||||
| 
 | 
 | ||||||
|         this.overallStats = !!lastFinished && this.options!.alloptions.marks >= AddonModQuizProvider.QUESTION_OPTIONS_MARK_AND_MAX; |         this.overallStats = !!lastFinished && this.options.alloptions.marks >= AddonModQuizProvider.QUESTION_OPTIONS_MARK_AND_MAX; | ||||||
| 
 | 
 | ||||||
|         // Calculate data to show for each attempt.
 |         // Calculate data to show for each attempt.
 | ||||||
|         const formattedAttempts = await Promise.all(attempts.map((attempt, index) => { |         const formattedAttempts = await Promise.all(attempts.map((attempt, index) => { | ||||||
|  | |||||||
| @ -61,6 +61,6 @@ export type AddonModQuizNavigationQuestion = CoreQuestionQuestionParsed & { | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export type AddonModQuizNavigationModalReturn = { | export type AddonModQuizNavigationModalReturn = { | ||||||
|     page?: number; |     page: number; | ||||||
|     slot?: number; |     slot?: number; | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -66,11 +66,13 @@ export class AddonModQuizPreflightModalComponent implements OnInit { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         try { |         try { | ||||||
|  |             const quiz = this.quiz; | ||||||
|  | 
 | ||||||
|             await Promise.all(this.rules.map(async (rule) => { |             await Promise.all(this.rules.map(async (rule) => { | ||||||
|                 // Check if preflight is required for rule and, if so, get the component to render it.
 |                 // Check if preflight is required for rule and, if so, get the component to render it.
 | ||||||
|                 const required = await AddonModQuizAccessRuleDelegate.isPreflightCheckRequiredForRule( |                 const required = await AddonModQuizAccessRuleDelegate.isPreflightCheckRequiredForRule( | ||||||
|                     rule, |                     rule, | ||||||
|                     this.quiz!, |                     quiz, | ||||||
|                     this.attempt, |                     this.attempt, | ||||||
|                     this.prefetch, |                     this.prefetch, | ||||||
|                     this.siteId, |                     this.siteId, | ||||||
|  | |||||||
| @ -94,20 +94,20 @@ export class AddonModQuizAttemptPage implements OnInit { | |||||||
|             // Load attempt data.
 |             // Load attempt data.
 | ||||||
|             const [options, accessInfo, attempt] = await Promise.all([ |             const [options, accessInfo, attempt] = await Promise.all([ | ||||||
|                 AddonModQuiz.getCombinedReviewOptions(this.quiz.id, { cmId: this.quiz.coursemodule }), |                 AddonModQuiz.getCombinedReviewOptions(this.quiz.id, { cmId: this.quiz.coursemodule }), | ||||||
|                 this.fetchAccessInfo(), |                 this.fetchAccessInfo(this.quiz), | ||||||
|                 this.fetchAttempt(), |                 this.fetchAttempt(this.quiz.id), | ||||||
|             ]); |             ]); | ||||||
| 
 | 
 | ||||||
|             // Set calculated data.
 |             // Set calculated data.
 | ||||||
|             this.showReviewColumn = accessInfo.canreviewmyattempts; |             this.showReviewColumn = accessInfo.canreviewmyattempts; | ||||||
|             AddonModQuizHelper.setQuizCalculatedData(this.quiz, options); |             AddonModQuizHelper.setQuizCalculatedData(this.quiz, options); | ||||||
| 
 | 
 | ||||||
|             this.attempt = await AddonModQuizHelper.setAttemptCalculatedData(this.quiz!, attempt, false, undefined, true); |             this.attempt = await AddonModQuizHelper.setAttemptCalculatedData(this.quiz, attempt, false, undefined, true); | ||||||
| 
 | 
 | ||||||
|             // Check if the feedback should be displayed.
 |             // Check if the feedback should be displayed.
 | ||||||
|             const grade = Number(this.attempt!.rescaledGrade); |             const grade = Number(this.attempt.rescaledGrade); | ||||||
| 
 | 
 | ||||||
|             if (this.quiz.showFeedbackColumn && AddonModQuiz.isAttemptFinished(this.attempt!.state) && |             if (this.quiz.showFeedbackColumn && AddonModQuiz.isAttemptFinished(this.attempt.state) && | ||||||
|                     options.someoptions.overallfeedback && !isNaN(grade)) { |                     options.someoptions.overallfeedback && !isNaN(grade)) { | ||||||
| 
 | 
 | ||||||
|                 // Feedback should be displayed, get the feedback for the grade.
 |                 // Feedback should be displayed, get the feedback for the grade.
 | ||||||
| @ -127,11 +127,12 @@ export class AddonModQuizAttemptPage implements OnInit { | |||||||
|     /** |     /** | ||||||
|      * Get the attempt. |      * Get the attempt. | ||||||
|      * |      * | ||||||
|  |      * @param quizId Quiz ID. | ||||||
|      * @returns Promise resolved when done. |      * @returns Promise resolved when done. | ||||||
|      */ |      */ | ||||||
|     protected async fetchAttempt(): Promise<AddonModQuizAttemptWSData> { |     protected async fetchAttempt(quizId: number): Promise<AddonModQuizAttemptWSData> { | ||||||
|         // Get all the attempts and search the one we want.
 |         // Get all the attempts and search the one we want.
 | ||||||
|         const attempts = await AddonModQuiz.getUserAttempts(this.quiz!.id, { cmId: this.cmId }); |         const attempts = await AddonModQuiz.getUserAttempts(quizId, { cmId: this.cmId }); | ||||||
| 
 | 
 | ||||||
|         const attempt = attempts.find(attempt => attempt.id == this.attemptId); |         const attempt = attempts.find(attempt => attempt.id == this.attemptId); | ||||||
| 
 | 
 | ||||||
| @ -148,10 +149,11 @@ export class AddonModQuizAttemptPage implements OnInit { | |||||||
|     /** |     /** | ||||||
|      * Get the access info. |      * Get the access info. | ||||||
|      * |      * | ||||||
|  |      * @param quiz Quiz instance. | ||||||
|      * @returns Promise resolved when done. |      * @returns Promise resolved when done. | ||||||
|      */ |      */ | ||||||
|     protected async fetchAccessInfo(): Promise<AddonModQuizGetQuizAccessInformationWSResponse> { |     protected async fetchAccessInfo(quiz: AddonModQuizQuizData): Promise<AddonModQuizGetQuizAccessInformationWSResponse> { | ||||||
|         const accessInfo = await AddonModQuiz.getQuizAccessInformation(this.quiz!.id, { cmId: this.cmId }); |         const accessInfo = await AddonModQuiz.getQuizAccessInformation(quiz.id, { cmId: this.cmId }); | ||||||
| 
 | 
 | ||||||
|         if (!accessInfo.canreviewmyattempts) { |         if (!accessInfo.canreviewmyattempts) { | ||||||
|             return accessInfo; |             return accessInfo; | ||||||
| @ -161,7 +163,7 @@ export class AddonModQuizAttemptPage implements OnInit { | |||||||
|         await CoreUtils.ignoreErrors(AddonModQuiz.invalidateAttemptReviewForPage(this.attemptId, -1)); |         await CoreUtils.ignoreErrors(AddonModQuiz.invalidateAttemptReviewForPage(this.attemptId, -1)); | ||||||
| 
 | 
 | ||||||
|         try { |         try { | ||||||
|             await AddonModQuiz.getAttemptReview(this.attemptId, { page: -1, cmId: this.quiz!.coursemodule }); |             await AddonModQuiz.getAttemptReview(this.attemptId, { page: -1, cmId: quiz.coursemodule }); | ||||||
|         } catch { |         } catch { | ||||||
|             // Error getting the review, assume the user cannot review the attempt.
 |             // Error getting the review, assume the user cannot review the attempt.
 | ||||||
|             accessInfo.canreviewmyattempts = false; |             accessInfo.canreviewmyattempts = false; | ||||||
| @ -202,7 +204,11 @@ export class AddonModQuizAttemptPage implements OnInit { | |||||||
|      * @returns Promise resolved when done. |      * @returns Promise resolved when done. | ||||||
|      */ |      */ | ||||||
|     async reviewAttempt(): Promise<void> { |     async reviewAttempt(): Promise<void> { | ||||||
|         CoreNavigator.navigate(`../../review/${this.attempt!.id}`); |         if (!this.attempt) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         CoreNavigator.navigate(`../../review/${this.attempt.id}`); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | |||||||
| @ -203,6 +203,10 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave { | |||||||
|      * @param button Clicked button. |      * @param button Clicked button. | ||||||
|      */ |      */ | ||||||
|     async behaviourButtonClicked(button: CoreQuestionBehaviourButton): Promise<void> { |     async behaviourButtonClicked(button: CoreQuestionBehaviourButton): Promise<void> { | ||||||
|  |         if (!this.quiz || !this.attempt) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         let modal: CoreIonLoadingElement | undefined; |         let modal: CoreIonLoadingElement | undefined; | ||||||
| 
 | 
 | ||||||
|         try { |         try { | ||||||
| @ -212,13 +216,13 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave { | |||||||
|             modal = await CoreDomUtils.showModalLoading('core.sending', true); |             modal = await CoreDomUtils.showModalLoading('core.sending', true); | ||||||
| 
 | 
 | ||||||
|             // Get the answers.
 |             // Get the answers.
 | ||||||
|             const answers = await this.prepareAnswers(); |             const answers = await this.prepareAnswers(this.quiz.coursemodule); | ||||||
| 
 | 
 | ||||||
|             // Add the clicked button data.
 |             // Add the clicked button data.
 | ||||||
|             answers[button.name] = button.value; |             answers[button.name] = button.value; | ||||||
| 
 | 
 | ||||||
|             // Behaviour checks are always in online.
 |             // Behaviour checks are always in online.
 | ||||||
|             await AddonModQuiz.processAttempt(this.quiz!, this.attempt!, answers, this.preflightData); |             await AddonModQuiz.processAttempt(this.quiz, this.attempt, answers, this.preflightData); | ||||||
| 
 | 
 | ||||||
|             this.reloadNavigation = true; // Data sent to server, navigation should be reloaded.
 |             this.reloadNavigation = true; // Data sent to server, navigation should be reloaded.
 | ||||||
| 
 | 
 | ||||||
| @ -230,7 +234,7 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave { | |||||||
|             this.content?.scrollToTop(); // Scroll top so the spinner is seen.
 |             this.content?.scrollToTop(); // Scroll top so the spinner is seen.
 | ||||||
| 
 | 
 | ||||||
|             try { |             try { | ||||||
|                 await this.loadPage(this.attempt!.currentpage!); |                 await this.loadPage(this.attempt.currentpage ?? 0); | ||||||
|             } finally { |             } finally { | ||||||
|                 this.loaded = true; |                 this.loaded = true; | ||||||
|                 if (scrollTop != -1) { |                 if (scrollTop != -1) { | ||||||
| @ -311,8 +315,8 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave { | |||||||
|             } |             } | ||||||
|         } catch (error) { |         } catch (error) { | ||||||
|             // If the user isn't seeing the summary, start the check again.
 |             // If the user isn't seeing the summary, start the check again.
 | ||||||
|             if (!this.showSummary) { |             if (!this.showSummary && this.quiz) { | ||||||
|                 this.autoSave.startCheckChangesProcess(this.quiz!, this.attempt, this.preflightData, this.offline); |                 this.autoSave.startCheckChangesProcess(this.quiz, this.attempt, this.preflightData, this.offline); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             CoreDomUtils.showErrorModalDefault(error, 'addon.mod_quiz.errorgetquestions', true); |             CoreDomUtils.showErrorModalDefault(error, 'addon.mod_quiz.errorgetquestions', true); | ||||||
| @ -351,7 +355,7 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave { | |||||||
|             this.offline = await AddonModQuiz.isLastAttemptOfflineUnfinished(this.quiz); |             this.offline = await AddonModQuiz.isLastAttemptOfflineUnfinished(this.quiz); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (this.quiz!.timelimit && this.quiz!.timelimit > 0) { |         if (this.quiz.timelimit && this.quiz.timelimit > 0) { | ||||||
|             this.readableTimeLimit = CoreTime.formatTime(this.quiz.timelimit); |             this.readableTimeLimit = CoreTime.formatTime(this.quiz.timelimit); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -394,11 +398,15 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave { | |||||||
|      * @returns Promise resolved when done. |      * @returns Promise resolved when done. | ||||||
|      */ |      */ | ||||||
|     async finishAttempt(userFinish?: boolean, timeUp?: boolean): Promise<void> { |     async finishAttempt(userFinish?: boolean, timeUp?: boolean): Promise<void> { | ||||||
|  |         if (!this.quiz || !this.attempt) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         let modal: CoreIonLoadingElement | undefined; |         let modal: CoreIonLoadingElement | undefined; | ||||||
| 
 | 
 | ||||||
|         try { |         try { | ||||||
|             // Show confirm if the user clicked the finish button and the quiz is in progress.
 |             // Show confirm if the user clicked the finish button and the quiz is in progress.
 | ||||||
|             if (!timeUp && this.attempt!.state == AddonModQuizProvider.ATTEMPT_IN_PROGRESS) { |             if (!timeUp && this.attempt.state == AddonModQuizProvider.ATTEMPT_IN_PROGRESS) { | ||||||
|                 await CoreDomUtils.showConfirm(Translate.instant('addon.mod_quiz.confirmclose')); |                 await CoreDomUtils.showConfirm(Translate.instant('addon.mod_quiz.confirmclose')); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
| @ -408,8 +416,8 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave { | |||||||
| 
 | 
 | ||||||
|             // Trigger an event to notify the attempt was finished.
 |             // Trigger an event to notify the attempt was finished.
 | ||||||
|             CoreEvents.trigger(AddonModQuizProvider.ATTEMPT_FINISHED_EVENT, { |             CoreEvents.trigger(AddonModQuizProvider.ATTEMPT_FINISHED_EVENT, { | ||||||
|                 quizId: this.quiz!.id, |                 quizId: this.quiz.id, | ||||||
|                 attemptId: this.attempt!.id, |                 attemptId: this.attempt.id, | ||||||
|                 synced: !this.offline, |                 synced: !this.offline, | ||||||
|             }, CoreSites.getCurrentSiteId()); |             }, CoreSites.getCurrentSiteId()); | ||||||
| 
 | 
 | ||||||
| @ -431,9 +439,13 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave { | |||||||
|      * @returns Promise resolved when done. |      * @returns Promise resolved when done. | ||||||
|      */ |      */ | ||||||
|     protected async fixSequenceChecks(): Promise<void> { |     protected async fixSequenceChecks(): Promise<void> { | ||||||
|  |         if (!this.attempt) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         // Get current page data again to get the latest sequencechecks.
 |         // Get current page data again to get the latest sequencechecks.
 | ||||||
|         const data = await AddonModQuiz.getAttemptData(this.attempt!.id, this.attempt!.currentpage!, this.preflightData, { |         const data = await AddonModQuiz.getAttemptData(this.attempt.id, this.attempt.currentpage ?? 0, this.preflightData, { | ||||||
|             cmId: this.quiz!.coursemodule, |             cmId: this.quiz?.coursemodule, | ||||||
|             readingStrategy: this.offline ? CoreSitesReadingStrategy.PREFER_CACHE : CoreSitesReadingStrategy.ONLY_NETWORK, |             readingStrategy: this.offline ? CoreSitesReadingStrategy.PREFER_CACHE : CoreSitesReadingStrategy.ONLY_NETWORK, | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
| @ -465,14 +477,14 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave { | |||||||
|      * Initializes the timer if enabled. |      * Initializes the timer if enabled. | ||||||
|      */ |      */ | ||||||
|     protected initTimer(): void { |     protected initTimer(): void { | ||||||
|         if (!this.attemptAccessInfo?.endtime || this.attemptAccessInfo.endtime < 0) { |         if (!this.quizAccessInfo || !this.attempt || !this.attemptAccessInfo?.endtime || this.attemptAccessInfo.endtime < 0) { | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // Quiz has an end time. Check if time left should be shown.
 |         // Quiz has an end time. Check if time left should be shown.
 | ||||||
|         const shouldShowTime = AddonModQuiz.shouldShowTimeLeft( |         const shouldShowTime = AddonModQuiz.shouldShowTimeLeft( | ||||||
|             this.quizAccessInfo!.activerulenames, |             this.quizAccessInfo.activerulenames, | ||||||
|             this.attempt!, |             this.attempt, | ||||||
|             this.attemptAccessInfo.endtime, |             this.attemptAccessInfo.endtime, | ||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
| @ -490,8 +502,12 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave { | |||||||
|      * @returns Promise resolved when done. |      * @returns Promise resolved when done. | ||||||
|      */ |      */ | ||||||
|     protected async loadPage(page: number): Promise<void> { |     protected async loadPage(page: number): Promise<void> { | ||||||
|         const data = await AddonModQuiz.getAttemptData(this.attempt!.id, page, this.preflightData, { |         if (!this.quiz || !this.attempt) { | ||||||
|             cmId: this.quiz!.coursemodule, |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         const data = await AddonModQuiz.getAttemptData(this.attempt.id, page, this.preflightData, { | ||||||
|  |             cmId: this.quiz.coursemodule, | ||||||
|             readingStrategy: this.offline ? CoreSitesReadingStrategy.PREFER_CACHE : CoreSitesReadingStrategy.ONLY_NETWORK, |             readingStrategy: this.offline ? CoreSitesReadingStrategy.PREFER_CACHE : CoreSitesReadingStrategy.ONLY_NETWORK, | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
| @ -523,7 +539,7 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave { | |||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
|         // Start looking for changes.
 |         // Start looking for changes.
 | ||||||
|         this.autoSave.startCheckChangesProcess(this.quiz!, this.attempt, this.preflightData, this.offline); |         this.autoSave.startCheckChangesProcess(this.quiz, this.attempt, this.preflightData, this.offline); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -532,23 +548,27 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave { | |||||||
|      * @returns Promise resolved when done. |      * @returns Promise resolved when done. | ||||||
|      */ |      */ | ||||||
|     protected async loadSummary(): Promise<void> { |     protected async loadSummary(): Promise<void> { | ||||||
|  |         if (!this.quiz || !this.attempt) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         this.summaryQuestions = []; |         this.summaryQuestions = []; | ||||||
| 
 | 
 | ||||||
|         this.summaryQuestions = await AddonModQuiz.getAttemptSummary(this.attempt!.id, this.preflightData, { |         this.summaryQuestions = await AddonModQuiz.getAttemptSummary(this.attempt.id, this.preflightData, { | ||||||
|             cmId: this.quiz!.coursemodule, |             cmId: this.quiz.coursemodule, | ||||||
|             loadLocal: this.offline, |             loadLocal: this.offline, | ||||||
|             readingStrategy: this.offline ? CoreSitesReadingStrategy.PREFER_CACHE : CoreSitesReadingStrategy.ONLY_NETWORK, |             readingStrategy: this.offline ? CoreSitesReadingStrategy.PREFER_CACHE : CoreSitesReadingStrategy.ONLY_NETWORK, | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|         this.showSummary = true; |         this.showSummary = true; | ||||||
|         this.canReturn = this.attempt!.state == AddonModQuizProvider.ATTEMPT_IN_PROGRESS && !this.attempt!.finishedOffline; |         this.canReturn = this.attempt.state == AddonModQuizProvider.ATTEMPT_IN_PROGRESS && !this.attempt.finishedOffline; | ||||||
|         this.preventSubmitMessages = AddonModQuiz.getPreventSubmitMessages(this.summaryQuestions); |         this.preventSubmitMessages = AddonModQuiz.getPreventSubmitMessages(this.summaryQuestions); | ||||||
| 
 | 
 | ||||||
|         this.dueDateWarning = AddonModQuiz.getAttemptDueDateWarning(this.quiz!, this.attempt!); |         this.dueDateWarning = AddonModQuiz.getAttemptDueDateWarning(this.quiz, this.attempt); | ||||||
| 
 | 
 | ||||||
|         // Log summary as viewed.
 |         // Log summary as viewed.
 | ||||||
|         CoreUtils.ignoreErrors( |         CoreUtils.ignoreErrors( | ||||||
|             AddonModQuiz.logViewAttemptSummary(this.attempt!.id, this.preflightData, this.quiz!.id, this.quiz!.name), |             AddonModQuiz.logViewAttemptSummary(this.attempt.id, this.preflightData, this.quiz.id, this.quiz.name), | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -558,9 +578,13 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave { | |||||||
|      * @returns Promise resolved when done. |      * @returns Promise resolved when done. | ||||||
|      */ |      */ | ||||||
|     protected async loadNavigation(): Promise<void> { |     protected async loadNavigation(): Promise<void> { | ||||||
|  |         if (!this.attempt) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         // We use the attempt summary to build the navigation because it contains all the questions.
 |         // We use the attempt summary to build the navigation because it contains all the questions.
 | ||||||
|         this.navigation = await AddonModQuiz.getAttemptSummary(this.attempt!.id, this.preflightData, { |         this.navigation = await AddonModQuiz.getAttemptSummary(this.attempt.id, this.preflightData, { | ||||||
|             cmId: this.quiz!.coursemodule, |             cmId: this.quiz?.coursemodule, | ||||||
|             loadLocal: this.offline, |             loadLocal: this.offline, | ||||||
|             readingStrategy: this.offline ? CoreSitesReadingStrategy.PREFER_CACHE : CoreSitesReadingStrategy.ONLY_NETWORK, |             readingStrategy: this.offline ? CoreSitesReadingStrategy.PREFER_CACHE : CoreSitesReadingStrategy.ONLY_NETWORK, | ||||||
|         }); |         }); | ||||||
| @ -602,21 +626,22 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave { | |||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         this.changePage(modalData.page!, true, modalData.slot); |         this.changePage(modalData.page, true, modalData.slot); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Prepare the answers to be sent for the attempt. |      * Prepare the answers to be sent for the attempt. | ||||||
|      * |      * | ||||||
|  |      * @param componentId Component ID. | ||||||
|      * @returns Promise resolved with the answers. |      * @returns Promise resolved with the answers. | ||||||
|      */ |      */ | ||||||
|     protected prepareAnswers(): Promise<CoreQuestionsAnswers> { |     protected prepareAnswers(componentId: number): Promise<CoreQuestionsAnswers> { | ||||||
|         return CoreQuestionHelper.prepareAnswers( |         return CoreQuestionHelper.prepareAnswers( | ||||||
|             this.questions, |             this.questions, | ||||||
|             this.getAnswers(), |             this.getAnswers(), | ||||||
|             this.offline, |             this.offline, | ||||||
|             this.component, |             this.component, | ||||||
|             this.quiz!.coursemodule, |             componentId, | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -629,18 +654,22 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave { | |||||||
|      * @returns Promise resolved when done. |      * @returns Promise resolved when done. | ||||||
|      */ |      */ | ||||||
|     protected async processAttempt(userFinish?: boolean, timeUp?: boolean, retrying?: boolean): Promise<void> { |     protected async processAttempt(userFinish?: boolean, timeUp?: boolean, retrying?: boolean): Promise<void> { | ||||||
|  |         if (!this.quiz || !this.attempt) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         // Get the answers to send.
 |         // Get the answers to send.
 | ||||||
|         let answers: CoreQuestionsAnswers = {}; |         let answers: CoreQuestionsAnswers = {}; | ||||||
| 
 | 
 | ||||||
|         if (!this.showSummary) { |         if (!this.showSummary) { | ||||||
|             answers = await this.prepareAnswers(); |             answers = await this.prepareAnswers(this.quiz.coursemodule); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         try { |         try { | ||||||
|             // Send the answers.
 |             // Send the answers.
 | ||||||
|             await AddonModQuiz.processAttempt( |             await AddonModQuiz.processAttempt( | ||||||
|                 this.quiz!, |                 this.quiz, | ||||||
|                 this.attempt!, |                 this.attempt, | ||||||
|                 answers, |                 answers, | ||||||
|                 this.preflightData, |                 this.preflightData, | ||||||
|                 userFinish, |                 userFinish, | ||||||
| @ -676,7 +705,7 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave { | |||||||
|             CoreForms.triggerFormSubmittedEvent(this.formElement, !this.offline, CoreSites.getCurrentSiteId()); |             CoreForms.triggerFormSubmittedEvent(this.formElement, !this.offline, CoreSites.getCurrentSiteId()); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return CoreQuestionHelper.clearTmpData(this.questions, this.component, this.quiz!.coursemodule); |         return CoreQuestionHelper.clearTmpData(this.questions, this.component, this.quiz.coursemodule); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -731,12 +760,16 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave { | |||||||
|      * @returns Promise resolved when done. |      * @returns Promise resolved when done. | ||||||
|      */ |      */ | ||||||
|     protected async startOrContinueAttempt(): Promise<void> { |     protected async startOrContinueAttempt(): Promise<void> { | ||||||
|  |         if (!this.quiz || !this.quizAccessInfo) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         let attempt = this.newAttempt ? undefined : this.lastAttempt; |         let attempt = this.newAttempt ? undefined : this.lastAttempt; | ||||||
| 
 | 
 | ||||||
|         // Get the preflight data and start attempt if needed.
 |         // Get the preflight data and start attempt if needed.
 | ||||||
|         attempt = await AddonModQuizHelper.getAndCheckPreflightData( |         attempt = await AddonModQuizHelper.getAndCheckPreflightData( | ||||||
|             this.quiz!, |             this.quiz, | ||||||
|             this.quizAccessInfo!, |             this.quizAccessInfo, | ||||||
|             this.preflightData, |             this.preflightData, | ||||||
|             attempt, |             attempt, | ||||||
|             this.offline, |             this.offline, | ||||||
| @ -745,8 +778,8 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave { | |||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
|         // Re-fetch attempt access information with the right attempt (might have changed because a new attempt was created).
 |         // Re-fetch attempt access information with the right attempt (might have changed because a new attempt was created).
 | ||||||
|         this.attemptAccessInfo = await AddonModQuiz.getAttemptAccessInformation(this.quiz!.id, attempt.id, { |         this.attemptAccessInfo = await AddonModQuiz.getAttemptAccessInformation(this.quiz.id, attempt.id, { | ||||||
|             cmId: this.quiz!.coursemodule, |             cmId: this.quiz.coursemodule, | ||||||
|             readingStrategy: this.offline ? CoreSitesReadingStrategy.PREFER_CACHE : CoreSitesReadingStrategy.ONLY_NETWORK, |             readingStrategy: this.offline ? CoreSitesReadingStrategy.PREFER_CACHE : CoreSitesReadingStrategy.ONLY_NETWORK, | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
| @ -756,7 +789,7 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave { | |||||||
| 
 | 
 | ||||||
|         if (this.attempt.state != AddonModQuizProvider.ATTEMPT_OVERDUE && !this.attempt.finishedOffline) { |         if (this.attempt.state != AddonModQuizProvider.ATTEMPT_OVERDUE && !this.attempt.finishedOffline) { | ||||||
|             // Attempt not overdue and not finished in offline, load page.
 |             // Attempt not overdue and not finished in offline, load page.
 | ||||||
|             await this.loadPage(this.attempt.currentpage!); |             await this.loadPage(this.attempt.currentpage ?? 0); | ||||||
| 
 | 
 | ||||||
|             this.initTimer(); |             this.initTimer(); | ||||||
|         } else { |         } else { | ||||||
|  | |||||||
| @ -112,7 +112,7 @@ export class AddonModQuizReviewPage implements OnInit { | |||||||
|      * @param slot Slot of the question to scroll to. |      * @param slot Slot of the question to scroll to. | ||||||
|      */ |      */ | ||||||
|     async changePage(page: number, slot?: number): Promise<void> { |     async changePage(page: number, slot?: number): Promise<void> { | ||||||
|         if (slot !== undefined && (this.attempt!.currentpage == -1 || page == this.currentPage)) { |         if (slot !== undefined && (this.attempt?.currentpage == -1 || page == this.currentPage)) { | ||||||
|             // Scrol to a certain question in the current page.
 |             // Scrol to a certain question in the current page.
 | ||||||
|             this.scrollToQuestion(slot); |             this.scrollToQuestion(slot); | ||||||
| 
 | 
 | ||||||
| @ -174,7 +174,7 @@ export class AddonModQuizReviewPage implements OnInit { | |||||||
|      * @returns Promise resolved when done. |      * @returns Promise resolved when done. | ||||||
|      */ |      */ | ||||||
|     protected async loadPage(page: number): Promise<void> { |     protected async loadPage(page: number): Promise<void> { | ||||||
|         const data = await AddonModQuiz.getAttemptReview(this.attemptId, { page, cmId: this.quiz!.coursemodule }); |         const data = await AddonModQuiz.getAttemptReview(this.attemptId, { page, cmId: this.quiz?.coursemodule }); | ||||||
| 
 | 
 | ||||||
|         this.attempt = data.attempt; |         this.attempt = data.attempt; | ||||||
|         this.attempt.currentpage = page; |         this.attempt.currentpage = page; | ||||||
| @ -203,7 +203,7 @@ export class AddonModQuizReviewPage implements OnInit { | |||||||
|      */ |      */ | ||||||
|     protected async loadNavigation(): Promise<void> { |     protected async loadNavigation(): Promise<void> { | ||||||
|         // Get all questions in single page to retrieve all the questions.
 |         // Get all questions in single page to retrieve all the questions.
 | ||||||
|         const data = await AddonModQuiz.getAttemptReview(this.attemptId, { page: -1, cmId: this.quiz!.coursemodule }); |         const data = await AddonModQuiz.getAttemptReview(this.attemptId, { page: -1, cmId: this.quiz?.coursemodule }); | ||||||
| 
 | 
 | ||||||
|         this.navigation = data.questions; |         this.navigation = data.questions; | ||||||
| 
 | 
 | ||||||
| @ -260,7 +260,7 @@ export class AddonModQuizReviewPage implements OnInit { | |||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         this.readableState = AddonModQuiz.getAttemptReadableStateName(this.attempt!.state || ''); |         this.readableState = AddonModQuiz.getAttemptReadableStateName(this.attempt.state ?? ''); | ||||||
| 
 | 
 | ||||||
|         if (this.attempt.state != AddonModQuizProvider.ATTEMPT_FINISHED) { |         if (this.attempt.state != AddonModQuizProvider.ATTEMPT_FINISHED) { | ||||||
|             return; |             return; | ||||||
| @ -283,7 +283,7 @@ export class AddonModQuizReviewPage implements OnInit { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // Treat grade.
 |         // Treat grade.
 | ||||||
|         if (this.options!.someoptions.marks >= AddonModQuizProvider.QUESTION_OPTIONS_MARK_AND_MAX && |         if (this.options && this.options.someoptions.marks >= AddonModQuizProvider.QUESTION_OPTIONS_MARK_AND_MAX && | ||||||
|                 AddonModQuiz.quizHasGrades(this.quiz)) { |                 AddonModQuiz.quizHasGrades(this.quiz)) { | ||||||
| 
 | 
 | ||||||
|             if (data.grade === null || data.grade === undefined) { |             if (data.grade === null || data.grade === undefined) { | ||||||
| @ -348,7 +348,7 @@ export class AddonModQuizReviewPage implements OnInit { | |||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         this.changePage(modalData.page!, modalData.slot); |         this.changePage(modalData.page, modalData.slot); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | |||||||
| @ -241,7 +241,7 @@ export class AddonModQuizPrefetchHandlerService extends CoreCourseActivityPrefet | |||||||
| 
 | 
 | ||||||
|         const isLastFinished = !attempts.length || AddonModQuiz.isAttemptFinished(attempts[attempts.length - 1].state); |         const isLastFinished = !attempts.length || AddonModQuiz.isAttemptFinished(attempts[attempts.length - 1].state); | ||||||
| 
 | 
 | ||||||
|         return quiz.attempts === 0 || quiz.attempts! > attempts.length || !isLastFinished; |         return quiz.attempts === 0 || (quiz.attempts ?? 0) > attempts.length || !isLastFinished; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -444,7 +444,7 @@ export class AddonModQuizPrefetchHandlerService extends CoreCourseActivityPrefet | |||||||
|             if (attempt.state == AddonModQuizProvider.ATTEMPT_IN_PROGRESS) { |             if (attempt.state == AddonModQuizProvider.ATTEMPT_IN_PROGRESS) { | ||||||
|                 // Get data for each page.
 |                 // Get data for each page.
 | ||||||
|                 promises = promises.concat(pages.map(async (page) => { |                 promises = promises.concat(pages.map(async (page) => { | ||||||
|                     if (isSequential && page < attempt.currentpage!) { |                     if (isSequential && attempt.currentpage && page < attempt.currentpage) { | ||||||
|                         // Sequential quiz, cannot get pages before the current one.
 |                         // Sequential quiz, cannot get pages before the current one.
 | ||||||
|                         return; |                         return; | ||||||
|                     } |                     } | ||||||
|  | |||||||
| @ -43,7 +43,7 @@ export class AddonModQuizPushClickHandlerService implements CorePushNotification | |||||||
|      */ |      */ | ||||||
|     async handles(notification: AddonModQuizPushNotificationData): Promise<boolean> { |     async handles(notification: AddonModQuizPushNotificationData): Promise<boolean> { | ||||||
|         return CoreUtils.isTrueOrOne(notification.notif) && notification.moodlecomponent == 'mod_quiz' && |         return CoreUtils.isTrueOrOne(notification.notif) && notification.moodlecomponent == 'mod_quiz' && | ||||||
|                 this.SUPPORTED_NAMES.indexOf(notification.name!) != -1; |                 this.SUPPORTED_NAMES.indexOf(notification.name ?? '') != -1; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | |||||||
| @ -358,7 +358,7 @@ export class AddonModQuizHelperProvider { | |||||||
|             if (attempt) { |             if (attempt) { | ||||||
|                 if (attempt.state != AddonModQuizProvider.ATTEMPT_OVERDUE && !attempt.finishedOffline) { |                 if (attempt.state != AddonModQuizProvider.ATTEMPT_OVERDUE && !attempt.finishedOffline) { | ||||||
|                     // We're continuing an attempt. Call getAttemptData to validate the preflight data.
 |                     // We're continuing an attempt. Call getAttemptData to validate the preflight data.
 | ||||||
|                     await AddonModQuiz.getAttemptData(attempt.id, attempt.currentpage!, preflightData, modOptions); |                     await AddonModQuiz.getAttemptData(attempt.id, attempt.currentpage ?? 0, preflightData, modOptions); | ||||||
| 
 | 
 | ||||||
|                     if (offline) { |                     if (offline) { | ||||||
|                         // Get current page stored in local.
 |                         // Get current page stored in local.
 | ||||||
|  | |||||||
| @ -198,11 +198,11 @@ export class AddonModQuizOfflineProvider { | |||||||
|         } else { |         } else { | ||||||
|             entry = { |             entry = { | ||||||
|                 quizid: quiz.id, |                 quizid: quiz.id, | ||||||
|                 userid: attempt.userid!, |                 userid: attempt.userid ?? CoreSites.getCurrentSiteUserId(), | ||||||
|                 id: attempt.id, |                 id: attempt.id, | ||||||
|                 courseid: quiz.course, |                 courseid: quiz.course, | ||||||
|                 timecreated: now, |                 timecreated: now, | ||||||
|                 attempt: attempt.attempt!, |                 attempt: attempt.attempt ?? 0, | ||||||
|                 currentpage: attempt.currentpage, |                 currentpage: attempt.currentpage, | ||||||
|                 timemodified: now, |                 timemodified: now, | ||||||
|                 finished: finish ? 1 : 0, |                 finished: finish ? 1 : 0, | ||||||
| @ -284,10 +284,12 @@ export class AddonModQuizOfflineProvider { | |||||||
| 
 | 
 | ||||||
|             if (questions[slot]) { |             if (questions[slot]) { | ||||||
|                 if (!questionsWithAnswers[slot]) { |                 if (!questionsWithAnswers[slot]) { | ||||||
|                     questionsWithAnswers[slot] = questions[slot]; |                     questionsWithAnswers[slot] = { | ||||||
|                     questionsWithAnswers[slot].answers = {}; |                         ...questions[slot], | ||||||
|  |                         answers: {}, | ||||||
|  |                     }; | ||||||
|                 } |                 } | ||||||
|                 questionsWithAnswers[slot].answers![nameWithoutPrefix] = answers[name]; |                 questionsWithAnswers[slot].answers[nameWithoutPrefix] = answers[name]; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -295,7 +297,7 @@ export class AddonModQuizOfflineProvider { | |||||||
|         await Promise.all(Object.values(questionsWithAnswers).map(async (question) => { |         await Promise.all(Object.values(questionsWithAnswers).map(async (question) => { | ||||||
| 
 | 
 | ||||||
|             const state = await CoreQuestionBehaviourDelegate.determineNewState( |             const state = await CoreQuestionBehaviourDelegate.determineNewState( | ||||||
|                 quiz.preferredbehaviour!, |                 quiz.preferredbehaviour ?? '', | ||||||
|                 AddonModQuizProvider.COMPONENT, |                 AddonModQuizProvider.COMPONENT, | ||||||
|                 attempt.id, |                 attempt.id, | ||||||
|                 question, |                 question, | ||||||
| @ -317,7 +319,7 @@ export class AddonModQuizOfflineProvider { | |||||||
|             AddonModQuizProvider.COMPONENT, |             AddonModQuizProvider.COMPONENT, | ||||||
|             quiz.id, |             quiz.id, | ||||||
|             attempt.id, |             attempt.id, | ||||||
|             attempt.userid!, |             attempt.userid ?? CoreSites.getCurrentSiteUserId(), | ||||||
|             answers, |             answers, | ||||||
|             timeMod, |             timeMod, | ||||||
|             siteId, |             siteId, | ||||||
| @ -332,7 +334,7 @@ export class AddonModQuizOfflineProvider { | |||||||
|                     AddonModQuizProvider.COMPONENT, |                     AddonModQuizProvider.COMPONENT, | ||||||
|                     quiz.id, |                     quiz.id, | ||||||
|                     attempt.id, |                     attempt.id, | ||||||
|                     attempt.userid!, |                     attempt.userid ?? CoreSites.getCurrentSiteUserId(), | ||||||
|                     question, |                     question, | ||||||
|                     newStates[slot], |                     newStates[slot], | ||||||
|                     siteId, |                     siteId, | ||||||
|  | |||||||
| @ -305,8 +305,9 @@ export class AddonModQuizSyncProvider extends CoreCourseActivitySyncBaseProvider | |||||||
| 
 | 
 | ||||||
|         // Get all the offline attempts for the quiz. It should always be 0 or 1 attempt
 |         // Get all the offline attempts for the quiz. It should always be 0 or 1 attempt
 | ||||||
|         const offlineAttempts = await AddonModQuizOffline.getQuizAttempts(quiz.id, siteId); |         const offlineAttempts = await AddonModQuizOffline.getQuizAttempts(quiz.id, siteId); | ||||||
|  |         const offlineAttempt = offlineAttempts.pop(); | ||||||
| 
 | 
 | ||||||
|         if (!offlineAttempts.length) { |         if (!offlineAttempt) { | ||||||
|             // Nothing to sync, finish.
 |             // Nothing to sync, finish.
 | ||||||
|             return this.finishSync(siteId, quiz, courseId, warnings); |             return this.finishSync(siteId, quiz, courseId, warnings); | ||||||
|         } |         } | ||||||
| @ -316,8 +317,6 @@ export class AddonModQuizSyncProvider extends CoreCourseActivitySyncBaseProvider | |||||||
|             throw new CoreError(Translate.instant('core.cannotconnect')); |             throw new CoreError(Translate.instant('core.cannotconnect')); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         const offlineAttempt = offlineAttempts.pop()!; |  | ||||||
| 
 |  | ||||||
|         // Now get the list of online attempts to make sure this attempt exists and isn't finished.
 |         // Now get the list of online attempts to make sure this attempt exists and isn't finished.
 | ||||||
|         const onlineAttempts = await AddonModQuiz.getUserAttempts(quiz.id, modOptions); |         const onlineAttempts = await AddonModQuiz.getUserAttempts(quiz.id, modOptions); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -290,7 +290,7 @@ export class AddonModQuizProvider { | |||||||
|                 return dueDate * 1000; |                 return dueDate * 1000; | ||||||
| 
 | 
 | ||||||
|             case AddonModQuizProvider.ATTEMPT_OVERDUE: |             case AddonModQuizProvider.ATTEMPT_OVERDUE: | ||||||
|                 return (dueDate + quiz.graceperiod!) * 1000; |                 return (dueDate + (quiz.graceperiod ?? 0)) * 1000; | ||||||
| 
 | 
 | ||||||
|             default: |             default: | ||||||
|                 this.logger.warn('Unexpected state when getting due date: ' + attempt.state); |                 this.logger.warn('Unexpected state when getting due date: ' + attempt.state); | ||||||
| @ -356,7 +356,7 @@ export class AddonModQuizProvider { | |||||||
|                     Translate.instant('addon.mod_quiz.statefinished'), |                     Translate.instant('addon.mod_quiz.statefinished'), | ||||||
|                     Translate.instant( |                     Translate.instant( | ||||||
|                         'addon.mod_quiz.statefinisheddetails', |                         'addon.mod_quiz.statefinisheddetails', | ||||||
|                         { $a: CoreTimeUtils.userDate(attempt.timefinish! * 1000) }, |                         { $a: CoreTimeUtils.userDate((attempt.timefinish ?? 0) * 1000) }, | ||||||
|                     ), |                     ), | ||||||
|                 ]; |                 ]; | ||||||
| 
 | 
 | ||||||
| @ -625,7 +625,7 @@ export class AddonModQuizProvider { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (quiz.questiondecimalpoints == -1) { |         if (quiz.questiondecimalpoints == -1) { | ||||||
|             return quiz.decimalpoints!; |             return quiz.decimalpoints ?? 1; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return quiz.questiondecimalpoints; |         return quiz.questiondecimalpoints; | ||||||
| @ -1780,7 +1780,7 @@ export class AddonModQuizProvider { | |||||||
|      * @returns Whether quiz is graded. |      * @returns Whether quiz is graded. | ||||||
|      */ |      */ | ||||||
|     quizHasGrades(quiz: AddonModQuizQuizWSData): boolean { |     quizHasGrades(quiz: AddonModQuizQuizWSData): boolean { | ||||||
|         return quiz.grade! >= 0.000005 && quiz.sumgrades! >= 0.000005; |         return (quiz.grade ?? 0) >= 0.000005 && (quiz.sumgrades ?? 0) >= 0.000005; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -1802,8 +1802,8 @@ export class AddonModQuizProvider { | |||||||
| 
 | 
 | ||||||
|         const rawGradeNum = typeof rawGrade == 'string' ? parseFloat(rawGrade) : rawGrade; |         const rawGradeNum = typeof rawGrade == 'string' ? parseFloat(rawGrade) : rawGrade; | ||||||
|         if (rawGradeNum !== undefined && rawGradeNum !== null && !isNaN(rawGradeNum)) { |         if (rawGradeNum !== undefined && rawGradeNum !== null && !isNaN(rawGradeNum)) { | ||||||
|             if (quiz.sumgrades! >= 0.000005) { |             if (quiz.sumgrades && quiz.sumgrades >= 0.000005) { | ||||||
|                 grade = rawGradeNum * quiz.grade! / quiz.sumgrades!; |                 grade = rawGradeNum * (quiz.grade ?? 0) / quiz.sumgrades; | ||||||
|             } else { |             } else { | ||||||
|                 grade = 0; |                 grade = 0; | ||||||
|             } |             } | ||||||
| @ -1816,7 +1816,7 @@ export class AddonModQuizProvider { | |||||||
|         if (format === 'question') { |         if (format === 'question') { | ||||||
|             return this.formatGrade(grade, this.getGradeDecimals(quiz)); |             return this.formatGrade(grade, this.getGradeDecimals(quiz)); | ||||||
|         } else if (format) { |         } else if (format) { | ||||||
|             return this.formatGrade(grade, quiz.decimalpoints!); |             return this.formatGrade(grade, quiz.decimalpoints ?? 1); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return String(grade); |         return String(grade); | ||||||
|  | |||||||
| @ -132,5 +132,5 @@ export const CoreQuestionBehaviourDelegate = makeSingleton(CoreQuestionBehaviour | |||||||
|  * Answers classified by question slot. |  * Answers classified by question slot. | ||||||
|  */ |  */ | ||||||
| export type CoreQuestionQuestionWithAnswers = CoreQuestionQuestionParsed & { | export type CoreQuestionQuestionWithAnswers = CoreQuestionQuestionParsed & { | ||||||
|     answers?: CoreQuestionsAnswers; |     answers: CoreQuestionsAnswers; | ||||||
| }; | }; | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user