diff --git a/src/addons/mod/data/classes/field-plugin-component.ts b/src/addons/mod/data/classes/field-plugin-component.ts index 9c0a79948..c48a136a8 100644 --- a/src/addons/mod/data/classes/field-plugin-component.ts +++ b/src/addons/mod/data/classes/field-plugin-component.ts @@ -32,10 +32,11 @@ export abstract class AddonModDataFieldPluginComponent implements OnInit, OnChan @Input() error?: string; // Error when editing. @Input() form?: FormGroup; // Form where to add the form control. Just required for edit and search modes. @Input() searchFields?: CoreFormFields; // The search value of all fields. - @Output() gotoEntry: EventEmitter; // Action to perform. + @Output() gotoEntry = new EventEmitter(); // Action to perform. + // Output called when the field is initialized with a value and it didn't have one already. + @Output() onFieldInit = new EventEmitter(); constructor(protected fb: FormBuilder) { - this.gotoEntry = new EventEmitter(); } /** @@ -114,3 +115,11 @@ export abstract class AddonModDataFieldPluginComponent implements OnInit, OnChan } } + +/** + * Data for an initialized field. + */ +export type AddonModDataEntryFieldInitialized = Partial & { + fieldid: number; + content: string; +}; diff --git a/src/addons/mod/data/components/field-plugin/field-plugin.ts b/src/addons/mod/data/components/field-plugin/field-plugin.ts index 74ed1e710..b1feba23d 100644 --- a/src/addons/mod/data/components/field-plugin/field-plugin.ts +++ b/src/addons/mod/data/components/field-plugin/field-plugin.ts @@ -16,6 +16,7 @@ import { Component, OnInit, OnChanges, ViewChild, Input, Output, SimpleChange, T import { FormGroup } from '@angular/forms'; import { CoreDynamicComponent } from '@components/dynamic-component/dynamic-component'; import { CoreFormFields } from '@singletons/form'; +import { AddonModDataEntryFieldInitialized } from '../../classes/field-plugin-component'; import { AddonModDataData, AddonModDataField, AddonModDataTemplateMode } from '../../services/data'; import { AddonModDataFieldsDelegate } from '../../services/data-fields-delegate'; @@ -37,7 +38,9 @@ export class AddonModDataFieldPluginComponent implements OnInit, OnChanges { @Input() error?: string; // Error when editing. @Input() form?: FormGroup; // Form where to add the form control. Just required for edit and search modes. @Input() searchFields?: CoreFormFields; // The search value of all fields. - @Output() gotoEntry = new EventEmitter(); // Action to perform. + @Output() gotoEntry = new EventEmitter(); // Action to perform. + // Output called when the field is initialized with a value and it didn't have one already. + @Output() onFieldInit = new EventEmitter(); fieldComponent?: Type; // Component to render the plugin. pluginData?: AddonDataFieldPluginComponentData; // Data to pass to the component. @@ -65,9 +68,10 @@ export class AddonModDataFieldPluginComponent implements OnInit, OnChanges { value: this.value, database: this.database, error: this.error, - gotoEntry: this.gotoEntry, form: this.form, searchFields: this.searchFields, + gotoEntry: this.gotoEntry, + onFieldInit: this.onFieldInit, }; } } finally { @@ -99,5 +103,6 @@ export type AddonDataFieldPluginComponentData = { error?: string; // Error when editing. form?: FormGroup; // Form where to add the form control. Just required for edit and search modes. searchFields?: CoreFormFields; // The search value of all fields. - gotoEntry: EventEmitter; + gotoEntry: EventEmitter; + onFieldInit: EventEmitter; }; diff --git a/src/addons/mod/data/fields/date/component/date.ts b/src/addons/mod/data/fields/date/component/date.ts index 5fa517dd7..ad539abf5 100644 --- a/src/addons/mod/data/fields/date/component/date.ts +++ b/src/addons/mod/data/fields/date/component/date.ts @@ -68,7 +68,16 @@ export class AddonModDataFieldDateComponent extends AddonModDataFieldPluginCompo } - this.addControl('f_' + this.field.id, CoreTimeUtils.toDatetimeFormat(date.getTime())); + const seconds = Math.floor(date.getTime() / 1000); + + this.addControl('f_' + this.field.id, CoreTimeUtils.toDatetimeFormat(seconds * 1000)); + + if (!this.searchMode && !this.value?.content) { + this.onFieldInit.emit({ + fieldid: this.field.id, + content: String(seconds), + }); + } } } diff --git a/src/addons/mod/data/pages/edit/edit.ts b/src/addons/mod/data/pages/edit/edit.ts index 068d83431..d93b58cae 100644 --- a/src/addons/mod/data/pages/edit/edit.ts +++ b/src/addons/mod/data/pages/edit/edit.ts @@ -41,6 +41,7 @@ import { } from '../../services/data'; import { AddonModDataHelper } from '../../services/data-helper'; import { CoreDom } from '@singletons/dom'; +import { AddonModDataEntryFieldInitialized } from '../../classes/field-plugin-component'; /** * Page that displays the view edit page. @@ -62,6 +63,7 @@ export class AddonModDataEditPage implements OnInit { protected forceLeave = false; // To allow leaving the page without checking for changes. protected initialSelectedGroup?: number; protected isEditing = false; + protected originalData: AddonModDataEntryFields = {}; entry?: AddonModDataEntry; fields: Record = {}; @@ -83,6 +85,7 @@ export class AddonModDataEditPage implements OnInit { contents: AddonModDataEntryFields; errors?: Record; form: FormGroup; + onFieldInit: (data: AddonModDataEntryFieldInitialized) => void; }; errors: Record = {}; @@ -128,7 +131,7 @@ export class AddonModDataEditPage implements OnInit { const inputData = this.editForm.value; - let changed = AddonModDataHelper.hasEditDataChanged(inputData, this.fieldsArray, this.entry.contents); + let changed = AddonModDataHelper.hasEditDataChanged(inputData, this.fieldsArray, this.originalData); changed = changed || (!this.isEditing && this.initialSelectedGroup != this.selectedGroup); if (changed) { @@ -162,6 +165,7 @@ export class AddonModDataEditPage implements OnInit { const entry = await AddonModDataHelper.fetchEntry(this.database, this.fieldsArray, this.entryId || 0); this.entry = entry.entry; + this.originalData = CoreUtils.clone(this.entry.contents); if (this.entryId) { // Load correct group. @@ -401,6 +405,7 @@ export class AddonModDataEditPage implements OnInit { form: this.editForm, database: this.database, errors: this.errors, + onFieldInit: this.onFieldInit.bind(this), }; let template = AddonModDataHelper.getTemplate(this.database!, AddonModDataTemplateType.ADD, this.fieldsArray); @@ -414,7 +419,7 @@ export class AddonModDataEditPage implements OnInit { // Replace field by a generic directive. const render = ''; + [error]="errors[' + field.id + ']" (onFieldInit)="onFieldInit($event)">'; template = template.replace(replaceRegEx, render); // Replace the field id tag. @@ -435,6 +440,27 @@ export class AddonModDataEditPage implements OnInit { return template; } + /** + * A certain value has been initialized. + * + * @param data Data. + */ + onFieldInit(data: AddonModDataEntryFieldInitialized): void { + if (!this.originalData[data.fieldid]) { + this.originalData[data.fieldid] = { + id: 0, + recordid: this.entry?.id ?? 0, + fieldid: data.fieldid, + content: data.content, + content1: data.content1 ?? null, + content2: data.content2 ?? null, + content3: data.content3 ?? null, + content4: data.content4 ?? null, + files: data.files ?? [], + }; + } + } + /** * Return to the entry list (previous page) discarding temp data. * diff --git a/src/addons/mod/data/pages/entry/entry.ts b/src/addons/mod/data/pages/entry/entry.ts index 6bc442857..761962bb1 100644 --- a/src/addons/mod/data/pages/entry/entry.ts +++ b/src/addons/mod/data/pages/entry/entry.ts @@ -212,7 +212,7 @@ export class AddonModDataEntryPage implements OnInit, OnDestroy { this.logAfterFetch = false; await CoreUtils.ignoreErrors(AddonModData.logView(this.database.id, this.database.name)); - // Store module viewed. It's done in this page because it can be reached using a link. + // Store module viewed because this page also updates recent accessed items block. CoreCourse.storeModuleViewed(this.courseId, this.moduleId); } } catch (error) { diff --git a/src/addons/mod/data/services/data-helper.ts b/src/addons/mod/data/services/data-helper.ts index 64703aedb..e11bc25d9 100644 --- a/src/addons/mod/data/services/data-helper.ts +++ b/src/addons/mod/data/services/data-helper.ts @@ -213,8 +213,8 @@ export class AddonModDataHelperProvider { // Replace field by a generic directive. const render = ''; + '].contents[' + field.id + ']" mode="' + mode + '" [database]="database" (gotoEntry)="gotoEntry($event)">' + + ''; template = template.replace(replaceRegex, render); }); diff --git a/src/addons/mod/data/services/data.ts b/src/addons/mod/data/services/data.ts index 8c7e4e993..297f4204e 100644 --- a/src/addons/mod/data/services/data.ts +++ b/src/addons/mod/data/services/data.ts @@ -1134,10 +1134,10 @@ export type AddonModDataEntryField = { fieldid: number; // The field type of the content. recordid: number; // The record this content belongs to. content: string; // Contents. - content1: string; // Contents. - content2: string; // Contents. - content3: string; // Contents. - content4: string; // Contents. + content1: string | null; // Contents. + content2: string | null; // Contents. + content3: string | null; // Contents. + content4: string | null; // Contents. files: CoreFileEntry[]; }; diff --git a/src/addons/mod/feedback/pages/attempt/attempt.ts b/src/addons/mod/feedback/pages/attempt/attempt.ts index 7ee5e6028..e8981da89 100644 --- a/src/addons/mod/feedback/pages/attempt/attempt.ts +++ b/src/addons/mod/feedback/pages/attempt/attempt.ts @@ -16,7 +16,6 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRouteSnapshot } from '@angular/router'; import { CoreRoutedItemsManagerSourcesTracker } from '@classes/items-management/routed-items-manager-sources-tracker'; import { CoreSwipeNavigationItemsManager } from '@classes/items-management/swipe-navigation-items-manager'; -import { CoreCourse } from '@features/course/services/course'; import { CoreNavigator } from '@services/navigator'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreTextUtils } from '@services/utils/text'; @@ -50,7 +49,6 @@ export class AddonModFeedbackAttemptPage implements OnInit, OnDestroy { loaded = false; protected attemptId: number; - protected fetchSuccess = false; constructor() { this.cmId = CoreNavigator.getRequiredRouteNumberParam('cmId'); @@ -132,12 +130,6 @@ export class AddonModFeedbackAttemptPage implements OnInit, OnDestroy { return attemptItem; }).filter((itemData) => itemData); // Filter items with errors. - - if (!this.fetchSuccess) { - this.fetchSuccess = true; - // Store module viewed. It's done in this page because it can be reached using a link. - CoreCourse.storeModuleViewed(this.courseId, this.cmId); - } } catch (message) { // Some call failed on fetch, go back. CoreDomUtils.showErrorModalDefault(message, 'core.course.errorgetmodule', true); diff --git a/src/addons/mod/feedback/pages/attempts/attempts.ts b/src/addons/mod/feedback/pages/attempts/attempts.ts index 1da06886d..ecea1615d 100644 --- a/src/addons/mod/feedback/pages/attempts/attempts.ts +++ b/src/addons/mod/feedback/pages/attempts/attempts.ts @@ -25,7 +25,6 @@ import { CoreDomUtils } from '@services/utils/dom'; import { CoreUtils } from '@services/utils/utils'; import { AddonModFeedbackAttemptItem, AddonModFeedbackAttemptsSource } from '../../classes/feedback-attempts-source'; import { AddonModFeedbackWSAnonAttempt, AddonModFeedbackWSAttempt } from '../../services/feedback'; -import { CoreCourse } from '@features/course/services/course'; /** * Page that displays feedback attempts. @@ -187,13 +186,4 @@ export class AddonModFeedbackAttemptsPage implements AfterViewInit, OnDestroy { * Attempts manager. */ class AddonModFeedbackAttemptsManager extends CoreListItemsManager { - - /** - * @inheritdoc - */ - protected async logActivity(): Promise { - // Store module viewed. It's done in this page because it can be reached using a link. - CoreCourse.storeModuleViewed(this.getSource().COURSE_ID, this.getSource().CM_ID); - } - } diff --git a/src/addons/mod/feedback/pages/nonrespondents/nonrespondents.ts b/src/addons/mod/feedback/pages/nonrespondents/nonrespondents.ts index 59b4f7ca5..db568a32a 100644 --- a/src/addons/mod/feedback/pages/nonrespondents/nonrespondents.ts +++ b/src/addons/mod/feedback/pages/nonrespondents/nonrespondents.ts @@ -13,7 +13,6 @@ // limitations under the License. import { Component, OnInit } from '@angular/core'; -import { CoreCourse } from '@features/course/services/course'; import { IonRefresher } from '@ionic/angular'; import { CoreGroupInfo, CoreGroups } from '@services/groups'; import { CoreNavigator } from '@services/navigator'; @@ -35,7 +34,6 @@ export class AddonModFeedbackNonRespondentsPage implements OnInit { protected courseId!: number; protected feedback?: AddonModFeedbackWSFeedback; protected page = 0; - protected fetchSuccess = false; selectedGroup!: number; groupInfo?: CoreGroupInfo; @@ -83,12 +81,6 @@ export class AddonModFeedbackNonRespondentsPage implements OnInit { this.selectedGroup = CoreGroups.validateGroupId(this.selectedGroup, this.groupInfo); await this.loadGroupUsers(this.selectedGroup); - - if (!this.fetchSuccess) { - this.fetchSuccess = true; - // Store module viewed. It's done in this page because it can be reached using a link. - CoreCourse.storeModuleViewed(this.courseId, this.cmId); - } } catch (message) { CoreDomUtils.showErrorModalDefault(message, 'core.course.errorgetmodule', true); diff --git a/src/addons/mod/forum/pages/discussion/discussion.page.ts b/src/addons/mod/forum/pages/discussion/discussion.page.ts index a59703a50..4fec08325 100644 --- a/src/addons/mod/forum/pages/discussion/discussion.page.ts +++ b/src/addons/mod/forum/pages/discussion/discussion.page.ts @@ -17,7 +17,6 @@ import { Component, OnDestroy, ViewChild, OnInit, AfterViewInit, ElementRef, Opt import { ActivatedRoute, ActivatedRouteSnapshot } from '@angular/router'; import { CoreRoutedItemsManagerSourcesTracker } from '@classes/items-management/routed-items-manager-sources-tracker'; import { CoreSplitViewComponent } from '@components/split-view/split-view'; -import { CoreCourse } from '@features/course/services/course'; import { CoreFileUploader } from '@features/fileuploader/services/fileuploader'; import { CoreRatingInfo, CoreRatingProvider } from '@features/rating/services/rating'; import { CoreRatingOffline } from '@features/rating/services/rating-offline'; @@ -120,7 +119,6 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes protected ratingOfflineObserver?: CoreEventObserver; protected ratingSyncObserver?: CoreEventObserver; protected changeDiscObserver?: CoreEventObserver; - protected fetchSuccess = false; constructor( @Optional() protected splitView: CoreSplitViewComponent, @@ -547,12 +545,6 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes this.hasOfflineRatings = await CoreRatingOffline.hasRatings('mod_forum', 'post', ContextLevel.MODULE, this.cmId, this.discussionId); - - if (!this.fetchSuccess) { - this.fetchSuccess = true; - // Store module viewed. It's done in this page because it can be reached using a link. - this.courseId && this.cmId && CoreCourse.storeModuleViewed(this.courseId, this.cmId); - } } catch (error) { CoreDomUtils.showErrorModal(error); } finally { diff --git a/src/addons/mod/glossary/pages/entry/entry.ts b/src/addons/mod/glossary/pages/entry/entry.ts index f07b4004b..ca53e83e0 100644 --- a/src/addons/mod/glossary/pages/entry/entry.ts +++ b/src/addons/mod/glossary/pages/entry/entry.ts @@ -17,7 +17,6 @@ import { ActivatedRoute, ActivatedRouteSnapshot } from '@angular/router'; import { CoreRoutedItemsManagerSourcesTracker } from '@classes/items-management/routed-items-manager-sources-tracker'; import { CoreCommentsCommentsComponent } from '@features/comments/components/comments/comments'; import { CoreComments } from '@features/comments/services/comments'; -import { CoreCourse } from '@features/course/services/course'; import { CoreRatingInfo } from '@features/rating/services/rating'; import { CoreTag } from '@features/tag/services/tag'; import { IonRefresher } from '@ionic/angular'; @@ -59,7 +58,6 @@ export class AddonModGlossaryEntryPage implements OnInit, OnDestroy { cmId?: number; protected entryId!: number; - protected fetchSuccess = false; constructor(protected route: ActivatedRoute) {} @@ -148,12 +146,6 @@ export class AddonModGlossaryEntryPage implements OnInit, OnDestroy { this.entry = result.entry; this.ratingInfo = result.ratinginfo; - if (!this.fetchSuccess) { - this.fetchSuccess = true; - // Store module viewed. It's done in this page because it can be reached using a link. - this.cmId && CoreCourse.storeModuleViewed(this.courseId, this.cmId); - } - if (this.glossary) { // Glossary already loaded, nothing else to load. return; 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 458449191..3b3727a2b 100644 --- a/src/addons/mod/h5pactivity/pages/attempt-results/attempt-results.ts +++ b/src/addons/mod/h5pactivity/pages/attempt-results/attempt-results.ts @@ -25,7 +25,6 @@ import { AddonModH5PActivityData, AddonModH5PActivityAttemptResults, } from '../../services/h5pactivity'; -import { CoreCourse } from '@features/course/services/course'; /** * Page that displays results of an attempt. @@ -100,9 +99,6 @@ export class AddonModH5PActivityAttemptResultsPage implements OnInit { this.h5pActivity.name, { attemptId: this.attemptId }, )); - - // Store module viewed. It's done in this page because it can be reached using a link. - CoreCourse.storeModuleViewed(this.courseId, this.cmId); } } catch (error) { CoreDomUtils.showErrorModalDefault(error, 'Error loading attempt.'); 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 a45b31b05..bb95f9fcb 100644 --- a/src/addons/mod/h5pactivity/pages/user-attempts/user-attempts.ts +++ b/src/addons/mod/h5pactivity/pages/user-attempts/user-attempts.ts @@ -26,7 +26,6 @@ import { AddonModH5PActivityData, AddonModH5PActivityUserAttempts, } from '../../services/h5pactivity'; -import { CoreCourse } from '@features/course/services/course'; /** * Page that displays user attempts of a certain user. @@ -102,9 +101,6 @@ export class AddonModH5PActivityUserAttemptsPage implements OnInit { this.h5pActivity.name, { userId: this.userId }, )); - - // Store module viewed. It's done in this page because it can be reached using a link. - CoreCourse.storeModuleViewed(this.courseId, this.cmId); } } catch (error) { CoreDomUtils.showErrorModalDefault(error, 'Error loading attempts.'); 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 2acb07e59..f50cd056c 100644 --- a/src/addons/mod/h5pactivity/pages/users-attempts/users-attempts.ts +++ b/src/addons/mod/h5pactivity/pages/users-attempts/users-attempts.ts @@ -13,7 +13,6 @@ // limitations under the License. import { Component, OnInit } from '@angular/core'; -import { CoreCourse } from '@features/course/services/course'; import { CoreUser, CoreUserProfile } from '@features/user/services/user'; import { IonRefresher } from '@ionic/angular'; @@ -93,9 +92,6 @@ export class AddonModH5PActivityUsersAttemptsPage implements OnInit { if (!this.fetchSuccess) { this.fetchSuccess = true; CoreUtils.ignoreErrors(AddonModH5PActivity.logViewReport(this.h5pActivity.id, this.h5pActivity.name)); - - // Store module viewed. It's done in this page because it can be reached using a link. - CoreCourse.storeModuleViewed(this.courseId, this.cmId); } } catch (error) { CoreDomUtils.showErrorModalDefault(error, 'Error loading attempts.'); diff --git a/src/addons/mod/lesson/pages/user-retake/user-retake.page.ts b/src/addons/mod/lesson/pages/user-retake/user-retake.page.ts index 4ac48bf57..21fbb7fac 100644 --- a/src/addons/mod/lesson/pages/user-retake/user-retake.page.ts +++ b/src/addons/mod/lesson/pages/user-retake/user-retake.page.ts @@ -34,7 +34,6 @@ import { AddonModLessonUserAttemptAnswerPageWSData, } from '../../services/lesson'; import { AddonModLessonAnswerData, AddonModLessonHelper } from '../../services/lesson-helper'; -import { CoreCourse } from '@features/course/services/course'; import { CoreTime } from '@singletons/time'; /** @@ -60,7 +59,6 @@ export class AddonModLessonUserRetakePage implements OnInit { protected userId?: number; // User ID to see the retakes. protected retakeNumber?: number; // Number of the initial retake to see. protected previousSelectedRetake?: number; // To be able to detect the previous selected retake when it has changed. - protected fetchSuccess = false; /** * Component being initialized. @@ -162,12 +160,6 @@ export class AddonModLessonUserRetakePage implements OnInit { this.student.profileimageurl = user?.profileimageurl; await this.setRetake(this.selectedRetake); - - if (!this.fetchSuccess) { - this.fetchSuccess = true; - // Store module viewed. It's done in this page because it can be reached using a link. - CoreCourse.storeModuleViewed(this.courseId, this.cmId); - } } catch (error) { CoreDomUtils.showErrorModalDefault(error, 'Error getting data.', true); } diff --git a/src/addons/mod/quiz/pages/review/review.page.ts b/src/addons/mod/quiz/pages/review/review.page.ts index 1a3a4c3f6..957d86304 100644 --- a/src/addons/mod/quiz/pages/review/review.page.ts +++ b/src/addons/mod/quiz/pages/review/review.page.ts @@ -13,7 +13,6 @@ // limitations under the License. import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'; -import { CoreCourse } from '@features/course/services/course'; import { CoreQuestionQuestionParsed } from '@features/question/services/question'; import { CoreQuestionHelper } from '@features/question/services/question-helper'; import { IonContent, IonRefresher } from '@ionic/angular'; @@ -162,9 +161,6 @@ export class AddonModQuizReviewPage implements OnInit { CoreUtils.ignoreErrors( AddonModQuiz.logViewAttemptReview(this.attemptId, this.quiz.id, this.quiz.name), ); - - // Store module viewed. It's done in this page because it can be reached using a link. - CoreCourse.storeModuleViewed(this.courseId, this.cmId); } } catch (error) { CoreDomUtils.showErrorModalDefault(error, 'addon.mod_quiz.errorgetquiz', true); diff --git a/src/addons/mod/wiki/components/index/index.ts b/src/addons/mod/wiki/components/index/index.ts index 558db1026..fac51cd87 100644 --- a/src/addons/mod/wiki/components/index/index.ts +++ b/src/addons/mod/wiki/components/index/index.ts @@ -447,6 +447,16 @@ export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComp } } + /** + * @inheritdoc + */ + protected async storeModuleViewed(): Promise { + // Only store module viewed when viewing the main page. + if (!this.pageId) { + await super.storeModuleViewed(); + } + } + /** * Get path to the wiki home view. If cannot determine or it's current view, return undefined. * diff --git a/src/core/features/comments/pages/viewer/viewer.html b/src/core/features/comments/pages/viewer/viewer.html index 8a909686e..95127219c 100644 --- a/src/core/features/comments/pages/viewer/viewer.html +++ b/src/core/features/comments/pages/viewer/viewer.html @@ -18,7 +18,7 @@ - @@ -31,7 +31,7 @@ - + diff --git a/src/core/features/comments/pages/viewer/viewer.page.ts b/src/core/features/comments/pages/viewer/viewer.page.ts index fba333730..038983c2f 100644 --- a/src/core/features/comments/pages/viewer/viewer.page.ts +++ b/src/core/features/comments/pages/viewer/viewer.page.ts @@ -30,7 +30,7 @@ import { import { IonContent, IonRefresher } from '@ionic/angular'; import { ContextLevel, CoreConstants } from '@/core/constants'; import { CoreNavigator } from '@services/navigator'; -import { Translate } from '@singletons'; +import { Network, NgZone, Translate } from '@singletons'; import { CoreUtils } from '@services/utils/utils'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreUser } from '@features/user/services/user'; @@ -41,6 +41,7 @@ import { CoreCommentsDBRecord } from '@features/comments/services/database/comme import { CoreTimeUtils } from '@services/utils/time'; import { CoreApp } from '@services/app'; import moment from 'moment'; +import { Subscription } from 'rxjs'; /** * Page that displays comments. @@ -77,9 +78,11 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { currentUserId: number; sending = false; newComment = ''; + isOnline: boolean; protected addDeleteCommentsAvailable = false; protected syncObserver?: CoreEventObserver; + protected onlineObserver: Subscription; protected viewDestroyed = false; constructor( @@ -104,6 +107,14 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { this.fetchComments(false); } }, CoreSites.getCurrentSiteId()); + + this.isOnline = CoreApp.isOnline(); + this.onlineObserver = Network.onChange().subscribe(() => { + // Execute the callback in the Angular zone, so change detection doesn't stop working. + NgZone.run(() => { + this.isOnline = CoreApp.isOnline(); + }); + }); } /** @@ -596,6 +607,7 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { */ ngOnDestroy(): void { this.syncObserver?.off(); + this.onlineObserver.unsubscribe(); this.viewDestroyed = true; } diff --git a/src/core/features/course/classes/main-resource-component.ts b/src/core/features/course/classes/main-resource-component.ts index a9600520c..9e995d682 100644 --- a/src/core/features/course/classes/main-resource-component.ts +++ b/src/core/features/course/classes/main-resource-component.ts @@ -442,7 +442,7 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, } this.fetchSuccess = true; - CoreCourse.storeModuleViewed(this.courseId, this.module.id, { sectionId: this.module.section }); + this.storeModuleViewed(); // Log activity now. try { @@ -456,6 +456,15 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, } } + /** + * Store module as viewed. + * + * @return Promise resolved when done. + */ + protected async storeModuleViewed(): Promise { + await CoreCourse.storeModuleViewed(this.courseId, this.module.id, { sectionId: this.module.section }); + } + /** * Log activity. * diff --git a/src/core/features/mainmenu/components/user-menu/user-menu.ts b/src/core/features/mainmenu/components/user-menu/user-menu.ts index c84bc642a..e3cda789c 100644 --- a/src/core/features/mainmenu/components/user-menu/user-menu.ts +++ b/src/core/features/mainmenu/components/user-menu/user-menu.ts @@ -79,12 +79,15 @@ export class CoreMainMenuUserMenuComponent implements OnInit, OnDestroy { return; } - this.handlers = []; - handlers.forEach((handler) => { - if (handler.type == CoreUserDelegateService.TYPE_NEW_PAGE) { - this.handlers.push(handler.data); - } - }); + const newHandlers = handlers + .filter((handler) => handler.type === CoreUserDelegateService.TYPE_NEW_PAGE) + .map((handler) => handler.data); + + // Only update handlers if they have changed, to prevent a blink effect. + if (newHandlers.length !== this.handlers.length || + JSON.stringify(newHandlers) !== JSON.stringify(this.handlers)) { + this.handlers = newHandlers; + } this.handlersLoaded = CoreUserDelegate.areHandlersLoaded(this.user.id, CoreUserDelegateContext.USER_MENU); });