From 71f65cec072a28e27d0151ee52e76ba4aafa8732 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Tue, 8 Mar 2022 07:42:09 +0100 Subject: [PATCH] MOBILE-3930 mod: Fix log view not called after PTR if fetch failed --- src/addons/blog/pages/entries/entries.ts | 16 +++--- .../pages/competency/competency.page.ts | 55 ++++++++++--------- .../competencysummary.page.ts | 12 ++-- .../mod/assign/components/index/index.ts | 41 +++++++------- .../bigbluebuttonbn/components/index/index.ts | 25 ++++----- src/addons/mod/book/components/index/index.ts | 12 ++-- src/addons/mod/chat/components/index/index.ts | 24 ++++---- .../mod/choice/components/index/index.ts | 26 ++++----- src/addons/mod/data/components/index/index.ts | 22 ++------ src/addons/mod/data/pages/entry/entry.ts | 11 +++- .../mod/feedback/components/index/index.ts | 16 ++++-- .../mod/folder/components/index/index.ts | 14 ++--- .../mod/forum/components/index/index.ts | 17 +++--- .../mod/glossary/components/index/index.ts | 32 ++++++----- .../mod/h5pactivity/components/index/index.ts | 6 +- .../pages/attempt-results/attempt-results.ts | 38 +++++++------ .../pages/user-attempts/user-attempts.ts | 38 +++++++------ .../pages/users-attempts/users-attempts.ts | 32 ++++++----- .../mod/imscp/components/index/index.ts | 14 ++--- .../mod/lesson/components/index/index.ts | 25 ++------- src/addons/mod/page/components/index/index.ts | 14 ++--- src/addons/mod/quiz/components/index/index.ts | 30 +++++----- .../mod/quiz/pages/review/review.page.ts | 12 ++-- .../mod/resource/components/index/index.ts | 13 +++-- .../mod/scorm/components/index/index.ts | 27 ++++----- .../mod/survey/components/index/index.ts | 19 ++++--- src/addons/mod/url/components/index/index.ts | 22 +++++--- src/addons/mod/wiki/components/index/index.ts | 34 ++++++------ .../mod/workshop/components/index/index.ts | 24 ++++---- .../workshop/pages/submission/submission.ts | 28 ++++++++-- src/addons/notes/pages/list/list.page.ts | 10 +++- .../items-management/list-items-manager.ts | 21 ++++++- .../course/classes/main-activity-component.ts | 2 + .../course/classes/main-resource-component.ts | 44 +++++++++++++++ .../grades/pages/course/course.page.ts | 8 ++- .../features/sitehome/pages/index/index.ts | 17 +++--- .../user/pages/profile/profile.page.ts | 25 +++++---- 37 files changed, 465 insertions(+), 361 deletions(-) diff --git a/src/addons/blog/pages/entries/entries.ts b/src/addons/blog/pages/entries/entries.ts index 98ae5f66a..100eec06b 100644 --- a/src/addons/blog/pages/entries/entries.ts +++ b/src/addons/blog/pages/entries/entries.ts @@ -43,6 +43,7 @@ export class AddonBlogEntriesPage implements OnInit { protected canLoadMoreEntries = false; protected canLoadMoreUserEntries = true; protected siteHomeId: number; + protected fetchSuccess = false; loaded = false; canLoadMore = false; @@ -123,8 +124,6 @@ export class AddonBlogEntriesPage implements OnInit { deepLinkManager.treatLink(); await this.fetchEntries(); - - CoreUtils.ignoreErrors(AddonBlog.logView(this.filter)); } /** @@ -176,13 +175,7 @@ export class AddonBlogEntriesPage implements OnInit { entry.summary = CoreTextUtils.replacePluginfileUrls(entry.summary, entry.summaryfiles || []); - return CoreUser.getProfile(entry.userid, entry.courseid, true).then((user) => { - entry.user = user; - - return; - }).catch(() => { - // Ignore errors. - }); + entry.user = await CoreUtils.ignoreErrors(CoreUser.getProfile(entry.userid, entry.courseid, true)); }); if (refresh) { @@ -205,6 +198,11 @@ export class AddonBlogEntriesPage implements OnInit { } await Promise.all(promises); + + if (!this.fetchSuccess) { + this.fetchSuccess = true; + CoreUtils.ignoreErrors(AddonBlog.logView(this.filter)); + } } catch (error) { CoreDomUtils.showErrorModalDefault(error, 'addon.blog.errorloadentries', true); this.loadMoreError = true; // Set to prevent infinite calls with infinite-loading. diff --git a/src/addons/competency/pages/competency/competency.page.ts b/src/addons/competency/pages/competency/competency.page.ts index e2c2a41a1..763d63ede 100644 --- a/src/addons/competency/pages/competency/competency.page.ts +++ b/src/addons/competency/pages/competency/competency.page.ts @@ -58,6 +58,8 @@ export class AddonCompetencyCompetencyPage implements OnInit, OnDestroy { contextLevel?: string; contextInstanceId?: number; + protected fetchSuccess = false; + constructor() { try { const planId = CoreNavigator.getRouteNumberParam('planId'); @@ -117,33 +119,6 @@ export class AddonCompetencyCompetencyPage implements OnInit, OnDestroy { await source.reload(); await this.competencies.start(); await this.fetchCompetency(); - - if (!this.competency) { - return; - } - - const name = this.competency.competency.competency.shortname; - - if (source instanceof AddonCompetencyPlanCompetenciesSource) { - this.planStatus && await CoreUtils.ignoreErrors( - AddonCompetency.logCompetencyInPlanView( - source.PLAN_ID, - this.requireCompetencyId(), - this.planStatus, - name, - source.user?.id, - ), - ); - } else { - await CoreUtils.ignoreErrors( - AddonCompetency.logCompetencyInCourseView( - source.COURSE_ID, - this.requireCompetencyId(), - name, - source.USER_ID, - ), - ); - } } finally { this.competencyLoaded = true; } @@ -180,6 +155,32 @@ export class AddonCompetencyCompetencyPage implements OnInit, OnDestroy { evidence.description = Translate.instant(key, { $a: evidence.desca }); } }); + + if (!this.fetchSuccess) { + this.fetchSuccess = true; + const name = this.competency.competency.competency.shortname; + + if (source instanceof AddonCompetencyPlanCompetenciesSource) { + this.planStatus && await CoreUtils.ignoreErrors( + AddonCompetency.logCompetencyInPlanView( + source.PLAN_ID, + this.requireCompetencyId(), + this.planStatus, + name, + source.user?.id, + ), + ); + } else { + await CoreUtils.ignoreErrors( + AddonCompetency.logCompetencyInCourseView( + source.COURSE_ID, + this.requireCompetencyId(), + name, + source.USER_ID, + ), + ); + } + } } catch (error) { CoreDomUtils.showErrorModalDefault(error, 'Error getting competency data.'); } diff --git a/src/addons/competency/pages/competencysummary/competencysummary.page.ts b/src/addons/competency/pages/competencysummary/competencysummary.page.ts index b490c4ed4..b6fe06664 100644 --- a/src/addons/competency/pages/competencysummary/competencysummary.page.ts +++ b/src/addons/competency/pages/competencysummary/competencysummary.page.ts @@ -36,6 +36,8 @@ export class AddonCompetencyCompetencySummaryPage implements OnInit { contextLevel?: ContextLevel; contextInstanceId?: number; + protected fetchSuccess = false; // Whether a fetch was finished successfully. + /** * @inheritdoc */ @@ -54,9 +56,6 @@ export class AddonCompetencyCompetencySummaryPage implements OnInit { try { await this.fetchCompetency(); - const name = this.competency!.competency && this.competency!.competency.shortname; - - CoreUtils.ignoreErrors(AddonCompetency.logCompetencyView(this.competencyId, name)); } finally { this.competencyLoaded = true; } @@ -73,10 +72,15 @@ export class AddonCompetencyCompetencySummaryPage implements OnInit { if (!this.contextLevel || this.contextInstanceId === undefined) { // Context not specified, use user context. this.contextLevel = ContextLevel.USER; - this.contextInstanceId = result.usercompetency!.userid; + this.contextInstanceId = result.usercompetency?.userid; } this.competency = result.competency; + + if (!this.fetchSuccess) { + this.fetchSuccess = true; + CoreUtils.ignoreErrors(AddonCompetency.logCompetencyView(this.competencyId, this.competency.competency.shortname)); + } } catch (error) { CoreDomUtils.showErrorModalDefault(error, 'Error getting competency summary data.'); } diff --git a/src/addons/mod/assign/components/index/index.ts b/src/addons/mod/assign/components/index/index.ts index 226d5e28a..b2ae03e97 100644 --- a/src/addons/mod/assign/components/index/index.ts +++ b/src/addons/mod/assign/components/index/index.ts @@ -17,7 +17,6 @@ import { Params } from '@angular/router'; import { CoreSite } from '@classes/site'; import { CoreCourseModuleMainActivityComponent } from '@features/course/classes/main-activity-component'; import { CoreCourseContentsPage } from '@features/course/pages/contents/contents'; -import { CoreCourse } from '@features/course/services/course'; import { IonContent } from '@ionic/angular'; import { CoreGroupInfo, CoreGroups } from '@services/groups'; import { CoreNavigator } from '@services/navigator'; @@ -121,7 +120,7 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo (data) => { if (this.assign && data.assignmentId == this.assign.id && data.userId == this.currentUserId) { // Assignment submitted, check completion. - CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata); + this.checkCompletion(); // Reload data since it can have offline data now. this.showLoadingAndRefresh(true, false); @@ -138,25 +137,6 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo }, this.siteId); await this.loadContent(false, true); - - if (!this.assign) { - return; - } - - try { - await AddonModAssign.logView(this.assign.id, this.assign.name); - CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata); - } catch { - // Ignore errors. Just don't check Module completion. - } - - if (this.canViewAllSubmissions) { - // User can see all submissions, log grading view. - CoreUtils.ignoreErrors(AddonModAssign.logGradingView(this.assign.id, this.assign.name)); - } else if (this.canViewOwnSubmission) { - // User can only see their own submission, log view the user submission. - CoreUtils.ignoreErrors(AddonModAssign.logSubmissionView(this.assign.id, this.assign.name)); - } } /** @@ -232,6 +212,25 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo } } + /** + * @inheritdoc + */ + protected async logActivity(): Promise { + if (!this.assign) { + return; // Shouldn't happen. + } + + await AddonModAssign.logView(this.assign.id, this.assign.name); + + if (this.canViewAllSubmissions) { + // User can see all submissions, log grading view. + CoreUtils.ignoreErrors(AddonModAssign.logGradingView(this.assign.id, this.assign.name)); + } else if (this.canViewOwnSubmission) { + // User can only see their own submission, log view the user submission. + CoreUtils.ignoreErrors(AddonModAssign.logSubmissionView(this.assign.id, this.assign.name)); + } + } + /** * Set group to see the summary. * diff --git a/src/addons/mod/bigbluebuttonbn/components/index/index.ts b/src/addons/mod/bigbluebuttonbn/components/index/index.ts index d8b639b95..33a5fe693 100644 --- a/src/addons/mod/bigbluebuttonbn/components/index/index.ts +++ b/src/addons/mod/bigbluebuttonbn/components/index/index.ts @@ -15,7 +15,6 @@ import { Component, OnInit, Optional } from '@angular/core'; import { CoreCourseModuleMainActivityComponent } from '@features/course/classes/main-activity-component'; import { CoreCourseContentsPage } from '@features/course/pages/contents/contents'; -import { CoreCourse } from '@features/course/services/course'; import { IonContent } from '@ionic/angular'; import { CoreGroupInfo, CoreGroups } from '@services/groups'; import { CoreDomUtils } from '@services/utils/dom'; @@ -54,18 +53,6 @@ export class AddonModBBBIndexComponent extends CoreCourseModuleMainActivityCompo super.ngOnInit(); await this.loadContent(); - - if (!this.bbb) { - return; - } - - try { - await AddonModBBB.logView(this.bbb.id, this.bbb.name); - - CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata); - } catch { - // Ignore errors. - } } /** @@ -82,7 +69,6 @@ export class AddonModBBBIndexComponent extends CoreCourseModuleMainActivityCompo this.groupId = CoreGroups.validateGroupId(this.groupId, this.groupInfo); await this.fetchMeetingInfo(); - } /** @@ -107,6 +93,17 @@ export class AddonModBBBIndexComponent extends CoreCourseModuleMainActivityCompo } } + /** + * @inheritdoc + */ + protected async logActivity(): Promise { + if (!this.bbb) { + return; // Shouldn't happen. + } + + await AddonModBBB.logView(this.bbb.id, this.bbb.name); + } + /** * Update meeting info. * diff --git a/src/addons/mod/book/components/index/index.ts b/src/addons/mod/book/components/index/index.ts index e711670ba..82747aa77 100644 --- a/src/addons/mod/book/components/index/index.ts +++ b/src/addons/mod/book/components/index/index.ts @@ -18,7 +18,6 @@ import { AddonModBook, AddonModBookBookWSData, AddonModBookNumbering, AddonModBo import { CoreCourseContentsPage } from '@features/course/pages/contents/contents'; import { CoreCourse } from '@features/course/services/course'; import { CoreNavigator } from '@services/navigator'; -import { CoreUtils } from '@services/utils/utils'; /** * Component that displays a book entry page. @@ -36,6 +35,7 @@ export class AddonModBookIndexComponent extends CoreCourseModuleMainResourceComp hasStartedBook = false; protected book?: AddonModBookBookWSData; + protected checkCompletionAfterLog = false; constructor( @Optional() courseContentsPage?: CoreCourseContentsPage) { super('AddonModBookIndexComponent', courseContentsPage); @@ -48,9 +48,6 @@ export class AddonModBookIndexComponent extends CoreCourseModuleMainResourceComp super.ngOnInit(); this.loadContent(); - - // Log book viewed. - await CoreUtils.ignoreErrors(AddonModBook.logView(this.module.instance, undefined, this.module.name)); } /** @@ -93,6 +90,13 @@ export class AddonModBookIndexComponent extends CoreCourseModuleMainResourceComp this.chapters = AddonModBook.getTocList(contents); } + /** + * @inheritdoc + */ + protected async logActivity(): Promise { + AddonModBook.logView(this.module.instance, undefined, this.module.name); + } + /** * Open the book in a certain chapter. * diff --git a/src/addons/mod/chat/components/index/index.ts b/src/addons/mod/chat/components/index/index.ts index 159f4e462..813c08cff 100644 --- a/src/addons/mod/chat/components/index/index.ts +++ b/src/addons/mod/chat/components/index/index.ts @@ -15,7 +15,6 @@ import { Component, OnInit, Optional } from '@angular/core'; import { CoreCourseModuleMainActivityComponent } from '@features/course/classes/main-activity-component'; import { CoreCourseContentsPage } from '@features/course/pages/contents/contents'; -import { CoreCourse } from '@features/course/services/course'; import { IonContent } from '@ionic/angular'; import { CoreNavigator } from '@services/navigator'; import { CoreTimeUtils } from '@services/utils/time'; @@ -53,18 +52,6 @@ export class AddonModChatIndexComponent extends CoreCourseModuleMainActivityComp super.ngOnInit(); await this.loadContent(); - - if (!this.chat) { - return; - } - - try { - await AddonModChat.logView(this.chat.id, this.chat.name); - - CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata); - } catch { - // Ignore errors. - } } /** @@ -89,6 +76,17 @@ export class AddonModChatIndexComponent extends CoreCourseModuleMainActivityComp this.dataRetrieved.emit(this.chat); } + /** + * @inheritdoc + */ + protected async logActivity(): Promise { + if (!this.chat) { + return; // Shouldn't happen. + } + + await AddonModChat.logView(this.chat.id, this.chat.name); + } + /** * Enter the chat. */ diff --git a/src/addons/mod/choice/components/index/index.ts b/src/addons/mod/choice/components/index/index.ts index a43fc8c58..aaa489e82 100644 --- a/src/addons/mod/choice/components/index/index.ts +++ b/src/addons/mod/choice/components/index/index.ts @@ -15,7 +15,6 @@ import { Component, Optional, OnInit } from '@angular/core'; import { CoreCourseModuleMainActivityComponent } from '@features/course/classes/main-activity-component'; import { CoreCourseContentsPage } from '@features/course/pages/contents/contents'; -import { CoreCourse } from '@features/course/services/course'; import { IonContent } from '@ionic/angular'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; @@ -86,18 +85,6 @@ export class AddonModChoiceIndexComponent extends CoreCourseModuleMainActivityCo this.userId = CoreSites.getCurrentSiteUserId(); await this.loadContent(false, true); - - if (!this.choice) { - return; - } - - try { - await AddonModChoice.logView(this.choice.id, this.choice.name); - - await CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata); - } catch { - // Ignore errors. - } } /** @@ -325,6 +312,17 @@ export class AddonModChoiceIndexComponent extends CoreCourseModuleMainActivityCo this.canSeeResults = hasVotes || AddonModChoice.canStudentSeeResults(choice, this.hasAnsweredOnline); } + /** + * @inheritdoc + */ + protected async logActivity(): Promise { + if (!this.choice) { + return; // Shouldn't happen. + } + + await AddonModChoice.logView(this.choice.id, this.choice.name); + } + /** * Check if a choice is open. * @@ -384,7 +382,7 @@ export class AddonModChoiceIndexComponent extends CoreCourseModuleMainActivityCo if (online) { CoreEvents.trigger(CoreEvents.ACTIVITY_DATA_SENT, { module: this.moduleName }); // Check completion since it could be configured to complete once the user answers the choice. - CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata); + this.checkCompletion(); } await this.dataUpdated(online); diff --git a/src/addons/mod/data/components/index/index.ts b/src/addons/mod/data/components/index/index.ts index 2c367fa48..164be2986 100644 --- a/src/addons/mod/data/components/index/index.ts +++ b/src/addons/mod/data/components/index/index.ts @@ -18,7 +18,6 @@ import { Params } from '@angular/router'; import { CoreCommentsProvider } from '@features/comments/services/comments'; import { CoreCourseModuleMainActivityComponent } from '@features/course/classes/main-activity-component'; import { CoreCourseContentsPage } from '@features/course/pages/contents/contents'; -import { CoreCourse } from '@features/course/services/course'; import { CoreRatingProvider } from '@features/rating/services/rating'; import { CoreRatingSyncProvider } from '@features/rating/services/rating-sync'; import { IonContent } from '@ionic/angular'; @@ -154,7 +153,6 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp }); await this.loadContent(false, true); - await this.logView(true); } /** @@ -410,7 +408,7 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp try { await this.fetchEntriesData(); // Log activity view for coherence with Moodle web. - await this.logView(); + await this.logActivity(); } catch (error) { CoreDomUtils.showErrorModalDefault(error, 'core.course.errorgetmodule', true); } finally { @@ -456,7 +454,7 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp await this.fetchEntriesData(); // Log activity view for coherence with Moodle web. - return this.logView(); + return this.logActivity(); } catch (error) { CoreDomUtils.showErrorModalDefault(error, 'core.course.errorgetmodule', true); } @@ -522,24 +520,14 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp } /** - * Log viewing the activity. - * - * @param checkCompletion Whether to check completion. - * @return Promise resolved when done. + * @inheritdoc */ - protected async logView(checkCompletion = false): Promise { + protected async logActivity(): Promise { if (!this.database || !this.database.id) { return; } - try { - await AddonModData.logView(this.database.id, this.database.name); - if (checkCompletion) { - CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata); - } - } catch { - // Ignore errors, the user could be offline. - } + await AddonModData.logView(this.database.id, this.database.name); } /** diff --git a/src/addons/mod/data/pages/entry/entry.ts b/src/addons/mod/data/pages/entry/entry.ts index 173242ed4..d7ef66e84 100644 --- a/src/addons/mod/data/pages/entry/entry.ts +++ b/src/addons/mod/data/pages/entry/entry.ts @@ -55,6 +55,7 @@ export class AddonModDataEntryPage implements OnInit, OnDestroy { protected entryChangedObserver: CoreEventObserver; // It will observe the changed entry event. protected fields: Record = {}; protected fieldsArray: AddonModDataField[] = []; + protected logAfterFetch = true; moduleId = 0; courseId!: number; @@ -149,7 +150,6 @@ export class AddonModDataEntryPage implements OnInit, OnDestroy { this.commentsEnabled = !CoreComments.areCommentsDisabledInSite(); await this.fetchEntryData(); - this.logView(); } /** @@ -201,6 +201,11 @@ export class AddonModDataEntryPage implements OnInit, OnDestroy { title: this.title, group: this.selectedGroup, }; + + if (this.logAfterFetch) { + this.logAfterFetch = false; + this.logView(); + } } catch (error) { if (!refresh) { // Some call failed, retry without using cache since it might be a new activity. @@ -225,9 +230,9 @@ export class AddonModDataEntryPage implements OnInit, OnDestroy { this.entryId = undefined; this.entry = undefined; this.entryLoaded = false; + this.logAfterFetch = true; await this.fetchEntryData(); - this.logView(); } /** @@ -286,9 +291,9 @@ export class AddonModDataEntryPage implements OnInit, OnDestroy { this.entry = undefined; this.entryId = undefined; this.entryLoaded = false; + this.logAfterFetch = true; await this.fetchEntryData(); - this.logView(); } /** diff --git a/src/addons/mod/feedback/components/index/index.ts b/src/addons/mod/feedback/components/index/index.ts index e31b5f529..cb20702f5 100644 --- a/src/addons/mod/feedback/components/index/index.ts +++ b/src/addons/mod/feedback/components/index/index.ts @@ -82,6 +82,7 @@ export class AddonModFeedbackIndexComponent extends CoreCourseModuleMainActivity protected submitObserver: CoreEventObserver; protected syncEventName = AddonModFeedbackSyncProvider.AUTO_SYNCED; + protected checkCompletionAfterLog = false; constructor( protected content?: IonContent, @@ -125,15 +126,22 @@ export class AddonModFeedbackIndexComponent extends CoreCourseModuleMainActivity try { await this.loadContent(false, true); - - if (this.feedback) { - CoreUtils.ignoreErrors(AddonModFeedback.logView(this.feedback.id, this.feedback.name)); - } } finally { this.tabsReady = true; } } + /** + * @inheritdoc + */ + protected async logActivity(): Promise { + if (!this.feedback) { + return; // Shouldn't happen. + } + + await AddonModFeedback.logView(this.feedback.id, this.feedback.name); + } + /** * @inheritdoc */ diff --git a/src/addons/mod/folder/components/index/index.ts b/src/addons/mod/folder/components/index/index.ts index fcfa675c7..8cd1f0504 100644 --- a/src/addons/mod/folder/components/index/index.ts +++ b/src/addons/mod/folder/components/index/index.ts @@ -62,13 +62,6 @@ export class AddonModFolderIndexComponent extends CoreCourseModuleMainResourceCo try { await this.loadContent(); - - try { - await AddonModFolder.logView(this.module.instance, this.module.name); - CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata); - } catch { - // Ignore errors. - } } finally { this.showLoading = false; } @@ -97,6 +90,13 @@ export class AddonModFolderIndexComponent extends CoreCourseModuleMainResourceCo this.contents = AddonModFolderHelper.formatContents(contents); } + /** + * @inheritdoc + */ + protected async logActivity(): Promise { + await AddonModFolder.logView(this.module.instance, this.module.name); + } + /** * Navigate to a subfolder. * diff --git a/src/addons/mod/forum/components/index/index.ts b/src/addons/mod/forum/components/index/index.ts index 55ac9daaa..849453c42 100644 --- a/src/addons/mod/forum/components/index/index.ts +++ b/src/addons/mod/forum/components/index/index.ts @@ -99,6 +99,7 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom protected ratingOfflineObserver?: CoreEventObserver; protected ratingSyncObserver?: CoreEventObserver; protected sourceUnsubscribe?: () => void; + protected checkCompletionAfterLog = false; // Use CoreListItemsManager log system instead. constructor( public route: ActivatedRoute, @@ -570,7 +571,7 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom }); // Check completion since it could be configured to complete once the user adds a new discussion or replies. - CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata); + this.checkCompletion(); } } @@ -692,15 +693,13 @@ class AddonModForumDiscussionsManager extends CoreListItemsManager { - CoreCourse.checkModuleCompletion(this.page.courseId, this.page.module.completiondata); + try { + await AddonModForum.instance.logView(forum.id, forum.name); - return; - }), - ); + CoreCourse.checkModuleCompletion(this.page.courseId, this.page.module.completiondata); + } catch { + // Ignore errors. + } } } diff --git a/src/addons/mod/glossary/components/index/index.ts b/src/addons/mod/glossary/components/index/index.ts index 4e483a919..8c3bbd65a 100644 --- a/src/addons/mod/glossary/components/index/index.ts +++ b/src/addons/mod/glossary/components/index/index.ts @@ -81,12 +81,13 @@ export class AddonModGlossaryIndexComponent extends CoreCourseModuleMainActivity protected sourceUnsubscribe?: () => void; protected ratingOfflineObserver?: CoreEventObserver; protected ratingSyncObserver?: CoreEventObserver; + protected checkCompletionAfterLog = false; // Use CoreListItemsManager log system instead. getDivider?: (entry: AddonModGlossaryEntry) => string; showDivider: (entry: AddonModGlossaryEntry, previous?: AddonModGlossaryEntry) => boolean = () => false; constructor( - protected route: ActivatedRoute, + public route: ActivatedRoute, protected content?: IonContent, @Optional() protected courseContentsPage?: CoreCourseContentsPage, ) { @@ -124,10 +125,7 @@ export class AddonModGlossaryIndexComponent extends CoreCourseModuleMainActivity [this.courseId, this.module.id, this.courseContentsPage ? `${AddonModGlossaryModuleHandlerService.PAGE_NAME}/` : ''], ); - this.promisedEntries.resolve(new AddonModGlossaryEntriesManager( - source, - this.route.component, - )); + this.promisedEntries.resolve(new AddonModGlossaryEntriesManager(source, this)); this.sourceUnsubscribe = source.addListener({ onItemsUpdated: items => this.hasOffline = !!items.find(item => source.isOfflineEntry(item)), @@ -139,7 +137,7 @@ export class AddonModGlossaryIndexComponent extends CoreCourseModuleMainActivity this.showLoadingAndRefresh(false); // Check completion since it could be configured to complete once the user adds a new entry. - CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata); + this.checkCompletion(); } }); @@ -166,12 +164,6 @@ export class AddonModGlossaryIndexComponent extends CoreCourseModuleMainActivity await this.loadContent(false, true); await entries.start(this.splitView); - - try { - CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata); - } catch (error) { - // Ignore errors. - } } /** @@ -437,6 +429,14 @@ export class AddonModGlossaryIndexComponent extends CoreCourseModuleMainActivity */ class AddonModGlossaryEntriesManager extends CoreListItemsManager { + page: AddonModGlossaryIndexComponent; + + constructor(source: AddonModGlossaryEntriesSource, page: AddonModGlossaryIndexComponent) { + super(source, page.route.component); + + this.page = page; + } + get offlineEntries(): AddonModGlossaryOfflineEntry[] { return this.getSource().offlineEntries; } @@ -463,7 +463,13 @@ class AddonModGlossaryEntriesManager extends CoreListItemsManager Promise; + protected checkCompletionAfterLog = false; // It's called later, when the user plays the package. constructor( protected content?: IonContent, @@ -390,7 +390,7 @@ export class AddonModH5PActivityIndexComponent extends CoreCourseModuleMainActiv // Mark the activity as viewed. await AddonModH5PActivity.logView(this.h5pActivity.id, this.h5pActivity.name, this.siteId); - CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata); + this.checkCompletion(); } /** @@ -464,7 +464,7 @@ export class AddonModH5PActivityIndexComponent extends CoreCourseModuleMainActiv // Check if the H5P has ended. Final statements don't include a subContentId. const hasEnded = data.statements.some(statement => !statement.object.id.includes('subContentId=')); if (hasEnded) { - CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata); + this.checkCompletion(); } } } catch (error) { diff --git a/src/addons/mod/h5pactivity/pages/attempt-results/attempt-results.ts b/src/addons/mod/h5pactivity/pages/attempt-results/attempt-results.ts index ad89f085f..3b3727a2b 100644 --- a/src/addons/mod/h5pactivity/pages/attempt-results/attempt-results.ts +++ b/src/addons/mod/h5pactivity/pages/attempt-results/attempt-results.ts @@ -45,6 +45,7 @@ export class AddonModH5PActivityAttemptResultsPage implements OnInit { cmId!: number; protected attemptId!: number; + protected fetchSuccess = false; /** * @inheritdoc @@ -62,17 +63,7 @@ export class AddonModH5PActivityAttemptResultsPage implements OnInit { return; } - try { - await this.fetchData(); - - if (this.h5pActivity) { - await AddonModH5PActivity.logViewReport(this.h5pActivity.id, this.h5pActivity.name, { attemptId: this.attemptId }); - } - } catch (error) { - CoreDomUtils.showErrorModalDefault(error, 'Error loading attempt.'); - } finally { - this.loaded = true; - } + await this.fetchData(); } /** @@ -92,13 +83,28 @@ export class AddonModH5PActivityAttemptResultsPage implements OnInit { * @return Promise resolved when done. */ protected async fetchData(): Promise { - this.h5pActivity = await AddonModH5PActivity.getH5PActivity(this.courseId, this.cmId); + try { + this.h5pActivity = await AddonModH5PActivity.getH5PActivity(this.courseId, this.cmId); - this.attempt = await AddonModH5PActivity.getAttemptResults(this.h5pActivity.id, this.attemptId, { - cmId: this.cmId, - }); + this.attempt = await AddonModH5PActivity.getAttemptResults(this.h5pActivity.id, this.attemptId, { + cmId: this.cmId, + }); - await this.fetchUserProfile(); + await this.fetchUserProfile(); + + if (!this.fetchSuccess) { + this.fetchSuccess = true; + CoreUtils.ignoreErrors(AddonModH5PActivity.logViewReport( + this.h5pActivity.id, + this.h5pActivity.name, + { attemptId: this.attemptId }, + )); + } + } catch (error) { + CoreDomUtils.showErrorModalDefault(error, 'Error loading attempt.'); + } finally { + this.loaded = true; + } } /** diff --git a/src/addons/mod/h5pactivity/pages/user-attempts/user-attempts.ts b/src/addons/mod/h5pactivity/pages/user-attempts/user-attempts.ts index 0165feb15..bb95f9fcb 100644 --- a/src/addons/mod/h5pactivity/pages/user-attempts/user-attempts.ts +++ b/src/addons/mod/h5pactivity/pages/user-attempts/user-attempts.ts @@ -46,6 +46,7 @@ export class AddonModH5PActivityUserAttemptsPage implements OnInit { isCurrentUser = false; protected userId!: number; + protected fetchSuccess = false; /** * @inheritdoc @@ -65,17 +66,7 @@ export class AddonModH5PActivityUserAttemptsPage implements OnInit { this.isCurrentUser = this.userId == CoreSites.getCurrentSiteUserId(); - try { - await this.fetchData(); - - if (this.h5pActivity) { - await AddonModH5PActivity.logViewReport(this.h5pActivity.id, this.h5pActivity.name, { userId: this.userId }); - } - } catch (error) { - CoreDomUtils.showErrorModalDefault(error, 'Error loading attempts.'); - } finally { - this.loaded = true; - } + await this.fetchData(); } /** @@ -95,12 +86,27 @@ export class AddonModH5PActivityUserAttemptsPage implements OnInit { * @return Promise resolved when done. */ protected async fetchData(): Promise { - this.h5pActivity = await AddonModH5PActivity.getH5PActivity(this.courseId, this.cmId); + try { + this.h5pActivity = await AddonModH5PActivity.getH5PActivity(this.courseId, this.cmId); - await Promise.all([ - this.fetchAttempts(), - this.fetchUserProfile(), - ]); + await Promise.all([ + this.fetchAttempts(), + this.fetchUserProfile(), + ]); + + if (!this.fetchSuccess) { + this.fetchSuccess = true; + CoreUtils.ignoreErrors(AddonModH5PActivity.logViewReport( + this.h5pActivity.id, + this.h5pActivity.name, + { userId: this.userId }, + )); + } + } catch (error) { + CoreDomUtils.showErrorModalDefault(error, 'Error loading attempts.'); + } finally { + this.loaded = true; + } } /** diff --git a/src/addons/mod/h5pactivity/pages/users-attempts/users-attempts.ts b/src/addons/mod/h5pactivity/pages/users-attempts/users-attempts.ts index 0d45c30b5..f50cd056c 100644 --- a/src/addons/mod/h5pactivity/pages/users-attempts/users-attempts.ts +++ b/src/addons/mod/h5pactivity/pages/users-attempts/users-attempts.ts @@ -44,6 +44,7 @@ export class AddonModH5PActivityUsersAttemptsPage implements OnInit { canLoadMore = false; protected page = 0; + protected fetchSuccess = false; /** * @inheritdoc @@ -60,17 +61,7 @@ export class AddonModH5PActivityUsersAttemptsPage implements OnInit { return; } - try { - await this.fetchData(); - - if (this.h5pActivity) { - await AddonModH5PActivity.logViewReport(this.h5pActivity.id, this.h5pActivity.name); - } - } catch (error) { - CoreDomUtils.showErrorModalDefault(error, 'Error loading attempts.'); - } finally { - this.loaded = true; - } + this.fetchData(); } /** @@ -91,11 +82,22 @@ export class AddonModH5PActivityUsersAttemptsPage implements OnInit { * @return Promise resolved when done. */ protected async fetchData(refresh?: boolean): Promise { - this.h5pActivity = await AddonModH5PActivity.getH5PActivity(this.courseId, this.cmId); + try { + this.h5pActivity = await AddonModH5PActivity.getH5PActivity(this.courseId, this.cmId); - await Promise.all([ - this.fetchUsers(refresh), - ]); + await Promise.all([ + this.fetchUsers(refresh), + ]); + + if (!this.fetchSuccess) { + this.fetchSuccess = true; + CoreUtils.ignoreErrors(AddonModH5PActivity.logViewReport(this.h5pActivity.id, this.h5pActivity.name)); + } + } catch (error) { + CoreDomUtils.showErrorModalDefault(error, 'Error loading attempts.'); + } finally { + this.loaded = true; + } } /** diff --git a/src/addons/mod/imscp/components/index/index.ts b/src/addons/mod/imscp/components/index/index.ts index 6d6e501b4..97eff0801 100644 --- a/src/addons/mod/imscp/components/index/index.ts +++ b/src/addons/mod/imscp/components/index/index.ts @@ -45,13 +45,6 @@ export class AddonModImscpIndexComponent extends CoreCourseModuleMainResourceCom super.ngOnInit(); await this.loadContent(); - - try { - await AddonModImscp.logView(this.module.instance, this.module.name); - CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata); - } catch { - // Ignore errors. - } } /** @@ -103,6 +96,13 @@ export class AddonModImscpIndexComponent extends CoreCourseModuleMainResourceCom this.items = AddonModImscp.createItemList(contents); } + /** + * @inheritdoc + */ + protected async logActivity(): Promise { + await AddonModImscp.logView(this.module.instance, this.module.name); + } + /** * Open IMSCP book with a certain item. * diff --git a/src/addons/mod/lesson/components/index/index.ts b/src/addons/mod/lesson/components/index/index.ts index 415bf0555..7b41a3d8b 100644 --- a/src/addons/mod/lesson/components/index/index.ts +++ b/src/addons/mod/lesson/components/index/index.ts @@ -18,7 +18,6 @@ import { Component, Input, ViewChild, ElementRef, OnInit, OnDestroy, Optional } import { CoreTabsComponent } from '@components/tabs/tabs'; import { CoreCourseModuleMainActivityComponent } from '@features/course/classes/main-activity-component'; import { CoreCourseContentsPage } from '@features/course/pages/contents/contents'; -import { CoreCourse } from '@features/course/services/course'; import { CoreUser } from '@features/user/services/user'; import { IonContent, IonInput } from '@ionic/angular'; import { CoreGroupInfo, CoreGroups } from '@services/groups'; @@ -108,12 +107,6 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo this.selectedTab = this.action == 'report' ? 1 : 0; await this.loadContent(false, true); - - if (!this.lesson || this.preventReasons.length) { - return; - } - - this.logView(); } /** @@ -285,7 +278,7 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo protected hasSyncSucceed(result: AddonModLessonSyncResult): boolean { if (result.updated || this.dataSent) { // Check completion status if something was sent. - CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata); + this.checkCompletion(); } this.dataSent = false; @@ -373,20 +366,14 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo } /** - * Log viewing the lesson. - * - * @return Promise resolved when done. + * @inheritdoc */ - protected async logView(): Promise { - if (!this.lesson) { + protected async logActivity(): Promise { + if (!this.lesson || this.preventReasons.length) { return; } - await CoreUtils.ignoreErrors( - AddonModLesson.logViewLesson(this.lesson.id, this.password, this.lesson.name), - ); - - CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata); + await AddonModLesson.logViewLesson(this.lesson.id, this.password, this.lesson.name); } /** @@ -631,7 +618,7 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo this.preventReasons = preventReason ? [preventReason] : []; // Log view now that we have the password. - this.logView(); + this.logActivity(); } catch (error) { CoreDomUtils.showErrorModal(error); } finally { diff --git a/src/addons/mod/page/components/index/index.ts b/src/addons/mod/page/components/index/index.ts index bbf8ac151..bbcac903a 100644 --- a/src/addons/mod/page/components/index/index.ts +++ b/src/addons/mod/page/components/index/index.ts @@ -52,13 +52,6 @@ export class AddonModPageIndexComponent extends CoreCourseModuleMainResourceComp super.ngOnInit(); await this.loadContent(); - - try { - await AddonModPage.logView(this.module.instance, this.module.name); - CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata); - } catch { - // Ignore errors. - } } /** @@ -118,4 +111,11 @@ export class AddonModPageIndexComponent extends CoreCourseModuleMainResourceComp this.timemodified = 'timemodified' in this.page ? this.page.timemodified : undefined; } + /** + * @inheritdoc + */ + protected async logActivity(): Promise { + await AddonModPage.logView(this.module.instance, this.module.name); + } + } diff --git a/src/addons/mod/quiz/components/index/index.ts b/src/addons/mod/quiz/components/index/index.ts index d827022de..bae07b503 100644 --- a/src/addons/mod/quiz/components/index/index.ts +++ b/src/addons/mod/quiz/components/index/index.ts @@ -17,7 +17,6 @@ import { Component, OnDestroy, OnInit, Optional } from '@angular/core'; import { CoreCourseModuleMainActivityComponent } from '@features/course/classes/main-activity-component'; import { CoreCourseContentsPage } from '@features/course/pages/contents/contents'; -import { CoreCourse } from '@features/course/services/course'; import { CoreQuestionBehaviourDelegate } from '@features/question/services/behaviour-delegate'; import { IonContent } from '@ionic/angular'; import { CoreNavigator } from '@services/navigator'; @@ -121,18 +120,6 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp ); await this.loadContent(false, true); - - if (!this.quiz) { - return; - } - - try { - await AddonModQuiz.logViewQuiz(this.quiz.id, this.quiz.name); - - CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata); - } catch { - // Ignore errors. - } } /** @@ -387,6 +374,17 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp } } + /** + * @inheritdoc + */ + protected async logActivity(): Promise { + if (!this.quiz) { + return; // Shouldn't happen. + } + + await AddonModQuiz.logViewQuiz(this.quiz.id, this.quiz.name); + } + /** * Go to review an attempt that has just been finished. * @@ -398,7 +396,7 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp } // If we go to auto review it means an attempt was finished. Check completion status. - CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata); + this.checkCompletion(); // Verify that user can see the review. const attemptId = this.autoReview.attemptId; @@ -425,7 +423,7 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp protected hasSyncSucceed(result: AddonModQuizSyncResult): boolean { if (result.attemptFinished) { // An attempt was finished, check completion status. - CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata); + this.checkCompletion(); } // If the sync call isn't rejected it means the sync was successful. @@ -508,7 +506,7 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp if (syncEventData.attemptFinished) { // An attempt was finished, check completion status. - CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata); + this.checkCompletion(); } if (this.quiz && syncEventData.quizId == this.quiz.id) { diff --git a/src/addons/mod/quiz/pages/review/review.page.ts b/src/addons/mod/quiz/pages/review/review.page.ts index 04b7bb7a7..e58aac16e 100644 --- a/src/addons/mod/quiz/pages/review/review.page.ts +++ b/src/addons/mod/quiz/pages/review/review.page.ts @@ -73,6 +73,7 @@ export class AddonModQuizReviewPage implements OnInit { protected attemptId!: number; // The attempt being reviewed. protected currentPage!: number; // The current page being reviewed. protected options?: AddonModQuizCombinedReviewOptions; // Review options. + protected fetchSuccess = false; constructor( protected elementRef: ElementRef, @@ -99,10 +100,6 @@ export class AddonModQuizReviewPage implements OnInit { try { await this.fetchData(); - - CoreUtils.ignoreErrors( - AddonModQuiz.logViewAttemptReview(this.attemptId, this.quiz!.id, this.quiz!.name), - ); } finally { this.loaded = true; } @@ -160,6 +157,13 @@ export class AddonModQuizReviewPage implements OnInit { // Load questions. await this.loadPage(this.currentPage); + + if (!this.fetchSuccess) { + this.fetchSuccess = true; + CoreUtils.ignoreErrors( + AddonModQuiz.logViewAttemptReview(this.attemptId, this.quiz.id, this.quiz.name), + ); + } } catch (error) { CoreDomUtils.showErrorModalDefault(error, 'addon.mod_quiz.errorgetquiz', true); } diff --git a/src/addons/mod/resource/components/index/index.ts b/src/addons/mod/resource/components/index/index.ts index 6c26ec16e..6d6942ca2 100644 --- a/src/addons/mod/resource/components/index/index.ts +++ b/src/addons/mod/resource/components/index/index.ts @@ -90,12 +90,6 @@ export class AddonModResourceIndexComponent extends CoreCourseModuleMainResource }); await this.loadContent(); - try { - await AddonModResource.logView(this.module.instance, this.module.name); - CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata); - } catch { - // Ignore errors. - } } /** @@ -189,6 +183,13 @@ export class AddonModResourceIndexComponent extends CoreCourseModuleMainResource } } + /** + * @inheritdoc + */ + protected async logActivity(): Promise { + await AddonModResource.logView(this.module.instance, this.module.name); + } + /** * Opens a file. * diff --git a/src/addons/mod/scorm/components/index/index.ts b/src/addons/mod/scorm/components/index/index.ts index fcfa22b41..ddc814819 100644 --- a/src/addons/mod/scorm/components/index/index.ts +++ b/src/addons/mod/scorm/components/index/index.ts @@ -16,7 +16,6 @@ import { CoreConstants } from '@/core/constants'; import { Component, OnInit, Optional } from '@angular/core'; import { CoreCourseModuleMainActivityComponent } from '@features/course/classes/main-activity-component'; import { CoreCourseContentsPage } from '@features/course/pages/contents/contents'; -import { CoreCourse } from '@features/course/services/course'; import { IonContent } from '@ionic/angular'; import { CoreNavigator } from '@services/navigator'; import { CoreSync } from '@services/sync'; @@ -114,21 +113,6 @@ export class AddonModScormIndexComponent extends CoreCourseModuleMainActivityCom if (this.skip) { this.open(); } - - try { - await AddonModScorm.logView(this.scorm.id, this.scorm.name); - - this.checkCompletion(); - } catch { - // Ignore errors. - } - } - - /** - * Check the completion. - */ - protected checkCompletion(): void { - CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata); } /** @@ -357,6 +341,17 @@ export class AddonModScormIndexComponent extends CoreCourseModuleMainActivityCom this.gradeFormatted = AddonModScorm.formatGrade(scorm, this.grade); } + /** + * @inheritdoc + */ + protected async logActivity(): Promise { + if (!this.scorm) { + return; // Shouldn't happen. + } + + await AddonModScorm.logView(this.scorm.id, this.scorm.name); + } + /** * Checks if sync has succeed from result sync data. * diff --git a/src/addons/mod/survey/components/index/index.ts b/src/addons/mod/survey/components/index/index.ts index eca06619b..28845b771 100644 --- a/src/addons/mod/survey/components/index/index.ts +++ b/src/addons/mod/survey/components/index/index.ts @@ -16,7 +16,6 @@ import { Component, OnInit, Optional } from '@angular/core'; import { CoreIonLoadingElement } from '@classes/ion-loading'; import { CoreCourseModuleMainActivityComponent } from '@features/course/classes/main-activity-component'; import { CoreCourseContentsPage } from '@features/course/pages/contents/contents'; -import { CoreCourse } from '@features/course/services/course'; import { IonContent } from '@ionic/angular'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; @@ -75,13 +74,6 @@ export class AddonModSurveyIndexComponent extends CoreCourseModuleMainActivityCo this.currentUserId = CoreSites.getCurrentSiteUserId(); await this.loadContent(false, true); - - try { - await AddonModSurvey.logView(this.survey!.id, this.survey!.name); - CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata); - } catch { - // Ignore errors. Just don't check Module completion. - } } /** @@ -166,6 +158,17 @@ export class AddonModSurveyIndexComponent extends CoreCourseModuleMainActivityCo }); } + /** + * @inheritdoc + */ + protected async logActivity(): Promise { + if (!this.survey) { + return; // Shouldn't happen. + } + + await AddonModSurvey.logView(this.survey.id, this.survey.name); + } + /** * Check if answers are valid to be submitted. * diff --git a/src/addons/mod/url/components/index/index.ts b/src/addons/mod/url/components/index/index.ts index b79ee6610..9c253a334 100644 --- a/src/addons/mod/url/components/index/index.ts +++ b/src/addons/mod/url/components/index/index.ts @@ -47,6 +47,8 @@ export class AddonModUrlIndexComponent extends CoreCourseModuleMainResourceCompo mimetype?: string; displayDescription = true; + protected checkCompletionAfterLog = false; + constructor(@Optional() courseContentsPage?: CoreCourseContentsPage) { super('AddonModUrlIndexComponent', courseContentsPage); } @@ -58,12 +60,6 @@ export class AddonModUrlIndexComponent extends CoreCourseModuleMainResourceCompo super.ngOnInit(); await this.loadContent(); - - if ((this.shouldIframe || - (this.shouldEmbed && this.isOther)) || - (!this.shouldIframe && (!this.shouldEmbed || !this.isOther))) { - this.logView(); - } } /** @@ -170,12 +166,24 @@ export class AddonModUrlIndexComponent extends CoreCourseModuleMainResourceCompo protected async logView(): Promise { try { await AddonModUrl.logView(this.module.instance, this.module.name); - CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata); + + this.checkCompletion(); } catch { // Ignore errors. } } + /** + * @inheritdoc + */ + protected async logActivity(): Promise { + if ((this.shouldIframe || + (this.shouldEmbed && this.isOther)) || + (!this.shouldIframe && (!this.shouldEmbed || !this.isOther))) { + this.logView(); + } + } + /** * Opens a file. */ diff --git a/src/addons/mod/wiki/components/index/index.ts b/src/addons/mod/wiki/components/index/index.ts index a9b8780de..bbe537f99 100644 --- a/src/addons/mod/wiki/components/index/index.ts +++ b/src/addons/mod/wiki/components/index/index.ts @@ -148,24 +148,6 @@ export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComp this.openMap(); } } - - if (!this.wiki) { - CoreNavigator.back(); - - return; - } - - if (!this.pageId) { - try { - await AddonModWiki.logView(this.wiki.id, this.wiki.name); - - CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata); - } catch { - // Ignore errors. - } - } else { - CoreUtils.ignoreErrors(AddonModWiki.logPageView(this.pageId, this.wiki.id, this.wiki.name)); - } } /** @@ -449,6 +431,22 @@ export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComp } } + /** + * @inheritdoc + */ + protected async logActivity(): Promise { + if (!this.wiki) { + return; // Shouldn't happen. + } + + if (!this.pageId) { + await AddonModWiki.logView(this.wiki.id, this.wiki.name); + } else { + this.checkCompletionAfterLog = false; + CoreUtils.ignoreErrors(AddonModWiki.logPageView(this.pageId, this.wiki.id, this.wiki.name)); + } + } + /** * Get path to the wiki home view. If cannot determine or it's current view, return undefined. * diff --git a/src/addons/mod/workshop/components/index/index.ts b/src/addons/mod/workshop/components/index/index.ts index 1376c7433..52808f0a9 100644 --- a/src/addons/mod/workshop/components/index/index.ts +++ b/src/addons/mod/workshop/components/index/index.ts @@ -16,7 +16,6 @@ import { Component, Input, OnDestroy, OnInit, Optional } from '@angular/core'; import { Params } from '@angular/router'; import { CoreCourseModuleMainActivityComponent } from '@features/course/classes/main-activity-component'; import { CoreCourseContentsPage } from '@features/course/pages/contents/contents'; -import { CoreCourse } from '@features/course/services/course'; import { IonContent } from '@ionic/angular'; import { CoreGroupInfo, CoreGroups } from '@services/groups'; import { CoreNavigator } from '@services/navigator'; @@ -138,16 +137,6 @@ export class AddonModWorkshopIndexComponent extends CoreCourseModuleMainActivity super.ngOnInit(); await this.loadContent(false, true); - if (!this.workshop) { - return; - } - - try { - await AddonModWorkshop.logView(this.workshop.id, this.workshop.name); - CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata); - } catch (error) { - // Ignore errors. - } } /** @@ -164,7 +153,7 @@ export class AddonModWorkshopIndexComponent extends CoreCourseModuleMainActivity this.showLoadingAndRefresh(true); // Check completion since it could be configured to complete once the user adds a new discussion or replies. - CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata); + this.checkCompletion(); } } @@ -257,6 +246,17 @@ export class AddonModWorkshopIndexComponent extends CoreCourseModuleMainActivity await this.setPhaseInfo(); } + /** + * @inheritdoc + */ + protected async logActivity(): Promise { + if (!this.workshop) { + return; // Shouldn't happen. + } + + await AddonModWorkshop.logView(this.workshop.id, this.workshop.name); + } + /** * Retrieves and shows submissions grade page. * diff --git a/src/addons/mod/workshop/pages/submission/submission.ts b/src/addons/mod/workshop/pages/submission/submission.ts index a131065af..e6c1fa6a2 100644 --- a/src/addons/mod/workshop/pages/submission/submission.ts +++ b/src/addons/mod/workshop/pages/submission/submission.ts @@ -102,6 +102,7 @@ export class AddonModWorkshopSubmissionPage implements OnInit, OnDestroy, CanLea protected obsAssessmentSaved: CoreEventObserver; protected syncObserver: CoreEventObserver; protected isDestroyed = false; + protected fetchSuccess = false; constructor( protected fb: FormBuilder, @@ -157,12 +158,7 @@ export class AddonModWorkshopSubmissionPage implements OnInit, OnDestroy, CanLea await this.fetchSubmissionData(); - try { - await AddonModWorkshop.logViewSubmission(this.submissionId, this.workshopId, this.workshop.name); - CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata); - } catch { - // Ignore errors. - } + this.logView(); } /** @@ -447,6 +443,8 @@ export class AddonModWorkshopSubmissionPage implements OnInit, OnDestroy, CanLea CoreEvents.trigger(AddonModWorkshopProvider.ASSESSMENT_INVALIDATED, null, this.siteId); await this.fetchSubmissionData(); + + this.logView(); } } @@ -596,6 +594,24 @@ export class AddonModWorkshopSubmissionPage implements OnInit, OnDestroy, CanLea }); } + /** + * Log submission viewed. + */ + protected async logView(): Promise { + if (this.fetchSuccess) { + return; // Already done. + } + + this.fetchSuccess = true; + + try { + await AddonModWorkshop.logViewSubmission(this.submissionId, this.workshopId, this.workshop.name); + CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata); + } catch { + // Ignore errors. + } + } + /** * Component being destroyed. */ diff --git a/src/addons/notes/pages/list/list.page.ts b/src/addons/notes/pages/list/list.page.ts index be375f2dd..64c0a373d 100644 --- a/src/addons/notes/pages/list/list.page.ts +++ b/src/addons/notes/pages/list/list.page.ts @@ -54,6 +54,7 @@ export class AddonNotesListPage implements OnInit, OnDestroy { currentUserId!: number; protected syncObserver!: CoreEventObserver; + protected logAfterFetch = true; constructor() { try { @@ -91,8 +92,6 @@ export class AddonNotesListPage implements OnInit, OnDestroy { */ async ngOnInit(): Promise { await this.fetchNotes(true); - - CoreUtils.ignoreErrors(AddonNotes.logView(this.courseId, this.userId)); } /** @@ -128,6 +127,11 @@ export class AddonNotesListPage implements OnInit, OnDestroy { } else { this.notes = await AddonNotes.getNotesUserData(notesList); } + + if (this.logAfterFetch) { + this.logAfterFetch = false; + CoreUtils.ignoreErrors(AddonNotes.logView(this.courseId, this.userId)); + } } catch (error) { CoreDomUtils.showErrorModal(error); } finally { @@ -172,9 +176,9 @@ export class AddonNotesListPage implements OnInit, OnDestroy { this.notesLoaded = false; this.refreshIcon = CoreConstants.ICON_LOADING; this.syncIcon = CoreConstants.ICON_LOADING; + this.logAfterFetch = true; await this.fetchNotes(true); - CoreUtils.ignoreErrors(AddonNotes.logView(this.courseId, this.userId)); } /** diff --git a/src/core/classes/items-management/list-items-manager.ts b/src/core/classes/items-management/list-items-manager.ts index 4fae782f3..aceac334a 100644 --- a/src/core/classes/items-management/list-items-manager.ts +++ b/src/core/classes/items-management/list-items-manager.ts @@ -34,6 +34,7 @@ export class CoreListItemsManager< protected pageRouteLocator?: unknown | ActivatedRoute; protected splitView?: CoreSplitViewComponent; protected splitViewOutletSubscription?: Subscription; + protected fetchSuccess = false; // Whether a fetch was finished successfully. constructor(source: Source, pageRouteLocator: unknown | ActivatedRoute) { super(source); @@ -71,9 +72,6 @@ export class CoreListItemsManager< // Calculate current selected item. this.updateSelectedItem(); - - // Log activity. - await CoreUtils.ignoreErrors(this.logActivity()); } /** @@ -145,6 +143,8 @@ export class CoreListItemsManager< */ async reload(): Promise { await this.getSource().reload(); + + this.finishSuccessfulFetch(); } /** @@ -152,6 +152,21 @@ export class CoreListItemsManager< */ async load(): Promise { await this.getSource().load(); + + this.finishSuccessfulFetch(); + } + + /** + * Finish a successful fetch. + */ + protected async finishSuccessfulFetch(): Promise { + if (this.fetchSuccess) { + return; // Already treated. + } + + // Log activity. + this.fetchSuccess = true; + await CoreUtils.ignoreErrors(this.logActivity()); } /** diff --git a/src/core/features/course/classes/main-activity-component.ts b/src/core/features/course/classes/main-activity-component.ts index e41b66bfb..adcd02751 100644 --- a/src/core/features/course/classes/main-activity-component.ts +++ b/src/core/features/course/classes/main-activity-component.ts @@ -169,6 +169,8 @@ export class CoreCourseModuleMainActivityComponent extends CoreCourseModuleMainR } await this.fetchContent(refresh, sync, showErrors); + + this.finishSuccessfulFetch(); } catch (error) { if (!refresh && !CoreSites.getCurrentSite()?.isOfflineDisabled() && this.isNotFoundError(error)) { // Module not found, retry without using cache. diff --git a/src/core/features/course/classes/main-resource-component.ts b/src/core/features/course/classes/main-resource-component.ts index 2a0eed662..1c3d9600f 100644 --- a/src/core/features/course/classes/main-resource-component.ts +++ b/src/core/features/course/classes/main-resource-component.ts @@ -72,6 +72,8 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, protected showCompletion = false; // Whether to show completion inside the activity. protected displayDescription = true; // Wether to show Module description on module page, and not on summary or the contrary. protected isDestroyed = false; // Whether the component is destroyed. + protected fetchSuccess = false; // Whether a fetch was finished successfully. + protected checkCompletionAfterLog = true; // Whether to check if completion has changed after calling logActivity. constructor( @Optional() @Inject('') loggerName: string = 'CoreCourseModuleMainResourceComponent', @@ -191,6 +193,8 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, try { await this.fetchContent(refresh); + + this.finishSuccessfulFetch(); } catch (error) { if (!refresh && !CoreSites.getCurrentSite()?.isOfflineDisabled() && this.isNotFoundError(error)) { // Module not found, retry without using cache. @@ -427,6 +431,46 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, } } + /** + * Finish a successful fetch. + * + * @return Promise resolved when done. + */ + protected async finishSuccessfulFetch(): Promise { + if (this.fetchSuccess) { + return; // Already treated. + } + + this.fetchSuccess = true; + + // Log activity now. + try { + await this.logActivity(); + + if (this.checkCompletionAfterLog) { + this.checkCompletion(); + } + } catch { + // Ignore errors. + } + } + + /** + * Log activity. + * + * @return Promise resolved when done. + */ + protected async logActivity(): Promise { + // To be overridden. + } + + /** + * Check the module completion. + */ + protected checkCompletion(): void { + CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata); + } + /** * Component being destroyed. */ diff --git a/src/core/features/grades/pages/course/course.page.ts b/src/core/features/grades/pages/course/course.page.ts index 22fadfa71..f877aba9c 100644 --- a/src/core/features/grades/pages/course/course.page.ts +++ b/src/core/features/grades/pages/course/course.page.ts @@ -54,6 +54,8 @@ export class CoreGradesCoursePage implements AfterViewInit, OnDestroy { totalColumnsSpan?: number; withinSplitView?: boolean; + protected fetchSuccess = false; + constructor( protected route: ActivatedRoute, protected element: ElementRef, @@ -93,7 +95,6 @@ export class CoreGradesCoursePage implements AfterViewInit, OnDestroy { await this.courses?.start(); await this.fetchInitialGrades(); - await CoreGrades.logCourseGradesView(this.courseId, this.userId); } /** @@ -198,6 +199,11 @@ export class CoreGradesCoursePage implements AfterViewInit, OnDestroy { this.columns = formattedTable.columns; this.rows = formattedTable.rows; this.totalColumnsSpan = formattedTable.columns.reduce((total, column) => total + column.colspan, 0); + + if (!this.fetchSuccess) { + this.fetchSuccess = true; + await CoreGrades.logCourseGradesView(this.courseId, this.userId); + } } } diff --git a/src/core/features/sitehome/pages/index/index.ts b/src/core/features/sitehome/pages/index/index.ts index 087589a5d..dbfc682e8 100644 --- a/src/core/features/sitehome/pages/index/index.ts +++ b/src/core/features/sitehome/pages/index/index.ts @@ -54,6 +54,7 @@ export class CoreSiteHomeIndexPage implements OnInit, OnDestroy { newsForumModule?: CoreCourseModuleData; protected updateSiteObserver: CoreEventObserver; + protected fetchSuccess = false; constructor() { // Refresh the enabled flags if site is updated. @@ -137,13 +138,15 @@ export class CoreSiteHomeIndexPage implements OnInit, OnDestroy { this.hasContent = result.hasContent || this.hasContent; } - // Add log in Moodle. - CoreUtils.ignoreErrors(CoreCourse.logView( - this.siteHomeId, - undefined, - undefined, - this.currentSite.getInfo()?.sitename, - )); + if (!this.fetchSuccess) { + this.fetchSuccess = true; + CoreUtils.ignoreErrors(CoreCourse.logView( + this.siteHomeId, + undefined, + undefined, + this.currentSite.getInfo()?.sitename, + )); + } } catch (error) { CoreDomUtils.showErrorModalDefault(error, 'core.course.couldnotloadsectioncontent', true); } diff --git a/src/core/features/user/pages/profile/profile.page.ts b/src/core/features/user/pages/profile/profile.page.ts index ba3061238..b22146d00 100644 --- a/src/core/features/user/pages/profile/profile.page.ts +++ b/src/core/features/user/pages/profile/profile.page.ts @@ -48,6 +48,7 @@ export class CoreUserProfilePage implements OnInit, OnDestroy { protected site!: CoreSite; protected obsProfileRefreshed: CoreEventObserver; protected subscription?: Subscription; + protected fetchSuccess = false; userLoaded = false; isLoadingHandlers = false; @@ -106,18 +107,6 @@ export class CoreUserProfilePage implements OnInit, OnDestroy { try { await this.fetchUser(); - - if (!this.user) { - return; - } - - try { - await CoreUser.logView(this.userId, this.courseId, this.user.fullname); - } catch (error) { - this.isDeleted = error?.errorcode === 'userdeleted' || error?.errorcode === 'wsaccessuserdeleted'; - this.isSuspended = error?.errorcode === 'wsaccessusersuspended'; - this.isEnrolled = error?.errorcode !== 'notenrolledprofile'; - } } finally { this.userLoaded = true; } @@ -162,6 +151,18 @@ export class CoreUserProfilePage implements OnInit, OnDestroy { this.isLoadingHandlers = !CoreUserDelegate.areHandlersLoaded(user.id, context, this.courseId); }); + if (!this.fetchSuccess) { + this.fetchSuccess = true; + + try { + await CoreUser.logView(this.userId, this.courseId, this.user.fullname); + } catch (error) { + this.isDeleted = error?.errorcode === 'userdeleted' || error?.errorcode === 'wsaccessuserdeleted'; + this.isSuspended = error?.errorcode === 'wsaccessusersuspended'; + this.isEnrolled = error?.errorcode !== 'notenrolledprofile'; + } + } + } catch (error) { // Error is null for deleted users, do not show the modal. CoreDomUtils.showErrorModal(error);