diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 7f710b31b74..546d1db5335 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -69,7 +69,7 @@ jobs: cat circular-dependencies lines=$(cat circular-dependencies | wc -l) echo "Total circular dependencies: $lines" - test $lines -eq 130 + test $lines -eq 90 - name: JavaScript code compatibility run: | npx check-es-compat www/*.js --polyfills="\{Array,String,TypedArray\}.prototype.at,Object.hasOwn" diff --git a/src/addons/badges/badge-lazy.module.ts b/src/addons/badges/badge-lazy.module.ts index e59049c6620..172a3fc6098 100644 --- a/src/addons/badges/badge-lazy.module.ts +++ b/src/addons/badges/badge-lazy.module.ts @@ -30,4 +30,4 @@ const routes: Routes = [ RouterModule.forChild(routes), ], }) -export class AddonBadgeLazyModule {} +export default class AddonBadgeLazyModule {} diff --git a/src/addons/badges/badgeclass-lazy.module.ts b/src/addons/badges/badgeclass-lazy.module.ts index c8d0901f379..51a2e38f250 100644 --- a/src/addons/badges/badgeclass-lazy.module.ts +++ b/src/addons/badges/badgeclass-lazy.module.ts @@ -33,4 +33,4 @@ const routes: Routes = [ AddonBadgesBadgeClassPage, ], }) -export class AddonBadgeClassLazyModule {} +export default class AddonBadgeClassLazyModule {} diff --git a/src/addons/badges/badges-lazy.module.ts b/src/addons/badges/badges-lazy.module.ts index db153560e3f..e2b44df54a2 100644 --- a/src/addons/badges/badges-lazy.module.ts +++ b/src/addons/badges/badges-lazy.module.ts @@ -63,4 +63,4 @@ const routes: Routes = [ AddonBadgesUserBadgesPage, ], }) -export class AddonBadgesLazyModule {} +export default class AddonBadgesLazyModule {} diff --git a/src/addons/badges/badges.module.ts b/src/addons/badges/badges.module.ts index 179319ef64d..2bcc796f235 100644 --- a/src/addons/badges/badges.module.ts +++ b/src/addons/badges/badges.module.ts @@ -43,15 +43,15 @@ export async function getBadgesServices(): Promise[]> { const mainMenuRoutes: Routes = [ { path: 'badge', - loadChildren: () => import('./badge-lazy.module').then(m => m.AddonBadgeLazyModule), + loadChildren: () => import('./badge-lazy.module'), }, { path: 'badges', - loadChildren: () => import('./badges-lazy.module').then(m => m.AddonBadgesLazyModule), + loadChildren: () => import('./badges-lazy.module'), }, { path: 'badgeclass', - loadChildren: () => import('./badgeclass-lazy.module').then(m => m.AddonBadgeClassLazyModule), + loadChildren: () => import('./badgeclass-lazy.module'), }, ]; diff --git a/src/addons/badges/pages/badge-class/badge-class.ts b/src/addons/badges/pages/badge-class/badge-class.ts index 0184cda39f4..f7c6c9bbb3a 100644 --- a/src/addons/badges/pages/badge-class/badge-class.ts +++ b/src/addons/badges/pages/badge-class/badge-class.ts @@ -14,7 +14,7 @@ import { Component, OnInit } from '@angular/core'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreNavigator } from '@services/navigator'; import { ActivatedRoute } from '@angular/router'; import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics'; @@ -81,7 +81,7 @@ export class AddonBadgesBadgeClassPage implements OnInit { * @param refresher Refresher. */ async refreshBadgeClass(refresher?: HTMLIonRefresherElement): Promise { - await CoreUtils.ignoreErrors(AddonBadges.invalidateBadgeClass(this.badgeId)); + await CorePromiseUtils.ignoreErrors(AddonBadges.invalidateBadgeClass(this.badgeId)); await this.fetchBadgeClass(); diff --git a/src/addons/badges/pages/issued-badge/issued-badge.ts b/src/addons/badges/pages/issued-badge/issued-badge.ts index 0b94c5874cb..44f0e54055d 100644 --- a/src/addons/badges/pages/issued-badge/issued-badge.ts +++ b/src/addons/badges/pages/issued-badge/issued-badge.ts @@ -18,7 +18,7 @@ import { CoreDomUtils } from '@services/utils/dom'; import { CoreSites } from '@services/sites'; import { CoreUser } from '@features/user/services/user'; import { AddonBadges, AddonBadgesUserBadge } from '../../services/badges'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreCourses } from '@features/courses/services/courses'; import { CoreNavigator } from '@services/navigator'; import { ActivatedRoute } from '@angular/router'; @@ -151,12 +151,12 @@ export class AddonBadgesIssuedBadgePage implements OnInit, OnDestroy { * @param refresher Refresher. */ async refreshBadges(refresher?: HTMLIonRefresherElement): Promise { - await CoreUtils.allPromisesIgnoringErrors([ + await CorePromiseUtils.allPromisesIgnoringErrors([ AddonBadges.invalidateUserBadges(this.courseId, this.userId), AddonBadges.invalidateUserBadgeByHash(this.badgeHash), ]); - await CoreUtils.ignoreErrors(Promise.all([ + await CorePromiseUtils.ignoreErrors(Promise.all([ this.fetchIssuedBadge(), ])); diff --git a/src/addons/badges/pages/user-badges/user-badges.ts b/src/addons/badges/pages/user-badges/user-badges.ts index eab870748cb..18cca0ec472 100644 --- a/src/addons/badges/pages/user-badges/user-badges.ts +++ b/src/addons/badges/pages/user-badges/user-badges.ts @@ -17,7 +17,7 @@ import { AddonBadges, AddonBadgesUserBadge } from '../../services/badges'; import { CoreTimeUtils } from '@services/utils/time'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreSites } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreSplitViewComponent } from '@components/split-view/split-view'; import { CoreNavigator } from '@services/navigator'; import { CoreListItemsManager } from '@classes/items-management/list-items-manager'; @@ -90,13 +90,13 @@ export class AddonBadgesUserBadgesPage implements AfterViewInit, OnDestroy { * @param refresher Refresher. */ async refreshBadges(refresher?: HTMLIonRefresherElement): Promise { - await CoreUtils.ignoreErrors( + await CorePromiseUtils.ignoreErrors( AddonBadges.invalidateUserBadges( this.badges.getSource().COURSE_ID, this.badges.getSource().USER_ID, ), ); - await CoreUtils.ignoreErrors(this.badges.reload()); + await CorePromiseUtils.ignoreErrors(this.badges.reload()); refresher?.complete(); } diff --git a/src/addons/badges/services/badges.ts b/src/addons/badges/services/badges.ts index 9ed97871309..2bb3b5b53a4 100644 --- a/src/addons/badges/services/badges.ts +++ b/src/addons/badges/services/badges.ts @@ -15,9 +15,9 @@ import { Injectable } from '@angular/core'; import { CoreSites } from '@services/sites'; import { CoreWSExternalWarning } from '@services/ws'; -import { CoreSite } from '@classes/sites/site'; import { makeSingleton } from '@singletons'; import { CoreError } from '@classes/errors/error'; +import { CoreCacheUpdateFrequency } from '@/core/constants'; const ROOT_CACHE_KEY = 'mmaBadges:'; @@ -70,7 +70,7 @@ export class AddonBadgesProvider { }; const preSets = { cacheKey: this.getBadgesCacheKey(courseId, userId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, }; const response = await site.read('core_badges_get_user_badges', data, preSets); @@ -133,7 +133,7 @@ export class AddonBadgesProvider { }; const preSets = { cacheKey: this.getUserBadgeByHashCacheKey(hash), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, }; const response = await site.read( @@ -190,7 +190,7 @@ export class AddonBadgesProvider { }; const preSets = { cacheKey: this.getBadgeClassCacheKey(id), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, }; const response = await site.read( diff --git a/src/addons/badges/services/handlers/push-click.ts b/src/addons/badges/services/handlers/push-click.ts index 8d3dfc90a26..5fa7b4c4e44 100644 --- a/src/addons/badges/services/handlers/push-click.ts +++ b/src/addons/badges/services/handlers/push-click.ts @@ -14,13 +14,14 @@ import { Injectable } from '@angular/core'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { CorePushNotificationsClickHandler } from '@features/pushnotifications/services/push-delegate'; import { AddonBadges } from '../badges'; import { makeSingleton } from '@singletons'; import { CorePushNotificationsNotificationBasicData } from '@features/pushnotifications/services/pushnotifications'; import { CoreNavigator } from '@services/navigator'; import { AddonBadgesHelper } from '../badges-helper'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Handler for badges push notifications clicks. @@ -72,7 +73,7 @@ export class AddonBadgesPushClickHandlerService implements CorePushNotifications } // No hash, open the list of user badges. - await CoreUtils.ignoreErrors( + await CorePromiseUtils.ignoreErrors( AddonBadges.invalidateUserBadges( 0, Number(notification.usertoid), diff --git a/src/addons/badges/services/handlers/tag-area.ts b/src/addons/badges/services/handlers/tag-area.ts index e04b3a08ad9..3c8c729abc6 100644 --- a/src/addons/badges/services/handlers/tag-area.ts +++ b/src/addons/badges/services/handlers/tag-area.ts @@ -13,7 +13,6 @@ // limitations under the License. import { Injectable, Type } from '@angular/core'; -import { CoreTagFeedComponent } from '@features/tag/components/feed/feed'; import { CoreTagAreaHandler } from '@features/tag/services/tag-area-delegate'; import { CoreTagFeedElement, CoreTagHelper } from '@features/tag/services/tag-helper'; import { makeSingleton } from '@singletons'; @@ -45,7 +44,9 @@ export class AddonBadgesTagAreaHandlerService implements CoreTagAreaHandler { /** * @inheritdoc */ - getComponent(): Type | Promise> { + async getComponent(): Promise> { + const { CoreTagFeedComponent } = await import('@features/tag/components/feed/feed'); + return CoreTagFeedComponent; } diff --git a/src/addons/block/activitymodules/components/activitymodules/activitymodules.ts b/src/addons/block/activitymodules/components/activitymodules/activitymodules.ts index 1c240c1c68b..6b273102d52 100644 --- a/src/addons/block/activitymodules/components/activitymodules/activitymodules.ts +++ b/src/addons/block/activitymodules/components/activitymodules/activitymodules.ts @@ -19,7 +19,7 @@ import { CoreBlockBaseComponent } from '@features/block/classes/base-block-compo import { CoreSites } from '@services/sites'; import { ContextLevel, CoreConstants } from '@/core/constants'; import { Translate } from '@singletons'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreObject } from '@singletons/object'; import { CoreNavigator } from '@services/navigator'; import { CoreCourseHelper } from '@features/course/services/course-helper'; import { CoreUrl } from '@singletons/url'; @@ -106,7 +106,7 @@ export class AddonBlockActivityModulesComponent extends CoreBlockBaseComponent i }); // Sort the modnames alphabetically. - modFullNames = CoreUtils.sortValues(modFullNames); + modFullNames = CoreObject.sortValues(modFullNames); for (const modName in modFullNames) { const iconModName = modName === 'resources' ? 'page' : modName; diff --git a/src/addons/block/calendarmonth/services/block-handler.ts b/src/addons/block/calendarmonth/services/block-handler.ts index fa717ab6cb7..e18a1027568 100644 --- a/src/addons/block/calendarmonth/services/block-handler.ts +++ b/src/addons/block/calendarmonth/services/block-handler.ts @@ -19,8 +19,8 @@ import { CoreBlockBaseHandler } from '@features/block/classes/base-block-handler import { CoreCourseBlock } from '@features/course/services/course'; import { Params } from '@angular/router'; import { makeSingleton } from '@singletons'; -import { AddonCalendarMainMenuHandlerService } from '@addons/calendar/services/handlers/mainmenu'; import { ContextLevel } from '@/core/constants'; +import { ADDON_CALENDAR_PAGE_NAME } from '@addons/calendar/constants'; /** * Block handler. @@ -46,7 +46,7 @@ export class AddonBlockCalendarMonthHandlerService extends CoreBlockBaseHandler title: 'addon.block_calendarmonth.pluginname', class: 'addon-block-calendar-month', component: CoreBlockOnlyTitleComponent, - link: AddonCalendarMainMenuHandlerService.PAGE_NAME, + link: ADDON_CALENDAR_PAGE_NAME, linkParams: linkParams, navOptions: { preferCurrentTab: false, diff --git a/src/addons/block/calendarupcoming/services/block-handler.ts b/src/addons/block/calendarupcoming/services/block-handler.ts index be3ff2c1eff..68aa09ffebe 100644 --- a/src/addons/block/calendarupcoming/services/block-handler.ts +++ b/src/addons/block/calendarupcoming/services/block-handler.ts @@ -19,9 +19,9 @@ import { CoreBlockBaseHandler } from '@features/block/classes/base-block-handler import { CoreCourseBlock } from '@features/course/services/course'; import { Params } from '@angular/router'; import { makeSingleton } from '@singletons'; -import { AddonCalendarMainMenuHandlerService } from '@addons/calendar/services/handlers/mainmenu'; import { CoreSites } from '@services/sites'; import { ContextLevel } from '@/core/constants'; +import { ADDON_CALENDAR_PAGE_NAME } from '@addons/calendar/constants'; /** * Block handler. @@ -51,7 +51,7 @@ export class AddonBlockCalendarUpcomingHandlerService extends CoreBlockBaseHandl title: 'addon.block_calendarupcoming.pluginname', class: 'addon-block-calendar-upcoming', component: CoreBlockOnlyTitleComponent, - link: AddonCalendarMainMenuHandlerService.PAGE_NAME, + link: ADDON_CALENDAR_PAGE_NAME, linkParams: linkParams, }; } diff --git a/src/addons/block/myoverview/components/myoverview/myoverview.ts b/src/addons/block/myoverview/components/myoverview/myoverview.ts index 57ada4ddb0f..cb4285b4705 100644 --- a/src/addons/block/myoverview/components/myoverview/myoverview.ts +++ b/src/addons/block/myoverview/components/myoverview/myoverview.ts @@ -17,7 +17,6 @@ import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreTimeUtils } from '@services/utils/time'; import { CoreSites, CoreSitesReadingStrategy } from '@services/sites'; import { - CoreCoursesProvider, CoreCourses, CoreCoursesMyCoursesUpdatedEventData, CoreCourseSummaryData, @@ -27,7 +26,7 @@ import { CoreCourseHelper, CorePrefetchStatusInfo } from '@features/course/servi import { CoreCourseOptionsDelegate } from '@features/course/services/course-options-delegate'; import { CoreBlockBaseComponent } from '@features/block/classes/base-block-component'; import { CoreSite } from '@classes/sites/site'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreText } from '@singletons/text'; import { AddonCourseCompletion } from '@addons/coursecompletion/services/coursecompletion'; @@ -38,6 +37,12 @@ import { PageLoadsManager } from '@classes/page-loads-manager'; import { DownloadStatus } from '@/core/constants'; import { CoreSharedModule } from '@/core/shared.module'; import { CoreCoursesComponentsModule } from '@features/courses/components/components.module'; +import { + CORE_COURSES_MY_COURSES_UPDATED_EVENT, + CoreCoursesMyCoursesUpdatedEventAction, + CORE_COURSES_STATE_FAVOURITE, + CORE_COURSES_STATE_HIDDEN, +} from '@features/courses/constants'; const FILTER_PRIORITY: AddonBlockMyOverviewTimeFilters[] = ['all', 'inprogress', 'future', 'past', 'favourite', 'allincludinghidden', 'hidden']; @@ -137,7 +142,7 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem }, CoreSites.getCurrentSiteId()); this.coursesObserver = CoreEvents.on( - CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, + CORE_COURSES_MY_COURSES_UPDATED_EVENT, (data) => { this.refreshCourseList(data); }, @@ -238,7 +243,7 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem // Invalidate course completion data. promises.push(this.invalidateCourseList().finally(() => - CoreUtils.allPromises(courseIds.map((courseId) => + CorePromiseUtils.allPromises(courseIds.map((courseId) => AddonCourseCompletion.invalidateCourseCompletion(courseId))))); if (courseIds.length == 1) { @@ -250,7 +255,7 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem promises.push(CoreCourses.invalidateCoursesByField('ids', courseIds.join(','))); } - await CoreUtils.allPromises(promises).finally(() => { + await CorePromiseUtils.allPromises(promises).finally(() => { this.prefetchIconsInitialized = false; }); } @@ -428,29 +433,29 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem } /** - * Refresh course list based on a EVENT_MY_COURSES_UPDATED event. + * Refresh course list based on a CORE_COURSES_MY_COURSES_UPDATED_EVENT event. * * @param data Event data. * @returns Promise resolved when done. */ protected async refreshCourseList(data: CoreCoursesMyCoursesUpdatedEventData): Promise { - if (data.action == CoreCoursesProvider.ACTION_ENROL) { + if (data.action === CoreCoursesMyCoursesUpdatedEventAction.ENROL) { // Always update if user enrolled in a course. return this.refreshContent(true); } const course = this.allCourses.find((course) => course.id == data.courseId); - if (data.action == CoreCoursesProvider.ACTION_STATE_CHANGED) { + if (data.action === CoreCoursesMyCoursesUpdatedEventAction.STATE_CHANGED) { if (!course) { // Not found, use WS update. return this.refreshContent(true); } - if (data.state == CoreCoursesProvider.STATE_FAVOURITE) { + if (data.state === CORE_COURSES_STATE_FAVOURITE) { course.isfavourite = !!data.value; } - if (data.state == CoreCoursesProvider.STATE_HIDDEN) { + if (data.state === CORE_COURSES_STATE_HIDDEN) { course.hidden = !!data.value; } @@ -458,7 +463,7 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem await this.filterCourses(); } - if (data.action == CoreCoursesProvider.ACTION_VIEW && data.courseId != CoreSites.getCurrentSiteHomeId()) { + if (data.action === CoreCoursesMyCoursesUpdatedEventAction.VIEW && data.courseId != CoreSites.getCurrentSiteHomeId()) { if (!course) { // Not found, use WS update. return this.refreshContent(true); diff --git a/src/addons/block/recentlyaccessedcourses/components/recentlyaccessedcourses/recentlyaccessedcourses.ts b/src/addons/block/recentlyaccessedcourses/components/recentlyaccessedcourses/recentlyaccessedcourses.ts index ff4e94259f7..133f8f7b40c 100644 --- a/src/addons/block/recentlyaccessedcourses/components/recentlyaccessedcourses/recentlyaccessedcourses.ts +++ b/src/addons/block/recentlyaccessedcourses/components/recentlyaccessedcourses/recentlyaccessedcourses.ts @@ -16,7 +16,6 @@ import { Component, OnInit, OnDestroy } from '@angular/core'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreSites } from '@services/sites'; import { - CoreCoursesProvider, CoreCoursesMyCoursesUpdatedEventData, CoreCourses, CoreCourseSummaryData, @@ -29,10 +28,16 @@ import { import { CoreCourseOptionsDelegate } from '@features/course/services/course-options-delegate'; import { AddonCourseCompletion } from '@addons/coursecompletion/services/coursecompletion'; import { CoreBlockBaseComponent } from '@features/block/classes/base-block-component'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { CoreSite } from '@classes/sites/site'; import { CoreSharedModule } from '@/core/shared.module'; import { CoreCoursesComponentsModule } from '@features/courses/components/components.module'; +import { + CORE_COURSES_MY_COURSES_UPDATED_EVENT, + CoreCoursesMyCoursesUpdatedEventAction, + CORE_COURSES_STATE_FAVOURITE, +} from '@features/courses/constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Component to render a recent courses block. @@ -73,7 +78,7 @@ export class AddonBlockRecentlyAccessedCoursesComponent extends CoreBlockBaseCom this.scrollElementId = `addon-block-recentlyaccessedcourses-scroll-${scrollId}`; this.coursesObserver = CoreEvents.on( - CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, + CORE_COURSES_MY_COURSES_UPDATED_EVENT, (data) => { this.refreshCourseList(data); }, @@ -114,7 +119,7 @@ export class AddonBlockRecentlyAccessedCoursesComponent extends CoreBlockBaseCom // Invalidate course completion data. promises.push(this.invalidateCourseList().finally(() => - CoreUtils.allPromises(courseIds.map((courseId) => + CorePromiseUtils.allPromises(courseIds.map((courseId) => AddonCourseCompletion.invalidateCourseCompletion(courseId))))); if (courseIds.length == 1) { @@ -126,7 +131,7 @@ export class AddonBlockRecentlyAccessedCoursesComponent extends CoreBlockBaseCom promises.push(CoreCourses.invalidateCoursesByField('ids', courseIds.join(','))); } - await CoreUtils.allPromises(promises); + await CorePromiseUtils.allPromises(promises); } /** @@ -170,20 +175,20 @@ export class AddonBlockRecentlyAccessedCoursesComponent extends CoreBlockBaseCom } /** - * Refresh course list based on a EVENT_MY_COURSES_UPDATED event. + * Refresh course list based on a CORE_COURSES_MY_COURSES_UPDATED_EVENT event. * * @param data Event data. * @returns Promise resolved when done. */ protected async refreshCourseList(data: CoreCoursesMyCoursesUpdatedEventData): Promise { - if (data.action == CoreCoursesProvider.ACTION_ENROL) { + if (data.action === CoreCoursesMyCoursesUpdatedEventAction.ENROL) { // Always update if user enrolled in a course. return this.refreshContent(); } const courseIndex = this.courses.findIndex((course) => course.id == data.courseId); const course = this.courses[courseIndex]; - if (data.action == CoreCoursesProvider.ACTION_VIEW && data.courseId != CoreSites.getCurrentSiteHomeId()) { + if (data.action === CoreCoursesMyCoursesUpdatedEventAction.VIEW && data.courseId != CoreSites.getCurrentSiteHomeId()) { if (!course) { // Not found, use WS update. return this.refreshContent(); @@ -196,8 +201,8 @@ export class AddonBlockRecentlyAccessedCoursesComponent extends CoreBlockBaseCom await this.invalidateCourseList(); } - if (data.action == CoreCoursesProvider.ACTION_STATE_CHANGED && - data.state == CoreCoursesProvider.STATE_FAVOURITE && course) { + if (data.action === CoreCoursesMyCoursesUpdatedEventAction.STATE_CHANGED && + data.state === CORE_COURSES_STATE_FAVOURITE && course) { course.isfavourite = !!data.value; await this.invalidateCourseList(); } diff --git a/src/addons/block/recentlyaccesseditems/components/recentlyaccesseditems/recentlyaccesseditems.ts b/src/addons/block/recentlyaccesseditems/components/recentlyaccesseditems/recentlyaccesseditems.ts index 31d199720db..b6d166d85fe 100644 --- a/src/addons/block/recentlyaccesseditems/components/recentlyaccesseditems/recentlyaccesseditems.ts +++ b/src/addons/block/recentlyaccesseditems/components/recentlyaccesseditems/recentlyaccesseditems.ts @@ -21,7 +21,7 @@ import { } from '../../services/recentlyaccesseditems'; import { CoreText } from '@singletons/text'; import { CoreLoadings } from '@services/loadings'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { CoreSharedModule } from '@/core/shared.module'; /** diff --git a/src/addons/block/starredcourses/components/starredcourses/starredcourses.ts b/src/addons/block/starredcourses/components/starredcourses/starredcourses.ts index 8aab432b12e..1b57099eb97 100644 --- a/src/addons/block/starredcourses/components/starredcourses/starredcourses.ts +++ b/src/addons/block/starredcourses/components/starredcourses/starredcourses.ts @@ -15,7 +15,7 @@ import { Component, OnInit, OnDestroy } from '@angular/core'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreSites } from '@services/sites'; -import { CoreCoursesProvider, CoreCoursesMyCoursesUpdatedEventData, CoreCourses } from '@features/courses/services/courses'; +import { CoreCoursesMyCoursesUpdatedEventData, CoreCourses } from '@features/courses/services/courses'; import { CoreCourseSearchedDataWithExtraInfoAndOptions, CoreEnrolledCourseDataWithOptions, @@ -23,11 +23,17 @@ import { import { CoreCourseOptionsDelegate } from '@features/course/services/course-options-delegate'; import { AddonCourseCompletion } from '@addons/coursecompletion/services/coursecompletion'; import { CoreBlockBaseComponent } from '@features/block/classes/base-block-component'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { CoreSite } from '@classes/sites/site'; import { AddonBlockStarredCourse, AddonBlockStarredCourses } from '../../services/starredcourses'; import { CoreSharedModule } from '@/core/shared.module'; import { CoreCoursesComponentsModule } from '@features/courses/components/components.module'; +import { + CORE_COURSES_MY_COURSES_UPDATED_EVENT, + CoreCoursesMyCoursesUpdatedEventAction, + CORE_COURSES_STATE_FAVOURITE, +} from '@features/courses/constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Component to render a starred courses block. @@ -68,7 +74,7 @@ export class AddonBlockStarredCoursesComponent extends CoreBlockBaseComponent im this.scrollElementId = `addon-block-starredcourses-scroll-${scrollId}`; this.coursesObserver = CoreEvents.on( - CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, + CORE_COURSES_MY_COURSES_UPDATED_EVENT, (data) => { this.refreshCourseList(data); }, @@ -108,7 +114,7 @@ export class AddonBlockStarredCoursesComponent extends CoreBlockBaseComponent im // Invalidate course completion data. promises.push(this.invalidateCourseList().finally(() => - CoreUtils.allPromises(courseIds.map((courseId) => + CorePromiseUtils.allPromises(courseIds.map((courseId) => AddonCourseCompletion.invalidateCourseCompletion(courseId))))); if (courseIds.length == 1) { @@ -120,7 +126,7 @@ export class AddonBlockStarredCoursesComponent extends CoreBlockBaseComponent im promises.push(CoreCourses.invalidateCoursesByField('ids', courseIds.join(','))); } - await CoreUtils.allPromises(promises); + await CorePromiseUtils.allPromises(promises); } /** @@ -157,19 +163,19 @@ export class AddonBlockStarredCoursesComponent extends CoreBlockBaseComponent im } /** - * Refresh course list based on a EVENT_MY_COURSES_UPDATED event. + * Refresh course list based on a CORE_COURSES_MY_COURSES_UPDATED_EVENT event. * * @param data Event data. * @returns Promise resolved when done. */ protected async refreshCourseList(data: CoreCoursesMyCoursesUpdatedEventData): Promise { - if (data.action == CoreCoursesProvider.ACTION_ENROL) { + if (data.action === CoreCoursesMyCoursesUpdatedEventAction.ENROL) { // Always update if user enrolled in a course. // New courses shouldn't be favourite by default, but just in case. return this.refreshContent(); } - if (data.action == CoreCoursesProvider.ACTION_STATE_CHANGED && data.state == CoreCoursesProvider.STATE_FAVOURITE) { + if (data.action === CoreCoursesMyCoursesUpdatedEventAction.STATE_CHANGED && data.state == CORE_COURSES_STATE_FAVOURITE) { const courseIndex = this.courses.findIndex((course) => course.id == data.courseId); if (courseIndex < 0) { // Not found, use WS update. Usually new favourite. diff --git a/src/addons/block/timeline/components/timeline/timeline.ts b/src/addons/block/timeline/components/timeline/timeline.ts index 9a814c0e341..fe552eed2bc 100644 --- a/src/addons/block/timeline/components/timeline/timeline.ts +++ b/src/addons/block/timeline/components/timeline/timeline.ts @@ -16,7 +16,7 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { CoreSites } from '@services/sites'; import { ICoreBlockComponent } from '@features/block/classes/base-block-component'; import { AddonBlockTimeline } from '../../services/timeline'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreCoursesHelper, CoreEnrolledCourseDataWithOptions } from '@features/courses/services/courses-helper'; import { CoreCourses } from '@features/courses/services/courses'; @@ -122,7 +122,7 @@ export class AddonBlockTimelineComponent implements OnInit, ICoreBlockComponent * @inheritdoc */ async invalidateContent(): Promise { - await CoreUtils.allPromises([ + await CorePromiseUtils.allPromises([ AddonBlockTimeline.invalidateActionEventsByTimesort(), AddonBlockTimeline.invalidateActionEventsByCourses(), CoreCourses.invalidateUserCourses(), diff --git a/src/addons/blog/blog-lazy.module.ts b/src/addons/blog/blog-lazy.module.ts index 1dcdf88c845..ad4059eb633 100644 --- a/src/addons/blog/blog-lazy.module.ts +++ b/src/addons/blog/blog-lazy.module.ts @@ -42,7 +42,7 @@ import { canLeaveGuard } from '@guards/can-leave'; }, { path: 'edit/:id', - loadComponent: () => import('./pages/edit-entry/edit-entry').then(c => c.AddonBlogEditEntryPage), + loadComponent: () => import('./pages/edit-entry/edit-entry'), canDeactivate: [canLeaveGuard], }, ...buildTabMainRoutes(injector, { @@ -71,4 +71,4 @@ import { canLeaveGuard } from '@guards/can-leave'; }, ], }) -export class AddonBlogLazyModule {} +export default class AddonBlogLazyModule {} diff --git a/src/addons/blog/blog.module.ts b/src/addons/blog/blog.module.ts index 06959bdf308..7e15a9af06f 100644 --- a/src/addons/blog/blog.module.ts +++ b/src/addons/blog/blog.module.ts @@ -37,7 +37,7 @@ import { AddonBlogSyncCronHandler } from './services/handlers/sync-cron'; const routes: Routes = [ { path: ADDON_BLOG_MAINMENU_PAGE_NAME, - loadChildren: () => import('@addons/blog/blog-lazy.module').then(m => m.AddonBlogLazyModule), + loadChildren: () => import('@addons/blog/blog-lazy.module'), }, ]; diff --git a/src/addons/blog/pages/edit-entry/edit-entry.ts b/src/addons/blog/pages/edit-entry/edit-entry.ts index 9eaba2f330a..5f732f19c96 100644 --- a/src/addons/blog/pages/edit-entry/edit-entry.ts +++ b/src/addons/blog/pages/edit-entry/edit-entry.ts @@ -41,12 +41,13 @@ import { CoreNetwork } from '@services/network'; import { CoreSites, CoreSitesReadingStrategy } from '@services/sites'; import { CoreSync } from '@services/sync'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreWSError } from '@classes/errors/wserror'; import { Translate } from '@singletons'; import { CoreEvents } from '@singletons/events'; import { CoreForms } from '@singletons/form'; import { CoreFileEntry } from '@services/file-helper'; import { CoreTimeUtils } from '@services/utils/time'; +import { CorePromiseUtils } from '@singletons/promise-utils'; @Component({ selector: 'addon-blog-edit-entry', @@ -59,7 +60,7 @@ import { CoreTimeUtils } from '@services/utils/time'; CoreTagComponentsModule, ], }) -export class AddonBlogEditEntryPage implements CanLeave, OnInit, OnDestroy { +export default class AddonBlogEditEntryPage implements CanLeave, OnInit, OnDestroy { @ViewChild('editEntryForm') formElement!: ElementRef; @@ -257,7 +258,7 @@ export class AddonBlogEditEntryPage implements CanLeave, OnInit, OnDestroy { return selectedEntry; } catch (error) { - if (!params.filters || CoreUtils.isWebServiceError(error)) { + if (!params.filters || CoreWSError.isWebServiceError(error)) { // Cannot get the entry, reject. throw error; } @@ -323,7 +324,7 @@ export class AddonBlogEditEntryPage implements CanLeave, OnInit, OnDestroy { const filters: AddonBlogFilter | undefined = CoreNavigator.getRouteParam('filters'); const entry = this.entry && 'attachment' in this.entry ? this.entry - : await CoreUtils.ignoreErrors(this.getEntry({ filters, lastModified, entryId: this.entry.id })); + : await CorePromiseUtils.ignoreErrors(this.getEntry({ filters, lastModified, entryId: this.entry.id })); const removedFiles = CoreFileUploader.getFilesToDelete(entry?.attachmentfiles ?? [], this.files); @@ -335,7 +336,7 @@ export class AddonBlogEditEntryPage implements CanLeave, OnInit, OnDestroy { return await this.saveEntry({ attachmentsId: attachmentsid }); } catch (error) { - if (CoreUtils.isWebServiceError(error)) { + if (CoreWSError.isWebServiceError(error)) { // It's a WebService error, the user cannot send the message so don't store it. CoreDomUtils.showErrorModalDefault(error, 'Error updating entry.'); @@ -360,7 +361,7 @@ export class AddonBlogEditEntryPage implements CanLeave, OnInit, OnDestroy { const attachmentsId = await this.uploadOrStoreFiles({ created }); await this.saveEntry({ created, attachmentsId }); } catch (error) { - if (CoreUtils.isWebServiceError(error)) { + if (CoreWSError.isWebServiceError(error)) { // It's a WebService error, the user cannot send the message so don't store it. CoreDomUtils.showErrorModalDefault(error, 'Error creating entry.'); diff --git a/src/addons/blog/pages/index/index.ts b/src/addons/blog/pages/index/index.ts index afd4312451b..64ef920d2ef 100644 --- a/src/addons/blog/pages/index/index.ts +++ b/src/addons/blog/pages/index/index.ts @@ -36,7 +36,7 @@ import { CoreNetwork } from '@services/network'; import { CoreSites, CoreSitesReadingStrategy } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreUrl } from '@singletons/url'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreArray } from '@singletons/array'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreTime } from '@singletons/time'; @@ -93,7 +93,7 @@ export class AddonBlogIndexPage implements OnInit, OnDestroy { this.isOnline.set(CoreNetwork.isOnline()); this.logView = CoreTime.once(async () => { - await CoreUtils.ignoreErrors(AddonBlog.logView(this.filter)); + await CorePromiseUtils.ignoreErrors(AddonBlog.logView(this.filter)); CoreAnalytics.logEvent({ type: CoreAnalyticsEventType.VIEW_ITEM_LIST, @@ -113,7 +113,7 @@ export class AddonBlogIndexPage implements OnInit, OnDestroy { this.entryUpdateObserver = CoreEvents.on(ADDON_BLOG_ENTRY_UPDATED, async () => { this.loaded.set(false); - await CoreUtils.ignoreErrors(this.refresh()); + await CorePromiseUtils.ignoreErrors(this.refresh()); this.loaded.set(true); }); @@ -123,7 +123,7 @@ export class AddonBlogIndexPage implements OnInit, OnDestroy { } this.loaded.set(false); - await CoreUtils.ignoreErrors(this.refresh(false)); + await CorePromiseUtils.ignoreErrors(this.refresh(false)); this.loaded.set(true); }); @@ -375,7 +375,7 @@ export class AddonBlogIndexPage implements OnInit, OnDestroy { * @param refresher Refresher instance. */ async refresh(sync = true, refresher?: HTMLIonRefresherElement): Promise { - const promises = this.entries.map((entry) => { + const promises = this.entries.map(async (entry) => { if (this.isOnlineEntry(entry)) { return CoreComments.invalidateCommentsData( ContextLevel.USER, @@ -399,7 +399,7 @@ export class AddonBlogIndexPage implements OnInit, OnDestroy { } - await CoreUtils.allPromises(promises); + await CorePromiseUtils.allPromises(promises); await this.fetchEntries(true, false, sync); refresher?.complete(); } diff --git a/src/addons/blog/services/blog-offline.ts b/src/addons/blog/services/blog-offline.ts index 3dbcd168a16..df964e28ab8 100644 --- a/src/addons/blog/services/blog-offline.ts +++ b/src/addons/blog/services/blog-offline.ts @@ -17,7 +17,7 @@ import { CoreFileUploader } from '@features/fileuploader/services/fileuploader'; import { CoreFile } from '@services/file'; import { CoreFileEntry } from '@services/file-helper'; import { CoreSites } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { makeSingleton } from '@singletons'; import { CoreObject } from '@singletons/object'; import { CorePath } from '@singletons/path'; @@ -108,7 +108,7 @@ export class AddonBlogOfflineService { */ async getOfflineEntry(filter: { id?: number; created?: number }, siteId?: string): Promise { const site = await CoreSites.getSite(siteId); - const record = await CoreUtils.ignoreErrors( + const record = await CorePromiseUtils.ignoreErrors( site.getDb().getRecord(OFFLINE_BLOG_ENTRIES_TABLE_NAME, filter), ); diff --git a/src/addons/blog/services/blog-sync.ts b/src/addons/blog/services/blog-sync.ts index 953ab556451..e34a43b1822 100644 --- a/src/addons/blog/services/blog-sync.ts +++ b/src/addons/blog/services/blog-sync.ts @@ -17,7 +17,7 @@ import { CoreSyncBaseProvider, CoreSyncBlockedError } from '@classes/base-sync'; import { CoreFileUploader } from '@features/fileuploader/services/fileuploader'; import { CoreSites, CoreSitesReadingStrategy } from '@services/sites'; import { CoreSync, CoreSyncResult } from '@services/sync'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreWSError } from '@classes/errors/wserror'; import { makeSingleton, Translate } from '@singletons'; import { CoreEvents } from '@singletons/events'; import { ADDON_BLOG_AUTO_SYNCED, ADDON_BLOG_SYNC_ID } from '../constants'; @@ -129,7 +129,7 @@ import { AddonBlogOfflineEntryDBRecord } from './database/blog'; await AddonBlogOffline.deleteOfflineEntryRecord({ created: entry.created }, siteId); result.updated = true; } catch (error) { - if (!error || !CoreUtils.isWebServiceError(error)) { + if (!CoreWSError.isWebServiceError(error)) { throw error; } @@ -223,7 +223,7 @@ import { AddonBlogOfflineEntryDBRecord } from './database/blog'; entriesToSync = entriesPendingToSync; } } catch (error) { - if (!CoreUtils.isWebServiceError(error)) { + if (!CoreWSError.isWebServiceError(error)) { throw error; } diff --git a/src/addons/blog/services/blog.ts b/src/addons/blog/services/blog.ts index 7fbfa09985c..0a52eceb7b2 100644 --- a/src/addons/blog/services/blog.ts +++ b/src/addons/blog/services/blog.ts @@ -12,10 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { ContextLevel } from '@/core/constants'; +import { ContextLevel, CoreCacheUpdateFrequency } from '@/core/constants'; import { Injectable } from '@angular/core'; import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site'; -import { CoreSite } from '@classes/sites/site'; import { CoreFileUploaderStoreFilesResult } from '@features/fileuploader/services/fileuploader'; import { CoreTagItem } from '@features/tag/services/tag'; import { CoreUser, CoreUserProfile } from '@features/user/services/user'; @@ -23,10 +22,12 @@ import { CoreFileEntry, CoreFileHelper } from '@services/file-helper'; import { CoreNetwork } from '@services/network'; import { CoreSites, CoreSitesCommonWSOptions } from '@services/sites'; import { CoreTimeUtils } from '@services/utils/time'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreObject } from '@singletons/object'; import { CoreStatusWithWarningsWSResponse, CoreWSExternalFile, CoreWSExternalWarning } from '@services/ws'; import { makeSingleton } from '@singletons'; import { AddonBlogOffline, AddonBlogOfflineEntry } from './blog-offline'; +import { CorePromiseUtils } from '@singletons/promise-utils'; +import { CoreWSError } from '@classes/errors/wserror'; const ROOT_CACHE_KEY = 'addonBlog:'; @@ -62,7 +63,7 @@ export class AddonBlogProvider { * @returns Cache key. */ getEntriesCacheKey(filter: AddonBlogFilter = {}): string { - return ROOT_CACHE_KEY + CoreUtils.sortAndStringify(filter); + return ROOT_CACHE_KEY + CoreObject.sortAndStringify(filter); } /** @@ -76,14 +77,14 @@ export class AddonBlogProvider { const site = await CoreSites.getSite(options?.siteId); const data: CoreBlogGetEntriesWSParams = { - filters: CoreUtils.objectToArrayOfObjects(filter, 'name', 'value'), + filters: CoreObject.toArrayOfObjects(filter, 'name', 'value'), page: options?.page ?? 0, perpage: AddonBlogProvider.ENTRIES_PER_PAGE, }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getEntriesCacheKey(filter), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + updateFrequency: CoreCacheUpdateFrequency.SOMETIMES, ...CoreSites.getReadingStrategyPreSets(options?.readingStrategy), }; @@ -121,7 +122,7 @@ export class AddonBlogProvider { try { await this.addEntryOnline(params, siteId); } catch (error) { - if (!CoreUtils.isWebServiceError(error)) { + if (!CoreWSError.isWebServiceError(error)) { // Couldn't connect to server, store in offline. return await storeOffline(); } @@ -177,7 +178,7 @@ export class AddonBlogProvider { try { await this.updateEntryOnline(params, siteId); } catch (error) { - if (!CoreUtils.isWebServiceError(error)) { + if (!CoreWSError.isWebServiceError(error)) { // Couldn't connect to server, store in offline. return await storeOffline(); } @@ -230,9 +231,9 @@ export class AddonBlogProvider { } await this.deleteEntryOnline(params, siteId); - await CoreUtils.ignoreErrors(AddonBlogOffline.unmarkEntryAsRemoved(params.entryid)); + await CorePromiseUtils.ignoreErrors(AddonBlogOffline.unmarkEntryAsRemoved(params.entryid)); } catch (error) { - if (!CoreUtils.isWebServiceError(error)) { + if (!CoreWSError.isWebServiceError(error)) { // Couldn't connect to server, store in offline. return await AddonBlogOffline.markEntryAsRemoved({ id: params.entryid, subject }, siteId); } @@ -289,7 +290,7 @@ export class AddonBlogProvider { const site = await CoreSites.getSite(siteId); const data: AddonBlogViewEntriesWSParams = { - filters: CoreUtils.objectToArrayOfObjects(filter, 'name', 'value'), + filters: CoreObject.toArrayOfObjects(filter, 'name', 'value'), }; return site.write('core_blog_view_entries', data); @@ -312,7 +313,7 @@ export class AddonBlogProvider { const tags = options?.find(option => option.name === 'tags')?.value as string | undefined; const publishState = options?.find(option => option.name === 'publishstate')?.value as AddonBlogPublishState ?? AddonBlogPublishState.draft; - const user = await CoreUtils.ignoreErrors(CoreUser.getProfile(offlineEntry.userid, courseId, true)); + const user = await CorePromiseUtils.ignoreErrors(CoreUser.getProfile(offlineEntry.userid, courseId, true)); const folder = 'id' in offlineEntry && offlineEntry.id ? { id: offlineEntry.id } : { created: offlineEntry.created }; const offlineFiles = await AddonBlogOffline.getOfflineFiles(folder); const optionsFiles = this.getAttachmentFilesFromOptions(options); @@ -376,7 +377,7 @@ export class AddonBlogProvider { } entry.summary = CoreFileHelper.replacePluginfileUrls(entry.summary, entry.summaryfiles || []); - entry.user = await CoreUtils.ignoreErrors(CoreUser.getProfile(entry.userid, entry.courseid, true)); + entry.user = await CorePromiseUtils.ignoreErrors(CoreUser.getProfile(entry.userid, entry.courseid, true)); } /** diff --git a/src/addons/blog/services/handlers/tag-area.ts b/src/addons/blog/services/handlers/tag-area.ts index 3a01dd0acc1..bf684343e0a 100644 --- a/src/addons/blog/services/handlers/tag-area.ts +++ b/src/addons/blog/services/handlers/tag-area.ts @@ -13,7 +13,6 @@ // limitations under the License. import { Injectable, Type } from '@angular/core'; -import { CoreTagFeedComponent } from '@features/tag/components/feed/feed'; import { CoreTagAreaHandler } from '@features/tag/services/tag-area-delegate'; import { CoreTagFeedElement, CoreTagHelper } from '@features/tag/services/tag-helper'; import { makeSingleton } from '@singletons'; @@ -45,9 +44,11 @@ export class AddonBlogTagAreaHandlerService implements CoreTagAreaHandler { /** * @inheritdoc */ - getComponent(): Type | Promise> { - return CoreTagFeedComponent; - } + async getComponent(): Promise> { + const { CoreTagFeedComponent } = await import('@features/tag/components/feed/feed'); + + return CoreTagFeedComponent; + } } export const AddonBlogTagAreaHandler = makeSingleton(AddonBlogTagAreaHandlerService); diff --git a/src/addons/calendar/calendar-lazy.module.ts b/src/addons/calendar/calendar-lazy.module.ts index f423766d37d..538c6c366b6 100644 --- a/src/addons/calendar/calendar-lazy.module.ts +++ b/src/addons/calendar/calendar-lazy.module.ts @@ -25,7 +25,7 @@ import { CoreEditorComponentsModule } from '@features/editor/components/componen import { CoreMainMenuComponentsModule } from '@features/mainmenu/components/components.module'; import { buildTabMainRoutes } from '@features/mainmenu/mainmenu-tab-routing.module'; -import { AddonCalendarMainMenuHandlerService } from './services/handlers/mainmenu'; +import { ADDON_CALENDAR_PAGE_NAME } from './constants'; /** * Build module routes. @@ -37,7 +37,7 @@ function buildRoutes(injector: Injector): Routes { return [ { path: 'index', - data: { mainMenuTabRoot: AddonCalendarMainMenuHandlerService.PAGE_NAME }, + data: { mainMenuTabRoot: ADDON_CALENDAR_PAGE_NAME }, component: AddonCalendarIndexPage, }, { @@ -86,4 +86,4 @@ function buildRoutes(injector: Injector): Routes { }, ], }) -export class AddonCalendarLazyModule {} +export default class AddonCalendarLazyModule {} diff --git a/src/addons/calendar/calendar.module.ts b/src/addons/calendar/calendar.module.ts index f57cfbc762a..cf90bae0548 100644 --- a/src/addons/calendar/calendar.module.ts +++ b/src/addons/calendar/calendar.module.ts @@ -20,7 +20,7 @@ import { CoreMainMenuDelegate } from '@features/mainmenu/services/mainmenu-deleg import { CoreCronDelegate } from '@services/cron'; import { CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate'; import { AddonCalendarViewLinkHandler } from './services/handlers/view-link'; -import { AddonCalendarMainMenuHandler, AddonCalendarMainMenuHandlerService } from './services/handlers/mainmenu'; +import { AddonCalendarMainMenuHandler } from './services/handlers/mainmenu'; import { AddonCalendarSyncCronHandler } from './services/handlers/sync-cron'; import { CORE_SITE_SCHEMAS } from '@services/sites'; @@ -29,6 +29,7 @@ import { CALENDAR_OFFLINE_SITE_SCHEMA } from './services/database/calendar-offli import { AddonCalendarComponentsModule } from './components/components.module'; import { AddonCalendar } from './services/calendar'; import { CoreMainMenuTabRoutingModule } from '@features/mainmenu/mainmenu-tab-routing.module'; +import { ADDON_CALENDAR_PAGE_NAME } from './constants'; /** * Get calendar services. @@ -51,8 +52,8 @@ export async function getCalendarServices(): Promise[]> { const mainMenuChildrenRoutes: Routes = [ { - path: AddonCalendarMainMenuHandlerService.PAGE_NAME, - loadChildren: () => import('./calendar-lazy.module').then(m => m.AddonCalendarLazyModule), + path: ADDON_CALENDAR_PAGE_NAME, + loadChildren: () => import('./calendar-lazy.module'), }, ]; diff --git a/src/addons/calendar/components/calendar/calendar.ts b/src/addons/calendar/components/calendar/calendar.ts index 2920b385141..46840cabbf2 100644 --- a/src/addons/calendar/components/calendar/calendar.ts +++ b/src/addons/calendar/components/calendar/calendar.ts @@ -29,10 +29,9 @@ import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreTimeUtils } from '@services/utils/time'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreArray } from '@singletons/array'; import { AddonCalendar, - AddonCalendarProvider, AddonCalendarWeek, AddonCalendarWeekDaysTranslationKeys, AddonCalendarEventToDisplay, @@ -54,6 +53,7 @@ import { CoreUrl } from '@singletons/url'; import { CoreTime } from '@singletons/time'; import { Translate } from '@singletons'; import { toBoolean } from '@/core/transforms/boolean'; +import { ADDON_CALENDAR_UNDELETED_EVENT_EVENT } from '@addons/calendar/constants'; /** * Component that displays a calendar. @@ -61,7 +61,7 @@ import { toBoolean } from '@/core/transforms/boolean'; @Component({ selector: 'addon-calendar-calendar', templateUrl: 'addon-calendar-calendar.html', - styleUrls: ['calendar.scss'], + styleUrl: 'calendar.scss', }) export class AddonCalendarCalendarComponent implements OnInit, DoCheck, OnDestroy { @@ -93,7 +93,7 @@ export class AddonCalendarCalendarComponent implements OnInit, DoCheck, OnDestro // Listen for events "undeleted" (offline). this.undeleteEventObserver = CoreEvents.on( - AddonCalendarProvider.UNDELETED_EVENT_EVENT, + ADDON_CALENDAR_UNDELETED_EVENT_EVENT, (data) => { if (!data || !data.eventId) { return; @@ -464,7 +464,7 @@ class AddonCalendarMonthSlidesItemsManagerSource extends CoreSwipeSlidesDynamicI const categories = await CoreCourses.getCategories(0, true); // Index categories by ID. - this.categories = CoreUtils.arrayToObject(categories, 'id'); + this.categories = CoreArray.toObject(categories, 'id'); } catch { // Ignore errors. } diff --git a/src/addons/calendar/components/filter/filter.ts b/src/addons/calendar/components/filter/filter.ts index 94db1a1ca98..8bb3aef4dfd 100644 --- a/src/addons/calendar/components/filter/filter.ts +++ b/src/addons/calendar/components/filter/filter.ts @@ -14,13 +14,13 @@ import { Component, Input, OnInit } from '@angular/core'; import { CoreEnrolledCourseData } from '@features/courses/services/courses'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreObject } from '@singletons/object'; import { ModalController } from '@singletons'; import { CoreEvents } from '@singletons/events'; -import { AddonCalendarEventType, AddonCalendarProvider } from '../../services/calendar'; -import { AddonCalendarFilter, AddonCalendarEventIcons } from '../../services/calendar-helper'; +import { AddonCalendarFilter } from '@addons/calendar/services/calendar-helper'; import { ALL_COURSES_ID } from '@features/courses/services/courses-helper'; import { CoreSharedModule } from '@/core/shared.module'; +import { ADDON_CALENDAR_FILTER_CHANGED_EVENT, AddonCalendarEventIcons, AddonCalendarEventType } from '@addons/calendar/constants'; /** * Component to display the events filter that includes events types and a list of courses. @@ -54,7 +54,7 @@ export class AddonCalendarFilterComponent implements OnInit { sortedCourses: CoreEnrolledCourseData[] = []; constructor() { - CoreUtils.enumKeys(AddonCalendarEventType).forEach((name) => { + CoreObject.enumKeys(AddonCalendarEventType).forEach((name) => { const value = AddonCalendarEventType[name]; this.typeIcons[value] = AddonCalendarEventIcons[name]; this.types.push(value); @@ -95,7 +95,7 @@ export class AddonCalendarFilterComponent implements OnInit { this.filter.filtered = !!this.filter.courseId || this.types.some((name) => !this.filter[name]); - CoreEvents.trigger(AddonCalendarProvider.FILTER_CHANGED_EVENT, this.filter); + CoreEvents.trigger(ADDON_CALENDAR_FILTER_CHANGED_EVENT, this.filter); } /** diff --git a/src/addons/calendar/components/upcoming-events/upcoming-events.ts b/src/addons/calendar/components/upcoming-events/upcoming-events.ts index 753a38b55d5..c96493a032e 100644 --- a/src/addons/calendar/components/upcoming-events/upcoming-events.ts +++ b/src/addons/calendar/components/upcoming-events/upcoming-events.ts @@ -17,7 +17,6 @@ import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { - AddonCalendarProvider, AddonCalendarEventToDisplay, AddonCalendar, } from '../../services/calendar'; @@ -29,6 +28,7 @@ import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics'; import { CoreUrl } from '@singletons/url'; import { CoreTime } from '@singletons/time'; import { Translate } from '@singletons'; +import { ADDON_CALENDAR_UNDELETED_EVENT_EVENT } from '@addons/calendar/constants'; /** * Component that displays upcoming events. @@ -36,7 +36,7 @@ import { Translate } from '@singletons'; @Component({ selector: 'addon-calendar-upcoming-events', templateUrl: 'addon-calendar-upcoming-events.html', - styleUrls: ['../../calendar-common.scss'], + styleUrl: '../../calendar-common.scss', }) export class AddonCalendarUpcomingEventsComponent implements OnInit, DoCheck, OnDestroy { @@ -70,7 +70,7 @@ export class AddonCalendarUpcomingEventsComponent implements OnInit, DoCheck, On // Listen for events "undeleted" (offline). this.undeleteEventObserver = CoreEvents.on( - AddonCalendarProvider.UNDELETED_EVENT_EVENT, + ADDON_CALENDAR_UNDELETED_EVENT_EVENT, (data) => { if (!data || !data.eventId) { return; diff --git a/src/addons/calendar/constants.ts b/src/addons/calendar/constants.ts index d4632c2b8a0..fb3550a728b 100644 --- a/src/addons/calendar/constants.ts +++ b/src/addons/calendar/constants.ts @@ -13,3 +13,46 @@ // limitations under the License. export const ADDON_CALENDAR_COMPONENT = 'AddonCalendarEvents'; + +export const ADDON_CALENDAR_PAGE_NAME = 'calendar'; + +export const ADDON_CALENDAR_EVENTS_TABLE = 'addon_calendar_events_3'; + +export const ADDON_CALENDAR_AUTO_SYNCED = 'addon_calendar_autom_synced'; +export const ADDON_CALENDAR_MANUAL_SYNCED = 'addon_calendar_manual_synced'; +export const ADDON_CALENDAR_SYNC_ID = 'calendar'; + +export const ADDON_CALENDAR_DAYS_INTERVAL = 30; + +export const ADDON_CALENDAR_STARTING_WEEK_DAY = 'addon_calendar_starting_week_day'; +export const ADDON_CALENDAR_TF_24 = '%H:%M'; // Calendar time in 24 hours format. +export const ADDON_CALENDAR_TF_12 = '%I:%M %p'; // Calendar time in 12 hours format. + +export const ADDON_CALENDAR_NEW_EVENT_EVENT = 'addon_calendar_new_event'; +export const ADDON_CALENDAR_NEW_EVENT_DISCARDED_EVENT = 'addon_calendar_new_event_discarded'; +export const ADDON_CALENDAR_EDIT_EVENT_EVENT = 'addon_calendar_edit_event'; +export const ADDON_CALENDAR_DELETED_EVENT_EVENT = 'addon_calendar_deleted_event'; +export const ADDON_CALENDAR_UNDELETED_EVENT_EVENT = 'addon_calendar_undeleted_event'; +export const ADDON_CALENDAR_FILTER_CHANGED_EVENT = 'addon_calendar_filter_changed_event'; + +/** + * Context levels enumeration. + */ +export enum AddonCalendarEventIcons { + SITE = 'fas-globe', + CATEGORY = 'fas-cubes', + COURSE = 'fas-graduation-cap', + GROUP = 'fas-users', + USER = 'fas-user', +} + +/** + * Main calendar Event types enumeration. + */ +export enum AddonCalendarEventType { + SITE = 'site', + CATEGORY = 'category', + COURSE = 'course', + GROUP = 'group', + USER = 'user', +} diff --git a/src/addons/calendar/pages/day/day.ts b/src/addons/calendar/pages/day/day.ts index 16faee9185f..0c0acf2f722 100644 --- a/src/addons/calendar/pages/day/day.ts +++ b/src/addons/calendar/pages/day/day.ts @@ -19,15 +19,13 @@ import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreTimeUtils } from '@services/utils/time'; import { - AddonCalendarProvider, AddonCalendar, AddonCalendarEventToDisplay, AddonCalendarCalendarDay, - AddonCalendarEventType, } from '../../services/calendar'; import { AddonCalendarOffline } from '../../services/calendar-offline'; import { AddonCalendarFilter, AddonCalendarHelper } from '../../services/calendar-helper'; -import { AddonCalendarSync, AddonCalendarSyncProvider } from '../../services/calendar-sync'; +import { AddonCalendarSync } from '../../services/calendar-sync'; import { CoreCategoryData, CoreCourses, CoreEnrolledCourseData } from '@features/courses/services/courses'; import { CoreCoursesHelper } from '@features/courses/services/courses-helper'; import moment from 'moment-timezone'; @@ -35,7 +33,7 @@ import { NgZone, Translate } from '@singletons'; import { CoreNavigator } from '@services/navigator'; import { Params } from '@angular/router'; import { Subscription } from 'rxjs'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreArray } from '@singletons/array'; import { CoreConstants } from '@/core/constants'; import { CoreSwipeSlidesDynamicItemsManager } from '@classes/items-management/swipe-slides-dynamic-items-manager'; import { CoreSwipeSlidesComponent } from '@components/swipe-slides/swipe-slides'; @@ -49,6 +47,18 @@ import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics'; import { CoreUrl } from '@singletons/url'; import { CoreTime } from '@singletons/time'; import { CoreModals } from '@services/modals'; +import { + ADDON_CALENDAR_AUTO_SYNCED, + ADDON_CALENDAR_DELETED_EVENT_EVENT, + ADDON_CALENDAR_EDIT_EVENT_EVENT, + ADDON_CALENDAR_FILTER_CHANGED_EVENT, + ADDON_CALENDAR_MANUAL_SYNCED, + ADDON_CALENDAR_NEW_EVENT_DISCARDED_EVENT, + ADDON_CALENDAR_NEW_EVENT_EVENT, + ADDON_CALENDAR_UNDELETED_EVENT_EVENT, + AddonCalendarEventType, +} from '@addons/calendar/constants'; +import { CoreObject } from '@singletons/object'; /** * Page that displays the calendar events for a certain day. @@ -91,7 +101,7 @@ export class AddonCalendarDayPage implements OnInit, OnDestroy { // Listen for events added. When an event is added, reload the data. this.eventObservers.push(CoreEvents.on( - AddonCalendarProvider.NEW_EVENT_EVENT, + ADDON_CALENDAR_NEW_EVENT_EVENT, (data) => { if (data && data.eventId) { this.manager?.getSource().markAllItemsUnloaded(); @@ -102,14 +112,14 @@ export class AddonCalendarDayPage implements OnInit, OnDestroy { )); // Listen for new event discarded event. When it does, reload the data. - this.eventObservers.push(CoreEvents.on(AddonCalendarProvider.NEW_EVENT_DISCARDED_EVENT, () => { + this.eventObservers.push(CoreEvents.on(ADDON_CALENDAR_NEW_EVENT_DISCARDED_EVENT, () => { this.manager?.getSource().markAllItemsUnloaded(); this.refreshData(true, true); }, this.currentSiteId)); // Listen for events edited. When an event is edited, reload the data. this.eventObservers.push(CoreEvents.on( - AddonCalendarProvider.EDIT_EVENT_EVENT, + ADDON_CALENDAR_EDIT_EVENT_EVENT, (data) => { if (data && data.eventId) { this.manager?.getSource().markAllItemsUnloaded(); @@ -120,13 +130,13 @@ export class AddonCalendarDayPage implements OnInit, OnDestroy { )); // Refresh data if calendar events are synchronized automatically. - this.eventObservers.push(CoreEvents.on(AddonCalendarSyncProvider.AUTO_SYNCED, () => { + this.eventObservers.push(CoreEvents.on(ADDON_CALENDAR_AUTO_SYNCED, () => { this.manager?.getSource().markAllItemsUnloaded(); this.refreshData(false, true); }, this.currentSiteId)); // Refresh data if calendar events are synchronized manually but not by this page. - this.eventObservers.push(CoreEvents.on(AddonCalendarSyncProvider.MANUAL_SYNCED, (data) => { + this.eventObservers.push(CoreEvents.on(ADDON_CALENDAR_MANUAL_SYNCED, (data) => { const selectedDay = this.manager?.getSelectedItem(); if (data && (data.source != 'day' || !selectedDay || !data.moment || !selectedDay.moment.isSame(data.moment, 'day'))) { this.manager?.getSource().markAllItemsUnloaded(); @@ -136,7 +146,7 @@ export class AddonCalendarDayPage implements OnInit, OnDestroy { // Update the events when an event is deleted. this.eventObservers.push(CoreEvents.on( - AddonCalendarProvider.DELETED_EVENT_EVENT, + ADDON_CALENDAR_DELETED_EVENT_EVENT, (data) => { if (data && !data.sent) { // Event was deleted in offline. Just mark it as deleted, no need to refresh. @@ -151,7 +161,7 @@ export class AddonCalendarDayPage implements OnInit, OnDestroy { // Listen for events "undeleted" (offline). this.eventObservers.push(CoreEvents.on( - AddonCalendarProvider.UNDELETED_EVENT_EVENT, + ADDON_CALENDAR_UNDELETED_EVENT_EVENT, (data) => { if (!data || !data.eventId) { return; @@ -164,7 +174,7 @@ export class AddonCalendarDayPage implements OnInit, OnDestroy { )); this.eventObservers.push(CoreEvents.on( - AddonCalendarProvider.FILTER_CHANGED_EVENT, + ADDON_CALENDAR_FILTER_CHANGED_EVENT, async (data) => { this.filter = data; @@ -212,7 +222,7 @@ export class AddonCalendarDayPage implements OnInit, OnDestroy { ngOnInit(): void { const types: string[] = []; - CoreUtils.enumKeys(AddonCalendarEventType).forEach((name) => { + CoreObject.enumKeys(AddonCalendarEventType).forEach((name) => { const value = AddonCalendarEventType[name]; this.filter[name] = CoreNavigator.getRouteBooleanParam(name) ?? true; types.push(value); @@ -343,7 +353,7 @@ export class AddonCalendarDayPage implements OnInit, OnDestroy { result.moment = selectedDay?.moment; this.manager?.getSource().markAllItemsUnloaded(); - CoreEvents.trigger(AddonCalendarSyncProvider.MANUAL_SYNCED, result, this.currentSiteId); + CoreEvents.trigger(ADDON_CALENDAR_MANUAL_SYNCED, result, this.currentSiteId); } } catch (error) { if (showErrors) { @@ -597,7 +607,7 @@ class AddonCalendarDaySlidesItemsManagerSource extends CoreSwipeSlidesDynamicIte const categories = await CoreCourses.getCategories(0, true); // Index categories by ID. - this.categories = CoreUtils.arrayToObject(categories, 'id'); + this.categories = CoreArray.toObject(categories, 'id'); } catch { // Ignore errors. } diff --git a/src/addons/calendar/pages/edit-event/edit-event.ts b/src/addons/calendar/pages/edit-event/edit-event.ts index daa80528173..c3850c4a581 100644 --- a/src/addons/calendar/pages/edit-event/edit-event.ts +++ b/src/addons/calendar/pages/edit-event/edit-event.ts @@ -20,20 +20,18 @@ import { CoreSites } from '@services/sites'; import { CoreSync } from '@services/sync'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreTimeUtils } from '@services/utils/time'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { CoreCategoryData, CoreCourses, CoreCourseSearchedData, CoreEnrolledCourseData } from '@features/courses/services/courses'; import { CoreEditorRichTextEditorComponent } from '@features/editor/components/rich-text-editor/rich-text-editor'; import { - AddonCalendarProvider, AddonCalendarGetCalendarAccessInformationWSResponse, AddonCalendarEvent, - AddonCalendarEventType, AddonCalendar, AddonCalendarSubmitCreateUpdateFormDataWSParams, } from '../../services/calendar'; import { AddonCalendarOffline } from '../../services/calendar-offline'; import { AddonCalendarEventTypeOption, AddonCalendarHelper } from '../../services/calendar-helper'; -import { AddonCalendarSync, AddonCalendarSyncProvider } from '../../services/calendar-sync'; +import { AddonCalendarSync } from '../../services/calendar-sync'; import { CoreSite } from '@classes/sites/site'; import { Translate } from '@singletons'; import { CoreFilterHelper } from '@features/filter/services/filter-helper'; @@ -42,12 +40,20 @@ import { CoreError } from '@classes/errors/error'; import { CoreNavigator } from '@services/navigator'; import { CanLeave } from '@guards/can-leave'; import { CoreForms } from '@singletons/form'; -import { CoreReminders, CoreRemindersService, CoreRemindersUnits } from '@features/reminders/services/reminders'; +import { CoreReminders, CoreRemindersService } from '@features/reminders/services/reminders'; import moment from 'moment-timezone'; -import { ADDON_CALENDAR_COMPONENT } from '@addons/calendar/constants'; +import { + ADDON_CALENDAR_COMPONENT, + ADDON_CALENDAR_EDIT_EVENT_EVENT, + ADDON_CALENDAR_NEW_EVENT_EVENT, + ADDON_CALENDAR_SYNC_ID, + AddonCalendarEventType, +} from '@addons/calendar/constants'; import { ContextLevel } from '@/core/constants'; import { CorePopovers } from '@services/popovers'; import { CoreLoadings } from '@services/loadings'; +import { REMINDERS_DISABLED, CoreRemindersUnits } from '@features/reminders/constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Page that displays a form to create/edit an event. @@ -55,7 +61,7 @@ import { CoreLoadings } from '@services/loadings'; @Component({ selector: 'page-addon-calendar-edit-event', templateUrl: 'edit-event.html', - styleUrls: ['edit-event.scss'], + styleUrl: 'edit-event.scss', }) export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave { @@ -159,7 +165,7 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave { try { const [types, accessInfo] = await Promise.all([ AddonCalendar.getAllowedEventTypes(this.courseId), - CoreUtils.ignoreErrors(AddonCalendar.getAccessInformation(this.courseId), { + CorePromiseUtils.ignoreErrors(AddonCalendar.getAccessInformation(this.courseId), { canmanageentries: false, canmanageownentries: false, canmanagegroupentries: false, @@ -179,7 +185,7 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave { // Editing an event, get the event data. Wait for sync first. const eventId = this.eventId; - promises.push(AddonCalendarSync.waitForSync(AddonCalendarSyncProvider.SYNC_ID).then(async () => { + promises.push(AddonCalendarSync.waitForSync(ADDON_CALENDAR_SYNC_ID).then(async () => { // Do not block if the scope is already destroyed. if (!this.isDestroyed && this.eventId) { CoreSync.blockOperation(ADDON_CALENDAR_COMPONENT, eventId); @@ -363,7 +369,7 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave { this.form.controls.repeateditall.setValue(1); } - if (event.eventtype == AddonCalendarEventType.GROUP && courseId) { + if (event.eventtype === AddonCalendarEventType.GROUP && courseId) { await this.loadGroups(courseId); } } @@ -489,12 +495,12 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave { repeat: formData.repeat, }; - if (formData.eventtype == AddonCalendarEventType.COURSE) { + if (formData.eventtype === AddonCalendarEventType.COURSE) { data.courseid = formData.courseid; - } else if (formData.eventtype == AddonCalendarEventType.GROUP) { + } else if (formData.eventtype === AddonCalendarEventType.GROUP) { data.groupcourseid = formData.groupcourseid; data.groupid = formData.groupid; - } else if (formData.eventtype == AddonCalendarEventType.CATEGORY) { + } else if (formData.eventtype === AddonCalendarEventType.CATEGORY) { data.categoryid = formData.categoryid; } @@ -560,13 +566,13 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave { if (this.eventId && this.eventId > 0) { // Editing an event. CoreEvents.trigger( - AddonCalendarProvider.EDIT_EVENT_EVENT, + ADDON_CALENDAR_EDIT_EVENT_EVENT, { eventId: this.eventId }, this.currentSite.getId(), ); } else { CoreEvents.trigger( - AddonCalendarProvider.NEW_EVENT_EVENT, + ADDON_CALENDAR_NEW_EVENT_EVENT, { eventId: event.id, oldEventId: this.eventId, @@ -617,7 +623,7 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave { // Check if default reminders are enabled. const defaultTime = await CoreReminders.getDefaultNotificationTime(this.currentSite.getId()); - if (defaultTime === CoreRemindersService.DISABLED) { + if (defaultTime === REMINDERS_DISABLED) { return; } diff --git a/src/addons/calendar/pages/event/event.ts b/src/addons/calendar/pages/event/event.ts index d97f1d3dd0c..5226f55af1f 100644 --- a/src/addons/calendar/pages/event/event.ts +++ b/src/addons/calendar/pages/event/event.ts @@ -17,11 +17,10 @@ import { AlertOptions } from '@ionic/core'; import { AddonCalendar, AddonCalendarEventToDisplay, - AddonCalendarProvider, } from '../../services/calendar'; import { AddonCalendarEventReminder, AddonCalendarHelper } from '../../services/calendar-helper'; import { AddonCalendarOffline } from '../../services/calendar-offline'; -import { AddonCalendarSync, AddonCalendarSyncEvents, AddonCalendarSyncProvider } from '../../services/calendar-sync'; +import { AddonCalendarSync, AddonCalendarSyncEvents } from '../../services/calendar-sync'; import { CoreNetwork } from '@services/network'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreDomUtils } from '@services/utils/dom'; @@ -32,13 +31,13 @@ import { CoreTimeUtils } from '@services/utils/time'; import { NgZone, Translate } from '@singletons'; import { Subscription } from 'rxjs'; import { CoreNavigator } from '@services/navigator'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { ActivatedRoute, ActivatedRouteSnapshot } from '@angular/router'; import { CoreConstants } from '@/core/constants'; import { CoreRoutedItemsManagerSourcesTracker } from '@classes/items-management/routed-items-manager-sources-tracker'; import { AddonCalendarEventsSource } from '@addons/calendar/classes/events-source'; import { CoreSwipeNavigationItemsManager } from '@classes/items-management/swipe-navigation-items-manager'; -import { CoreReminders, CoreRemindersService } from '@features/reminders/services/reminders'; +import { CoreReminders } from '@features/reminders/services/reminders'; import { CoreLocalNotifications } from '@services/local-notifications'; import { CorePlatform } from '@services/platform'; import { CoreConfig } from '@services/config'; @@ -46,6 +45,16 @@ import { CoreToasts, ToastDuration } from '@services/toasts'; import { CorePopovers } from '@services/popovers'; import { CoreLoadings } from '@services/loadings'; import { CoreUrl } from '@singletons/url'; +import { + ADDON_CALENDAR_AUTO_SYNCED, + ADDON_CALENDAR_DELETED_EVENT_EVENT, + ADDON_CALENDAR_EDIT_EVENT_EVENT, + ADDON_CALENDAR_MANUAL_SYNCED, + ADDON_CALENDAR_NEW_EVENT_DISCARDED_EVENT, + ADDON_CALENDAR_NEW_EVENT_EVENT, + ADDON_CALENDAR_UNDELETED_EVENT_EVENT, +} from '@addons/calendar/constants'; +import { REMINDERS_DEFAULT_NOTIFICATION_TIME_CHANGED } from '@features/reminders/constants'; /** * Page that displays a single calendar event. @@ -99,7 +108,7 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy { this.canEdit = AddonCalendar.canEditEventsInSite(); // Listen for event edited. If current event is edited, reload the data. - this.editEventObserver = CoreEvents.on(AddonCalendarProvider.EDIT_EVENT_EVENT, (data) => { + this.editEventObserver = CoreEvents.on(ADDON_CALENDAR_EDIT_EVENT_EVENT, (data) => { if (data && data.eventId === this.eventId) { this.eventLoaded = false; this.refreshEvent(true, false); @@ -107,7 +116,7 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy { }, this.currentSiteId); // Listen for event created. If user edits the data of a new offline event or it's sent to server, this event is triggered. - this.newEventObserver = CoreEvents.on(AddonCalendarProvider.NEW_EVENT_EVENT, (data) => { + this.newEventObserver = CoreEvents.on(ADDON_CALENDAR_NEW_EVENT_EVENT, (data) => { if (this.eventId < 0 && data && (data.eventId === this.eventId || data.oldEventId === this.eventId)) { this.eventId = data.eventId; this.eventLoaded = false; @@ -117,14 +126,14 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy { // Refresh data if this calendar event is synchronized automatically. this.syncObserver = CoreEvents.on( - AddonCalendarSyncProvider.AUTO_SYNCED, + ADDON_CALENDAR_AUTO_SYNCED, (data) => this.checkSyncResult(false, data), this.currentSiteId, ); // Refresh data if calendar events are synchronized manually but not by this page. this.manualSyncObserver = CoreEvents.on( - AddonCalendarSyncProvider.MANUAL_SYNCED, + ADDON_CALENDAR_MANUAL_SYNCED, (data) => this.checkSyncResult(true, data), this.currentSiteId, ); @@ -138,7 +147,7 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy { }); // Reload reminders if default notification time changes. - this.defaultTimeChangedObserver = CoreEvents.on(CoreRemindersService.DEFAULT_NOTIFICATION_TIME_CHANGED, () => { + this.defaultTimeChangedObserver = CoreEvents.on(REMINDERS_DEFAULT_NOTIFICATION_TIME_CHANGED, () => { this.loadReminders(); }, this.currentSiteId); @@ -364,7 +373,7 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy { result.source = 'event'; CoreEvents.trigger( - AddonCalendarSyncProvider.MANUAL_SYNCED, + ADDON_CALENDAR_MANUAL_SYNCED, result, this.currentSiteId, ); @@ -471,7 +480,7 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy { } promises.push(AddonCalendar.invalidateTimeFormat()); - await CoreUtils.allPromisesIgnoringErrors(promises); + await CorePromiseUtils.allPromisesIgnoringErrors(promises); await this.fetchEvent(sync, showErrors); } @@ -552,9 +561,9 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy { // Trigger an event. if (this.event.id < 0) { - CoreEvents.trigger(AddonCalendarProvider.NEW_EVENT_DISCARDED_EVENT, {}, CoreSites.getCurrentSiteId()); + CoreEvents.trigger(ADDON_CALENDAR_NEW_EVENT_DISCARDED_EVENT, {}, CoreSites.getCurrentSiteId()); } else { - CoreEvents.trigger(AddonCalendarProvider.DELETED_EVENT_EVENT, { + CoreEvents.trigger(ADDON_CALENDAR_DELETED_EVENT_EVENT, { eventId: this.eventId, sent: onlineEventDeleted, }, CoreSites.getCurrentSiteId()); @@ -595,7 +604,7 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy { await AddonCalendarOffline.unmarkDeleted(this.event.id); // Trigger an event. - CoreEvents.trigger(AddonCalendarProvider.UNDELETED_EVENT_EVENT, { + CoreEvents.trigger(ADDON_CALENDAR_UNDELETED_EVENT_EVENT, { eventId: this.eventId, }, CoreSites.getCurrentSiteId()); diff --git a/src/addons/calendar/pages/index/index.ts b/src/addons/calendar/pages/index/index.ts index 7bfa6e2d6f3..0ab5b077a98 100644 --- a/src/addons/calendar/pages/index/index.ts +++ b/src/addons/calendar/pages/index/index.ts @@ -18,9 +18,9 @@ import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreCoursesHelper } from '@features/courses/services/courses-helper'; -import { AddonCalendar, AddonCalendarProvider } from '../../services/calendar'; +import { AddonCalendar } from '../../services/calendar'; import { AddonCalendarOffline } from '../../services/calendar-offline'; -import { AddonCalendarSync, AddonCalendarSyncProvider } from '../../services/calendar-sync'; +import { AddonCalendarSync } from '../../services/calendar-sync'; import { AddonCalendarFilter, AddonCalendarHelper } from '../../services/calendar-helper'; import { NgZone } from '@singletons'; import { Subscription } from 'rxjs'; @@ -31,6 +31,16 @@ import { AddonCalendarUpcomingEventsComponent } from '../../components/upcoming- import { CoreNavigator } from '@services/navigator'; import { CoreConstants } from '@/core/constants'; import { CoreModals } from '@services/modals'; +import { + ADDON_CALENDAR_AUTO_SYNCED, + ADDON_CALENDAR_DELETED_EVENT_EVENT, + ADDON_CALENDAR_EDIT_EVENT_EVENT, + ADDON_CALENDAR_FILTER_CHANGED_EVENT, + ADDON_CALENDAR_MANUAL_SYNCED, + ADDON_CALENDAR_NEW_EVENT_DISCARDED_EVENT, + ADDON_CALENDAR_NEW_EVENT_EVENT, + ADDON_CALENDAR_UNDELETED_EVENT_EVENT, +} from '@addons/calendar/constants'; /** * Page that displays the calendar events. @@ -85,7 +95,7 @@ export class AddonCalendarIndexPage implements OnInit, OnDestroy { // Listen for events added. When an event is added, reload the data. this.newEventObserver = CoreEvents.on( - AddonCalendarProvider.NEW_EVENT_EVENT, + ADDON_CALENDAR_NEW_EVENT_EVENT, (data) => { if (data && data.eventId) { this.loaded = false; @@ -96,14 +106,14 @@ export class AddonCalendarIndexPage implements OnInit, OnDestroy { ); // Listen for new event discarded event. When it does, reload the data. - this.discardedObserver = CoreEvents.on(AddonCalendarProvider.NEW_EVENT_DISCARDED_EVENT, () => { + this.discardedObserver = CoreEvents.on(ADDON_CALENDAR_NEW_EVENT_DISCARDED_EVENT, () => { this.loaded = false; this.refreshData(true, false, true); }, this.currentSiteId); // Listen for events edited. When an event is edited, reload the data. this.editEventObserver = CoreEvents.on( - AddonCalendarProvider.EDIT_EVENT_EVENT, + ADDON_CALENDAR_EDIT_EVENT_EVENT, (data) => { if (data && data.eventId) { this.loaded = false; @@ -114,13 +124,13 @@ export class AddonCalendarIndexPage implements OnInit, OnDestroy { ); // Refresh data if calendar events are synchronized automatically. - this.syncObserver = CoreEvents.on(AddonCalendarSyncProvider.AUTO_SYNCED, () => { + this.syncObserver = CoreEvents.on(ADDON_CALENDAR_AUTO_SYNCED, () => { this.loaded = false; this.refreshData(false, false, true); }, this.currentSiteId); // Refresh data if calendar events are synchronized manually but not by this page. - this.manualSyncObserver = CoreEvents.on(AddonCalendarSyncProvider.MANUAL_SYNCED, (data) => { + this.manualSyncObserver = CoreEvents.on(ADDON_CALENDAR_MANUAL_SYNCED, (data) => { if (data && data.source != 'index') { this.loaded = false; this.refreshData(false, false, true); @@ -128,18 +138,18 @@ export class AddonCalendarIndexPage implements OnInit, OnDestroy { }, this.currentSiteId); // Update the events when an event is deleted. - this.deleteEventObserver = CoreEvents.on(AddonCalendarProvider.DELETED_EVENT_EVENT, () => { + this.deleteEventObserver = CoreEvents.on(ADDON_CALENDAR_DELETED_EVENT_EVENT, () => { this.loaded = false; this.refreshData(false, false, true); }, this.currentSiteId); // Update the "hasOffline" property if an event deleted in offline is restored. - this.undeleteEventObserver = CoreEvents.on(AddonCalendarProvider.UNDELETED_EVENT_EVENT, async () => { + this.undeleteEventObserver = CoreEvents.on(ADDON_CALENDAR_UNDELETED_EVENT_EVENT, async () => { this.hasOffline = await AddonCalendarOffline.hasOfflineData(); }, this.currentSiteId); this.filterChangedObserver = CoreEvents.on( - AddonCalendarProvider.FILTER_CHANGED_EVENT, + ADDON_CALENDAR_FILTER_CHANGED_EVENT, async (filterData) => { this.filter = filterData; @@ -205,7 +215,7 @@ export class AddonCalendarIndexPage implements OnInit, OnDestroy { result.source = 'index'; CoreEvents.trigger( - AddonCalendarSyncProvider.MANUAL_SYNCED, + ADDON_CALENDAR_MANUAL_SYNCED, result, this.currentSiteId, ); diff --git a/src/addons/calendar/pages/settings/settings.ts b/src/addons/calendar/pages/settings/settings.ts index 1e7cb5e82c6..87237d62e74 100644 --- a/src/addons/calendar/pages/settings/settings.ts +++ b/src/addons/calendar/pages/settings/settings.ts @@ -18,6 +18,7 @@ import { CoreReminders, CoreRemindersService, } from '@features/reminders/services/reminders'; +import { REMINDERS_DISABLED } from '@features/reminders/constants'; /** * Page that displays the calendar settings. @@ -67,7 +68,7 @@ export class AddonCalendarSettingsPage implements OnInit { return; } - await CoreReminders.setDefaultNotificationTime(reminderTime.timeBefore ?? CoreRemindersService.DISABLED); + await CoreReminders.setDefaultNotificationTime(reminderTime.timeBefore ?? REMINDERS_DISABLED); this.updateDefaultTimeLabel(); } diff --git a/src/addons/calendar/services/calendar-helper.ts b/src/addons/calendar/services/calendar-helper.ts index 7f32071b2e1..decb8075ef9 100644 --- a/src/addons/calendar/services/calendar-helper.ts +++ b/src/addons/calendar/services/calendar-helper.ts @@ -20,36 +20,30 @@ import { AddonCalendarEvent, AddonCalendarEventBase, AddonCalendarEventToDisplay, - AddonCalendarEventType, AddonCalendarGetEventsEvent, - AddonCalendarProvider, AddonCalendarWeek, AddonCalendarWeekDay, } from './calendar'; import { CoreConfig } from '@services/config'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreObject } from '@singletons/object'; import { CoreCourse } from '@features/course/services/course'; import { ContextLevel, CoreConstants } from '@/core/constants'; import moment from 'moment-timezone'; import { makeSingleton } from '@singletons'; -import { AddonCalendarSyncInvalidateEvent } from './calendar-sync'; import { AddonCalendarOfflineEventDBRecord } from './database/calendar-offline'; import { CoreCategoryData } from '@features/courses/services/courses'; import { CoreTimeUtils } from '@services/utils/time'; import { CoreReminders, CoreRemindersService } from '@features/reminders/services/reminders'; import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate'; -import { ADDON_CALENDAR_COMPONENT } from '../constants'; - -/** - * Context levels enumeration. - */ -export enum AddonCalendarEventIcons { - SITE = 'fas-globe', - CATEGORY = 'fas-cubes', - COURSE = 'fas-graduation-cap', - GROUP = 'fas-users', - USER = 'fas-user', -} +import { + ADDON_CALENDAR_COMPONENT, + ADDON_CALENDAR_STARTING_WEEK_DAY, + AddonCalendarEventIcons, + AddonCalendarEventType, +} from '../constants'; +import { AddonCalendarSyncInvalidateEvent } from './calendar-sync'; +import { REMINDERS_DISABLED, REMINDERS_DEFAULT_REMINDER_TIMEBEFORE } from '@features/reminders/constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Service that provides some features regarding lists of courses and categories. @@ -67,7 +61,7 @@ export class AddonCalendarHelperProvider { */ getEventIcon(eventType: AddonCalendarEventType | string): string { if (this.eventTypeIcons.length == 0) { - CoreUtils.enumKeys(AddonCalendarEventType).forEach((name) => { + CoreObject.enumKeys(AddonCalendarEventType).forEach((name) => { const value = AddonCalendarEventType[name]; this.eventTypeIcons[value] = AddonCalendarEventIcons[name]; }); @@ -322,7 +316,7 @@ export class AddonCalendarHelperProvider { const defaultTime = await CoreReminders.getDefaultNotificationTime(siteId); let defaultLabel: string | undefined; - if (defaultTime > CoreRemindersService.DISABLED) { + if (defaultTime > REMINDERS_DISABLED) { const data = CoreRemindersService.convertSecondsToValueAndUnit(defaultTime); defaultLabel = CoreReminders.getUnitValueLabel(data.value, data.unit, true); } @@ -332,7 +326,7 @@ export class AddonCalendarHelperProvider { id: reminder.id, }; - if (reminder.timebefore === CoreRemindersService.DEFAULT_REMINDER_TIMEBEFORE) { + if (reminder.timebefore === REMINDERS_DEFAULT_REMINDER_TIMEBEFORE) { // Default time. Check if default notifications are disabled. if (defaultLabel !== undefined) { formatted.label = defaultLabel; @@ -418,7 +412,7 @@ export class AddonCalendarHelperProvider { const site = await CoreSites.getSite(siteId); // Get starting week day user preference, fallback to site configuration. let startWeekDayStr = site.getStoredConfig('calendar_startwday') || '1'; - startWeekDayStr = await CoreConfig.get(AddonCalendarProvider.STARTING_WEEK_DAY, startWeekDayStr); + startWeekDayStr = await CoreConfig.get(ADDON_CALENDAR_STARTING_WEEK_DAY, startWeekDayStr); const startWeekDay = parseInt(startWeekDayStr, 10); const today = moment(); @@ -645,7 +639,7 @@ export class AddonCalendarHelperProvider { const repeatedEvents = await AddonCalendar.getLocalEventsByRepeatIdFromLocalDb(eventData.repeatid, site.id); - await CoreUtils.allPromises(repeatedEvents.map((event) => + await CorePromiseUtils.allPromises(repeatedEvents.map((event) => AddonCalendar.invalidateEvent(event.id))); return; @@ -666,7 +660,7 @@ export class AddonCalendarHelperProvider { })); try { - await CoreUtils.allPromisesIgnoringErrors(promises); + await CorePromiseUtils.allPromisesIgnoringErrors(promises); } finally { const treatedMonths = {}; const treatedDays = {}; @@ -734,7 +728,7 @@ export class AddonCalendarHelperProvider { } }); - await CoreUtils.allPromisesIgnoringErrors(finalPromises); + await CorePromiseUtils.allPromisesIgnoringErrors(finalPromises); } } @@ -784,7 +778,6 @@ export class AddonCalendarHelperProvider { } } - export const AddonCalendarHelper = makeSingleton(AddonCalendarHelperProvider); /** diff --git a/src/addons/calendar/services/calendar-offline.ts b/src/addons/calendar/services/calendar-offline.ts index 4136fa66fda..0af703c16be 100644 --- a/src/addons/calendar/services/calendar-offline.ts +++ b/src/addons/calendar/services/calendar-offline.ts @@ -15,7 +15,7 @@ import { Injectable } from '@angular/core'; import { SQLiteDBRecordValues } from '@classes/sqlitedb'; import { CoreSites } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreArray } from '@singletons/array'; import { makeSingleton } from '@singletons'; import { AddonCalendarSubmitCreateUpdateFormDataWSParams } from './calendar'; import { @@ -62,7 +62,7 @@ export class AddonCalendarOfflineProvider { const result = await Promise.all(promises); - return CoreUtils.mergeArraysWithoutDuplicates(result[0], result[1]); + return CoreArray.mergeWithoutDuplicates(result[0], result[1]); } /** diff --git a/src/addons/calendar/services/calendar-sync.ts b/src/addons/calendar/services/calendar-sync.ts index bad6607d516..48169e73126 100644 --- a/src/addons/calendar/services/calendar-sync.ts +++ b/src/addons/calendar/services/calendar-sync.ts @@ -17,7 +17,7 @@ import { CoreSyncBaseProvider, CoreSyncBlockedError } from '@classes/base-sync'; import { CoreNetwork } from '@services/network'; import { CoreEvents } from '@singletons/events'; import { CoreSites } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { AddonCalendar, AddonCalendarEvent, @@ -29,7 +29,14 @@ import { makeSingleton, Translate } from '@singletons'; import { CoreSync, CoreSyncResult } from '@services/sync'; import { CoreNetworkError } from '@classes/errors/network-error'; import moment from 'moment-timezone'; -import { ADDON_CALENDAR_COMPONENT } from '../constants'; +import { + ADDON_CALENDAR_AUTO_SYNCED, + ADDON_CALENDAR_COMPONENT, + ADDON_CALENDAR_MANUAL_SYNCED, + ADDON_CALENDAR_SYNC_ID, +} from '../constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; +import { CoreWSError } from '@classes/errors/wserror'; /** * Service to sync calendar. @@ -37,10 +44,6 @@ import { ADDON_CALENDAR_COMPONENT } from '../constants'; @Injectable({ providedIn: 'root' }) export class AddonCalendarSyncProvider extends CoreSyncBaseProvider { - static readonly AUTO_SYNCED = 'addon_calendar_autom_synced'; - static readonly MANUAL_SYNCED = 'addon_calendar_manual_synced'; - static readonly SYNC_ID = 'calendar'; - protected componentTranslatableString = 'addon.calendar.calendarevent'; constructor() { @@ -72,7 +75,7 @@ export class AddonCalendarSyncProvider extends CoreSyncBaseProvider { siteId = siteId || CoreSites.getCurrentSiteId(); - const needed = await this.isSyncNeeded(AddonCalendarSyncProvider.SYNC_ID, siteId); + const needed = await this.isSyncNeeded(ADDON_CALENDAR_SYNC_ID, siteId); if (needed) { return this.syncEvents(siteId); @@ -101,7 +104,7 @@ export class AddonCalendarSyncProvider extends CoreSyncBaseProvider { siteId = siteId || CoreSites.getCurrentSiteId(); - const currentSyncPromise = this.getOngoingSync(AddonCalendarSyncProvider.SYNC_ID, siteId); + const currentSyncPromise = this.getOngoingSync(ADDON_CALENDAR_SYNC_ID, siteId); if (currentSyncPromise) { // There's already a sync ongoing for this site, return the promise. return currentSyncPromise; @@ -112,7 +115,7 @@ export class AddonCalendarSyncProvider extends CoreSyncBaseProvider 0) { if (!CoreNetwork.isOnline()) { @@ -141,7 +144,7 @@ export class AddonCalendarSyncProvider extends CoreSyncBaseProvider this.syncOfflineEvent(eventId, result, siteId)); - await CoreUtils.allPromises(promises); + await CorePromiseUtils.allPromises(promises); if (result.updated) { @@ -151,12 +154,12 @@ export class AddonCalendarSyncProvider extends CoreSyncBaseProvider[] = []; promises.push(site.getDb().deleteRecords( - EVENTS_TABLE, + ADDON_CALENDAR_EVENTS_TABLE, { id: eventId }, )); promises.push(CoreReminders.removeReminders({ @@ -256,7 +243,7 @@ export class AddonCalendarProvider { component: ADDON_CALENDAR_COMPONENT, } , siteId)); - await CoreUtils.ignoreErrors(Promise.all(promises)); + await CorePromiseUtils.ignoreErrors(Promise.all(promises)); } /** @@ -297,7 +284,7 @@ export class AddonCalendarProvider { } CoreNavigator.navigateToSitePath( - AddonCalendarMainMenuHandlerService.PAGE_NAME, + ADDON_CALENDAR_PAGE_NAME, { siteId: notification.siteId, preferCurrentTab: false, @@ -429,7 +416,7 @@ export class AddonCalendarProvider { * @returns Cache key. */ protected getAccessInformationCacheKey(courseId?: number): string { - return ROOT_CACHE_KEY + 'accessInformation:' + (courseId || 0); + return AddonCalendarProvider.ROOT_CACHE_KEY + 'accessInformation:' + (courseId || 0); } /** @@ -441,7 +428,7 @@ export class AddonCalendarProvider { async getAllEventsFromLocalDb(siteId?: string): Promise { const site = await CoreSites.getSite(siteId); - return site.getDb().getAllRecords(EVENTS_TABLE); + return site.getDb().getAllRecords(ADDON_CALENDAR_EVENTS_TABLE); } /** @@ -482,7 +469,7 @@ export class AddonCalendarProvider { * @returns Cache key. */ protected getAllowedEventTypesCacheKey(courseId?: number): string { - return ROOT_CACHE_KEY + 'allowedEventTypes:' + (courseId || 0); + return AddonCalendarProvider.ROOT_CACHE_KEY + 'allowedEventTypes:' + (courseId || 0); } /** @@ -527,9 +514,9 @@ export class AddonCalendarProvider { format = site.getStoredConfig('calendar_site_timeformat'); } - if (format === AddonCalendarProvider.CALENDAR_TF_12) { + if (format === ADDON_CALENDAR_TF_12) { format = Translate.instant('core.strftimetime12'); - } else if (format === AddonCalendarProvider.CALENDAR_TF_24) { + } else if (format === ADDON_CALENDAR_TF_24) { format = Translate.instant('core.strftimetime24'); } @@ -577,7 +564,7 @@ export class AddonCalendarProvider { const site = await CoreSites.getSite(siteId); const preSets: CoreSiteWSPreSets = { cacheKey: this.getEventCacheKey(id), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, }; const params: AddonCalendarGetCalendarEventsWSParams = { options: { @@ -613,7 +600,7 @@ export class AddonCalendarProvider { const site = await CoreSites.getSite(siteId); const preSets: CoreSiteWSPreSets = { cacheKey: this.getEventCacheKey(id), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, }; const params: AddonCalendarGetCalendarEventByIdWSParams = { eventid: id, @@ -641,7 +628,7 @@ export class AddonCalendarProvider { * @returns Cache key. */ protected getEventCacheKey(id: number): string { - return ROOT_CACHE_KEY + 'events:' + id; + return AddonCalendarProvider.ROOT_CACHE_KEY + 'events:' + id; } /** @@ -654,7 +641,7 @@ export class AddonCalendarProvider { async getEventFromLocalDb(id: number, siteId?: string): Promise { const site = await CoreSites.getSite(siteId); const record: AddonCalendarGetEventsEvent | AddonCalendarEvent | AddonCalendarEventDBRecord = - await site.getDb().getRecord(EVENTS_TABLE, { id: id }); + await site.getDb().getRecord(ADDON_CALENDAR_EVENTS_TABLE, { id: id }); const eventConverted = record as AddonCalendarEvent; const originalEvent = record as AddonCalendarGetEventsEvent; @@ -662,8 +649,8 @@ export class AddonCalendarProvider { // Calculate data to match the new WS. eventConverted.descriptionformat = originalEvent.format; - eventConverted.iscourseevent = originalEvent.eventtype == AddonCalendarEventType.COURSE; - eventConverted.iscategoryevent = originalEvent.eventtype == AddonCalendarEventType.CATEGORY; + eventConverted.iscourseevent = originalEvent.eventtype === AddonCalendarEventType.COURSE; + eventConverted.iscategoryevent = originalEvent.eventtype === AddonCalendarEventType.CATEGORY; eventConverted.normalisedeventtype = this.getEventType(recordAsRecord); try { eventConverted.category = CoreText.parseJSON(recordAsRecord.category || ''); @@ -699,7 +686,7 @@ export class AddonCalendarProvider { siteId?: string, ): Promise { - timebefore = timebefore ?? CoreRemindersService.DEFAULT_REMINDER_TIMEBEFORE; + timebefore = timebefore ?? REMINDERS_DEFAULT_REMINDER_TIMEBEFORE; const previousReminders = await CoreReminders.getReminders({ instanceId: event.id, @@ -779,7 +766,7 @@ export class AddonCalendarProvider { } const preSets: CoreSiteWSPreSets = { cacheKey: this.getDayEventsCacheKey(year, month, day, courseId, categoryId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + updateFrequency: CoreCacheUpdateFrequency.SOMETIMES, }; if (ignoreCache) { preSets.getFromCache = false; @@ -797,7 +784,7 @@ export class AddonCalendarProvider { * @returns Prefix Cache key. */ protected getDayEventsPrefixCacheKey(): string { - return ROOT_CACHE_KEY + 'day:'; + return AddonCalendarProvider.ROOT_CACHE_KEY + 'day:'; } /** @@ -843,7 +830,7 @@ export class AddonCalendarProvider { async getEventsList( initialTime?: number, daysToStart: number = 0, - daysInterval: number = AddonCalendarProvider.DAYS_INTERVAL, + daysInterval: number = ADDON_CALENDAR_DAYS_INTERVAL, siteId?: string, ): Promise { @@ -891,7 +878,7 @@ export class AddonCalendarProvider { cacheKey: this.getEventsListCacheKey(daysToStart, daysInterval), getCacheUsingCacheKey: true, uniqueCacheKey: true, - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + updateFrequency: CoreCacheUpdateFrequency.SOMETIMES, }; const response = await site.read('core_calendar_get_calendar_events', params, preSets); @@ -907,7 +894,7 @@ export class AddonCalendarProvider { * @returns Prefix Cache key. */ protected getEventsListPrefixCacheKey(): string { - return ROOT_CACHE_KEY + 'events:'; + return AddonCalendarProvider.ROOT_CACHE_KEY + 'events:'; } /** @@ -931,7 +918,7 @@ export class AddonCalendarProvider { async getLocalEventsByRepeatIdFromLocalDb(repeatId: number, siteId?: string): Promise { const site = await CoreSites.getSite(siteId); - return site.getDb().getRecords(EVENTS_TABLE, { repeatid: repeatId }); + return site.getDb().getRecords(ADDON_CALENDAR_EVENTS_TABLE, { repeatid: repeatId }); } /** @@ -969,7 +956,7 @@ export class AddonCalendarProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getMonthlyEventsCacheKey(year, month, courseId, categoryId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + updateFrequency: CoreCacheUpdateFrequency.SOMETIMES, }; if (ignoreCache) { preSets.getFromCache = false; @@ -985,7 +972,7 @@ export class AddonCalendarProvider { // Store starting week day preference, we need it in offline to show months that are not in cache. if (CoreNetwork.isOnline()) { - CoreConfig.set(AddonCalendarProvider.STARTING_WEEK_DAY, response.daynames[0].dayno); + CoreConfig.set(ADDON_CALENDAR_STARTING_WEEK_DAY, response.daynames[0].dayno); } return response; @@ -997,7 +984,7 @@ export class AddonCalendarProvider { * @returns Prefix Cache key. */ protected getMonthlyEventsPrefixCacheKey(): string { - return ROOT_CACHE_KEY + 'monthly:'; + return AddonCalendarProvider.ROOT_CACHE_KEY + 'monthly:'; } /** @@ -1054,7 +1041,7 @@ export class AddonCalendarProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getUpcomingEventsCacheKey(courseId, categoryId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + updateFrequency: CoreCacheUpdateFrequency.SOMETIMES, }; if (ignoreCache) { @@ -1074,7 +1061,7 @@ export class AddonCalendarProvider { * @returns Prefix Cache key. */ protected getUpcomingEventsPrefixCacheKey(): string { - return ROOT_CACHE_KEY + 'upcoming:'; + return AddonCalendarProvider.ROOT_CACHE_KEY + 'upcoming:'; } /** @@ -1120,7 +1107,7 @@ export class AddonCalendarProvider { getWeekDays(startingDay?: number): AddonCalendarWeekDaysTranslationKeys[] { startingDay = startingDay || 0; - return this.weekDays.slice(startingDay).concat(this.weekDays.slice(0, startingDay)); + return AddonCalendarProvider.weekDays.slice(startingDay).concat(AddonCalendarProvider.weekDays.slice(0, startingDay)); } /** @@ -1440,7 +1427,7 @@ export class AddonCalendarProvider { await this.addDefaultEventReminder(eventRecord, site.getId()); } - await site.getDb().insertRecord(EVENTS_TABLE, eventRecord); + await site.getDb().insertRecord(ADDON_CALENDAR_EVENTS_TABLE, eventRecord); } /** @@ -1451,7 +1438,7 @@ export class AddonCalendarProvider { */ protected async addDefaultEventReminder(event: AddonCalendarEventDBRecord, siteId?: string): Promise { // Add default reminder if the event isn't stored already and doesn't have any reminder. - const eventExist = await CoreUtils.promiseWorks(this.getEventFromLocalDb(event.id, siteId)); + const eventExist = await CorePromiseUtils.promiseWorks(this.getEventFromLocalDb(event.id, siteId)); if (eventExist) { return; } @@ -1522,7 +1509,7 @@ export class AddonCalendarProvider { // Now save the reminders if any. if (options.reminders?.length) { - await CoreUtils.ignoreErrors( + await CorePromiseUtils.ignoreErrors( Promise.all(options.reminders.map((reminder) => this.addEventReminder(event, reminder.time, siteId))), ); @@ -1545,7 +1532,7 @@ export class AddonCalendarProvider { // Now save the reminders if any. if (options.reminders?.length) { - await CoreUtils.ignoreErrors( + await CorePromiseUtils.ignoreErrors( Promise.all(options.reminders.map((reminder) => this.addEventReminder(event, reminder.time, siteId))), ); @@ -1553,7 +1540,7 @@ export class AddonCalendarProvider { return ({ sent: true, event }); } catch (error) { - if (error && !CoreUtils.isWebServiceError(error)) { + if (error && !CoreWSError.isWebServiceError(error)) { // Couldn't connect to server, store in offline. return storeOffline(); } else { @@ -1591,7 +1578,7 @@ export class AddonCalendarProvider { } const params: AddonCalendarSubmitCreateUpdateFormWSParams = { - formdata: CoreUtils.objectToGetParams(formData), + formdata: CoreObject.toGetParams(formData), }; const result = await site.write('core_calendar_submit_create_update_form', params); @@ -1606,7 +1593,7 @@ export class AddonCalendarProvider { if (eventId < 0) { // Offline event has been sent. Change reminders instanceId if any. - await CoreUtils.ignoreErrors( + await CorePromiseUtils.ignoreErrors( CoreReminders.updateReminders( { instanceId: result.event.id }, { @@ -1620,7 +1607,7 @@ export class AddonCalendarProvider { if (formData.id === 0) { // Store the new event in local DB. - await CoreUtils.ignoreErrors(this.storeEventInLocalDb(result.event, { addDefaultReminder: false, siteId })); + await CorePromiseUtils.ignoreErrors(this.storeEventInLocalDb(result.event, { addDefaultReminder: false, siteId })); } return result.event; @@ -2081,9 +2068,9 @@ export type AddonCalendarEventToDisplay = Partial & /** * Event triggered when an event is modified with event types: - * NEW_EVENT_EVENT, EDIT_EVENT_EVENT, DELETED_EVENT_EVENT, UNDELETED_EVENT_EVENT. + * NEW_EVENT, EDIT_EVENT, DELETED_EVENT, UNDELETED_EVENT. */ -export type AddonCalendarUpdatedEventEvent = { +export type AddonCalendarUpdatedEvent = { eventId: number; oldEventId?: number; // Old event ID. Used when an offline event is sent. sent?: boolean; diff --git a/src/addons/calendar/services/database/calendar-offline.ts b/src/addons/calendar/services/database/calendar-offline.ts index 5e08f837879..a6a4b2315e9 100644 --- a/src/addons/calendar/services/database/calendar-offline.ts +++ b/src/addons/calendar/services/database/calendar-offline.ts @@ -13,7 +13,8 @@ // limitations under the License. import { CoreSiteSchema } from '@services/sites'; -import { AddonCalendarEventType } from '../calendar'; +import { AddonCalendarEventType } from '@addons/calendar/constants'; + /** * Database variables for AddonDatabaseOffline service. */ diff --git a/src/addons/calendar/services/database/calendar.ts b/src/addons/calendar/services/database/calendar.ts index 1b87cb28196..9eb62c60530 100644 --- a/src/addons/calendar/services/database/calendar.ts +++ b/src/addons/calendar/services/database/calendar.ts @@ -12,24 +12,24 @@ // See the License for the specific language governing permissions and // limitations under the License. +import { ADDON_CALENDAR_EVENTS_TABLE, AddonCalendarEventType } from '@addons/calendar/constants'; import { SQLiteDB } from '@classes/sqlitedb'; -import { CoreRemindersService, CoreReminders } from '@features/reminders/services/reminders'; +import { REMINDERS_DISABLED } from '@features/reminders/constants'; +import { CoreReminders } from '@features/reminders/services/reminders'; import { CoreConfig } from '@services/config'; import { CoreSiteSchema } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; -import { AddonCalendarEventType } from '../calendar'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Database variables for AddonCalendarProvider service. */ -export const EVENTS_TABLE = 'addon_calendar_events_3'; export const CALENDAR_SITE_SCHEMA: CoreSiteSchema = { name: 'AddonCalendarProvider', version: 5, - canBeCleared: [EVENTS_TABLE], + canBeCleared: [ADDON_CALENDAR_EVENTS_TABLE], tables: [ { - name: EVENTS_TABLE, + name: ADDON_CALENDAR_EVENTS_TABLE, columns: [ { name: 'id', @@ -199,10 +199,10 @@ const migrateDefaultTime = async (siteId: string, convertToSeconds = false): Pro const key = 'mmaCalendarDefaultNotifTime#' + siteId; try { let defaultTime = await CoreConfig.get(key); - await CoreUtils.ignoreErrors(CoreConfig.delete(key)); + await CorePromiseUtils.ignoreErrors(CoreConfig.delete(key)); if (defaultTime <= 0) { - defaultTime = CoreRemindersService.DISABLED; + defaultTime = REMINDERS_DISABLED; } else if (convertToSeconds) { // Convert from minutes to seconds. defaultTime = defaultTime * 60; diff --git a/src/addons/calendar/services/handlers/mainmenu.ts b/src/addons/calendar/services/handlers/mainmenu.ts index 2cf9259a9bb..1b9269f13d1 100644 --- a/src/addons/calendar/services/handlers/mainmenu.ts +++ b/src/addons/calendar/services/handlers/mainmenu.ts @@ -16,6 +16,7 @@ import { Injectable } from '@angular/core'; import { AddonCalendar } from '../calendar'; import { makeSingleton } from '@singletons'; import { CoreMainMenuHandler, CoreMainMenuHandlerData } from '@features/mainmenu/services/mainmenu-delegate'; +import { ADDON_CALENDAR_PAGE_NAME } from '@addons/calendar/constants'; /** * Handler to inject an option into main menu. @@ -23,8 +24,6 @@ import { CoreMainMenuHandler, CoreMainMenuHandlerData } from '@features/mainmenu @Injectable({ providedIn: 'root' }) export class AddonCalendarMainMenuHandlerService implements CoreMainMenuHandler { - static readonly PAGE_NAME = 'calendar'; - name = 'AddonCalendar'; priority = 550; @@ -46,7 +45,7 @@ export class AddonCalendarMainMenuHandlerService implements CoreMainMenuHandler return { icon: 'far-calendar', title: 'addon.calendar.calendar', - page: AddonCalendarMainMenuHandlerService.PAGE_NAME, + page: ADDON_CALENDAR_PAGE_NAME, class: 'addon-calendar-handler', }; } diff --git a/src/addons/competency/classes/competency-course-competencies-source.ts b/src/addons/competency/classes/competency-course-competencies-source.ts index 36351490061..58d8aa0812d 100644 --- a/src/addons/competency/classes/competency-course-competencies-source.ts +++ b/src/addons/competency/classes/competency-course-competencies-source.ts @@ -14,7 +14,7 @@ import { CoreRoutedItemsManagerSource } from '@classes/items-management/routed-items-manager-source'; import { CoreUserProfile } from '@features/user/services/user'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { AddonCompetency, AddonCompetencyDataForCourseCompetenciesPageCompetency, @@ -70,7 +70,7 @@ export class AddonCompetencyCourseCompetenciesSource * Invalidate course cache. */ async invalidateCache(): Promise { - await CoreUtils.ignoreErrors(AddonCompetency.invalidateCourseCompetencies(this.COURSE_ID, this.USER_ID)); + await CorePromiseUtils.ignoreErrors(AddonCompetency.invalidateCourseCompetencies(this.COURSE_ID, this.USER_ID)); } /** diff --git a/src/addons/competency/classes/competency-plan-competencies-source.ts b/src/addons/competency/classes/competency-plan-competencies-source.ts index a674f4c1e41..95396ba6996 100644 --- a/src/addons/competency/classes/competency-plan-competencies-source.ts +++ b/src/addons/competency/classes/competency-plan-competencies-source.ts @@ -14,7 +14,7 @@ import { CoreRoutedItemsManagerSource } from '@classes/items-management/routed-items-manager-source'; import { CoreUserProfile } from '@features/user/services/user'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { AddonCompetency, AddonCompetencyDataForPlanPageCompetency, @@ -60,7 +60,7 @@ export class AddonCompetencyPlanCompetenciesSource extends CoreRoutedItemsManage * Invalidate plan cache. */ async invalidateCache(): Promise { - await CoreUtils.ignoreErrors(AddonCompetency.invalidateLearningPlan(this.PLAN_ID)); + await CorePromiseUtils.ignoreErrors(AddonCompetency.invalidateLearningPlan(this.PLAN_ID)); } /** diff --git a/src/addons/competency/competency-course-contents-lazy.module.ts b/src/addons/competency/competency-course-contents-lazy.module.ts index f132f614be4..bee7bb5cf53 100644 --- a/src/addons/competency/competency-course-contents-lazy.module.ts +++ b/src/addons/competency/competency-course-contents-lazy.module.ts @@ -31,4 +31,4 @@ const routes: Routes = [ AddonCompetencyCourseCompetenciesPageModule, ], }) -export class AddonCompetencyCourseContentsLazyModule {} +export default class AddonCompetencyCourseContentsLazyModule {} diff --git a/src/addons/competency/competency-course-details-lazy.module.ts b/src/addons/competency/competency-course-details-lazy.module.ts index 6d0530ae497..074d4dbb0fd 100644 --- a/src/addons/competency/competency-course-details-lazy.module.ts +++ b/src/addons/competency/competency-course-details-lazy.module.ts @@ -69,4 +69,4 @@ const routes: Routes = [ AddonCompetencyCompetencySummaryPageModule, ], }) -export class AddonCompetencyCourseDetailsLazyModule {} +export default class AddonCompetencyCourseDetailsLazyModule {} diff --git a/src/addons/competency/competency-learning-plans-lazy.module.ts b/src/addons/competency/competency-learning-plans-lazy.module.ts index bf093ed31b4..dbdcb244c9d 100644 --- a/src/addons/competency/competency-learning-plans-lazy.module.ts +++ b/src/addons/competency/competency-learning-plans-lazy.module.ts @@ -89,4 +89,4 @@ const routes: Routes = [ AddonCompetencyPlanListPage, ], }) -export class AddonCompetencyLearningPlansLazyModule {} +export default class AddonCompetencyLearningPlansLazyModule {} diff --git a/src/addons/competency/competency.module.ts b/src/addons/competency/competency.module.ts index aa41855796b..f6f970a3abf 100644 --- a/src/addons/competency/competency.module.ts +++ b/src/addons/competency/competency.module.ts @@ -28,7 +28,7 @@ import { Routes } from '@angular/router'; import { CoreMainMenuTabRoutingModule } from '@features/mainmenu/mainmenu-tab-routing.module'; import { CoreCourseIndexRoutingModule } from '@features/course/course-routing.module'; import { PARTICIPANTS_PAGE_NAME } from '@features/user/constants'; -import { COURSE_PAGE_NAME } from '@features/course/constants'; +import { CORE_COURSE_PAGE_NAME } from '@features/course/constants'; import { ADDON_COMPETENCY_LEARNING_PLANS_PAGE, ADDON_COMPETENCY_COMPETENCIES_PAGE } from './constants'; /** @@ -49,22 +49,22 @@ export async function getCompetencyServices(): Promise[]> { const mainMenuChildrenRoutes: Routes = [ { path: ADDON_COMPETENCY_LEARNING_PLANS_PAGE, - loadChildren: () => import('./competency-learning-plans-lazy.module').then(m => m.AddonCompetencyLearningPlansLazyModule), + loadChildren: () => import('./competency-learning-plans-lazy.module'), }, { - path: `${COURSE_PAGE_NAME}/:courseId/${ADDON_COMPETENCY_COMPETENCIES_PAGE}`, - loadChildren: () => import('./competency-course-details-lazy.module').then(m => m.AddonCompetencyCourseDetailsLazyModule), + path: `${CORE_COURSE_PAGE_NAME}/:courseId/${ADDON_COMPETENCY_COMPETENCIES_PAGE}`, + loadChildren: () => import('./competency-course-details-lazy.module'), }, { - path: `${COURSE_PAGE_NAME}/:courseId/${PARTICIPANTS_PAGE_NAME}/:userId/${ADDON_COMPETENCY_COMPETENCIES_PAGE}`, - loadChildren: () => import('./competency-course-details-lazy.module').then(m => m.AddonCompetencyCourseDetailsLazyModule), + path: `${CORE_COURSE_PAGE_NAME}/:courseId/${PARTICIPANTS_PAGE_NAME}/:userId/${ADDON_COMPETENCY_COMPETENCIES_PAGE}`, + loadChildren: () => import('./competency-course-details-lazy.module'), }, ]; const courseIndexRoutes: Routes = [ { path: ADDON_COMPETENCY_COMPETENCIES_PAGE, - loadChildren: () => import('./competency-course-contents-lazy.module').then(m => m.AddonCompetencyCourseContentsLazyModule), + loadChildren: () => import('./competency-course-contents-lazy.module'), }, ]; diff --git a/src/addons/competency/pages/competency/competency.page.ts b/src/addons/competency/pages/competency/competency.page.ts index cb94fde051a..94e4c67d7e5 100644 --- a/src/addons/competency/pages/competency/competency.page.ts +++ b/src/addons/competency/pages/competency/competency.page.ts @@ -31,7 +31,7 @@ import { } from '@addons/competency/services/competency'; import { CoreNavigator } from '@services/navigator'; import { ContextLevel } from '@/core/constants'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { ADDON_COMPETENCY_SUMMARY_PAGE } from '@addons/competency/constants'; import { CoreSwipeNavigationItemsManager } from '@classes/items-management/swipe-navigation-items-manager'; import { CoreRoutedItemsManagerSourcesTracker } from '@classes/items-management/routed-items-manager-sources-tracker'; @@ -175,7 +175,7 @@ export class AddonCompetencyCompetencyPage implements OnInit, OnDestroy { async refreshCompetency(refresher: HTMLIonRefresherElement): Promise { const source = this.competencies.getSource(); - await CoreUtils.ignoreErrors( + await CorePromiseUtils.ignoreErrors( source instanceof AddonCompetencyPlanCompetenciesSource ? AddonCompetency.invalidateCompetencyInPlan(source.PLAN_ID, this.requireCompetencyId()) : AddonCompetency.invalidateCompetencyInCourse(source.COURSE_ID, this.requireCompetencyId(), source.USER_ID), @@ -287,7 +287,7 @@ export class AddonCompetencyCompetencyPage implements OnInit, OnDestroy { return; } - await CoreUtils.ignoreErrors( + await CorePromiseUtils.ignoreErrors( AddonCompetency.logCompetencyInPlanView(source.PLAN_ID, compId, this.planStatus, name, userId), ); @@ -316,7 +316,9 @@ export class AddonCompetencyCompetencyPage implements OnInit, OnDestroy { return; } - await CoreUtils.ignoreErrors(AddonCompetency.logCompetencyInCourseView(source.COURSE_ID, compId, name, source.USER_ID)); + await CorePromiseUtils.ignoreErrors( + AddonCompetency.logCompetencyInCourseView(source.COURSE_ID, compId, name, source.USER_ID), + ); CoreAnalytics.logEvent({ type: CoreAnalyticsEventType.VIEW_ITEM, diff --git a/src/addons/competency/pages/competencysummary/competencysummary.page.ts b/src/addons/competency/pages/competencysummary/competencysummary.page.ts index 8099d303f11..62cdc5c5eb6 100644 --- a/src/addons/competency/pages/competencysummary/competencysummary.page.ts +++ b/src/addons/competency/pages/competencysummary/competencysummary.page.ts @@ -17,7 +17,7 @@ import { ContextLevel } from '@/core/constants'; import { AddonCompetencySummary, AddonCompetency } from '@addons/competency/services/competency'; import { CoreNavigator } from '@services/navigator'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { ADDON_COMPETENCY_SUMMARY_PAGE } from '@addons/competency/constants'; import { CoreTime } from '@singletons/time'; import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics'; @@ -45,7 +45,7 @@ export class AddonCompetencyCompetencySummaryPage implements OnInit { return; } - await CoreUtils.ignoreErrors( + await CorePromiseUtils.ignoreErrors( AddonCompetency.logCompetencyView(this.competencyId, this.competency.competency.shortname), ); diff --git a/src/addons/competency/services/competency.ts b/src/addons/competency/services/competency.ts index d6b3ca68956..3ec9cf87cd3 100644 --- a/src/addons/competency/services/competency.ts +++ b/src/addons/competency/services/competency.ts @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +import { CoreCacheUpdateFrequency } from '@/core/constants'; import { Injectable } from '@angular/core'; import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site'; import { CoreSite } from '@classes/sites/site'; @@ -19,7 +20,7 @@ import { CoreCommentsArea } from '@features/comments/services/comments'; import { CoreCourseSummary } from '@features/course/services/course'; import { CoreUserSummary } from '@features/user/services/user'; import { CoreSites } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { makeSingleton } from '@singletons'; const ROOT_CACHE_KEY = 'mmaCompetency:'; @@ -188,7 +189,7 @@ export class AddonCompetencyProvider { return false; } - return CoreUtils.promiseWorks(this.getCourseCompetencies(courseId, undefined, siteId)); + return CorePromiseUtils.promiseWorks(this.getCourseCompetencies(courseId, undefined, siteId)); } /** @@ -208,7 +209,7 @@ export class AddonCompetencyProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getLearningPlansCacheKey(userId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, }; const response = await site.read('tool_lp_data_for_plans_page', params, preSets); @@ -232,7 +233,7 @@ export class AddonCompetencyProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getLearningPlanCacheKey(planId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, }; return site.read('tool_lp_data_for_plan_page', params, preSets); @@ -260,7 +261,7 @@ export class AddonCompetencyProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getCompetencyInPlanCacheKey(planId, competencyId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + updateFrequency: CoreCacheUpdateFrequency.SOMETIMES, }; return site.read( @@ -298,7 +299,7 @@ export class AddonCompetencyProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getCompetencyInCourseCacheKey(courseId, competencyId, userId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + updateFrequency: CoreCacheUpdateFrequency.SOMETIMES, }; if (ignoreCache) { @@ -334,7 +335,7 @@ export class AddonCompetencyProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getCompetencySummaryCacheKey(competencyId, userId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + updateFrequency: CoreCacheUpdateFrequency.SOMETIMES, }; if (ignoreCache) { @@ -400,7 +401,7 @@ export class AddonCompetencyProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getCourseCompetenciesCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + updateFrequency: CoreCacheUpdateFrequency.SOMETIMES, }; if (ignoreCache) { diff --git a/src/addons/competency/services/handlers/competency-link.ts b/src/addons/competency/services/handlers/competency-link.ts index 69a42f6eea7..f87ff9fd0ef 100644 --- a/src/addons/competency/services/handlers/competency-link.ts +++ b/src/addons/competency/services/handlers/competency-link.ts @@ -16,7 +16,7 @@ import { ADDON_COMPETENCY_COMPETENCIES_PAGE, ADDON_COMPETENCY_LEARNING_PLANS_PAG import { Injectable } from '@angular/core'; import { CoreContentLinksHandlerBase } from '@features/contentlinks/classes/base-handler'; import { CoreContentLinksAction } from '@features/contentlinks/services/contentlinks-delegate'; -import { COURSE_PAGE_NAME } from '@features/course/constants'; +import { CORE_COURSE_PAGE_NAME } from '@features/course/constants'; import { CoreNavigator } from '@services/navigator'; import { makeSingleton } from '@singletons'; import { AddonCompetency } from '../competency'; @@ -41,7 +41,7 @@ export class AddonCompetencyCompetencyLinkHandlerService extends CoreContentLink action: async (siteId: string): Promise => { if (courseId) { await CoreNavigator.navigateToSitePath( - `${COURSE_PAGE_NAME}/${courseId}/${ADDON_COMPETENCY_COMPETENCIES_PAGE}`, + `${CORE_COURSE_PAGE_NAME}/${courseId}/${ADDON_COMPETENCY_COMPETENCIES_PAGE}`, { params: { userId: params.userid }, siteId, diff --git a/src/addons/competency/services/handlers/course-option.ts b/src/addons/competency/services/handlers/course-option.ts index 1288774df81..62a400083e6 100644 --- a/src/addons/competency/services/handlers/course-option.ts +++ b/src/addons/competency/services/handlers/course-option.ts @@ -13,7 +13,7 @@ // limitations under the License. import { Injectable } from '@angular/core'; -import { CoreCourseAccessDataType } from '@features/course/services/course'; +import { CoreCourseAccessDataType } from '@features/course/constants'; import { CoreCourseAccess, CoreCourseOptionsHandler, diff --git a/src/addons/competency/services/handlers/push-click.ts b/src/addons/competency/services/handlers/push-click.ts index 63e4b1a5021..678d44d85b2 100644 --- a/src/addons/competency/services/handlers/push-click.ts +++ b/src/addons/competency/services/handlers/push-click.ts @@ -14,14 +14,15 @@ import { ADDON_COMPETENCY_COMPETENCIES_PAGE, ADDON_COMPETENCY_LEARNING_PLANS_PAGE } from '@addons/competency/constants'; import { Injectable } from '@angular/core'; -import { COURSE_PAGE_NAME } from '@features/course/constants'; +import { CORE_COURSE_PAGE_NAME } from '@features/course/constants'; import { CorePushNotificationsClickHandler } from '@features/pushnotifications/services/push-delegate'; import { CorePushNotificationsNotificationBasicData } from '@features/pushnotifications/services/pushnotifications'; import { CoreNavigator } from '@services/navigator'; import { CoreUrl } from '@singletons/url'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { makeSingleton } from '@singletons'; import { AddonCompetency } from '../competency'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Handler for competencies push notifications clicks. @@ -55,7 +56,7 @@ export class AddonCompetencyPushClickHandlerService implements CorePushNotificat // Open the learning plan. const planId = Number(contextUrlParams.id); - await CoreUtils.ignoreErrors(AddonCompetency.invalidateLearningPlan(planId, notification.site)); + await CorePromiseUtils.ignoreErrors(AddonCompetency.invalidateLearningPlan(planId, notification.site)); await CoreNavigator.navigateToSitePath(`${ADDON_COMPETENCY_LEARNING_PLANS_PAGE}/${planId}`, { siteId: notification.site, @@ -71,11 +72,13 @@ export class AddonCompetencyPushClickHandlerService implements CorePushNotificat const planId = Number(contextUrlParams.planid); const userId = Number(contextUrlParams.userid); - await CoreUtils.ignoreErrors(AddonCompetency.invalidateCompetencyInPlan(planId, competencyId, notification.site)); + await CorePromiseUtils.ignoreErrors( + AddonCompetency.invalidateCompetencyInPlan(planId, competencyId, notification.site), + ); if (courseId) { await CoreNavigator.navigateToSitePath( - `${COURSE_PAGE_NAME}/${courseId}/${ADDON_COMPETENCY_COMPETENCIES_PAGE}/${competencyId}`, + `${CORE_COURSE_PAGE_NAME}/${courseId}/${ADDON_COMPETENCY_COMPETENCIES_PAGE}/${competencyId}`, { params: { userId }, siteId: notification.site, @@ -101,7 +104,7 @@ export class AddonCompetencyPushClickHandlerService implements CorePushNotificat // Open the list of plans. const userId = Number(contextUrlParams.userid); - await CoreUtils.ignoreErrors(AddonCompetency.invalidateLearningPlans(userId, notification.site)); + await CorePromiseUtils.ignoreErrors(AddonCompetency.invalidateLearningPlans(userId, notification.site)); await CoreNavigator.navigateToSitePath(ADDON_COMPETENCY_LEARNING_PLANS_PAGE, { params: { userId }, diff --git a/src/addons/competency/services/handlers/user.ts b/src/addons/competency/services/handlers/user.ts index 28c06c66e39..a2caca4f289 100644 --- a/src/addons/competency/services/handlers/user.ts +++ b/src/addons/competency/services/handlers/user.ts @@ -14,7 +14,7 @@ import { ADDON_COMPETENCY_COMPETENCIES_PAGE, ADDON_COMPETENCY_LEARNING_PLANS_PAGE } from '@addons/competency/constants'; import { Injectable } from '@angular/core'; -import { COURSE_PAGE_NAME } from '@features/course/constants'; +import { CORE_COURSE_PAGE_NAME } from '@features/course/constants'; import { CoreUserProfile } from '@features/user/services/user'; import { CoreUserProfileHandler, @@ -112,9 +112,13 @@ export class AddonCompetencyUserHandlerService implements CoreUserProfileHandler action: (event, user, context, contextId): void => { event.preventDefault(); event.stopPropagation(); - CoreNavigator.navigateToSitePath( - [COURSE_PAGE_NAME, contextId, PARTICIPANTS_PAGE_NAME, user.id, ADDON_COMPETENCY_COMPETENCIES_PAGE].join('/'), - ); + CoreNavigator.navigateToSitePath([ + CORE_COURSE_PAGE_NAME, + contextId, + PARTICIPANTS_PAGE_NAME, + user.id, + ADDON_COMPETENCY_COMPETENCIES_PAGE, + ].join('/')); }, }; } diff --git a/src/addons/coursecompletion/coursecompletion-lazy.module.ts b/src/addons/coursecompletion/coursecompletion-lazy.module.ts index 2cf6534f8bc..766ec57acf9 100644 --- a/src/addons/coursecompletion/coursecompletion-lazy.module.ts +++ b/src/addons/coursecompletion/coursecompletion-lazy.module.ts @@ -37,4 +37,4 @@ const routes: Routes = [ AddonCourseCompletionReportPage, ], }) -export class AddonCourseCompletionLazyModule {} +export default class AddonCourseCompletionLazyModule {} diff --git a/src/addons/coursecompletion/coursecompletion.module.ts b/src/addons/coursecompletion/coursecompletion.module.ts index 5dca1d45071..68738252069 100644 --- a/src/addons/coursecompletion/coursecompletion.module.ts +++ b/src/addons/coursecompletion/coursecompletion.module.ts @@ -39,7 +39,7 @@ export async function getCourseCompletionServices(): Promise[]> { const routes: Routes = [ { path: 'coursecompletion', - loadChildren: () => import('./coursecompletion-lazy.module').then(m => m.AddonCourseCompletionLazyModule), + loadChildren: () => import('./coursecompletion-lazy.module'), }, ]; diff --git a/src/addons/coursecompletion/services/coursecompletion.ts b/src/addons/coursecompletion/services/coursecompletion.ts index 02de4257715..2c3094379a0 100644 --- a/src/addons/coursecompletion/services/coursecompletion.ts +++ b/src/addons/coursecompletion/services/coursecompletion.ts @@ -15,7 +15,7 @@ import { Injectable } from '@angular/core'; import { CoreLogger } from '@singletons/logger'; import { CoreSites, CoreSitesCommonWSOptions } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreWSError } from '@classes/errors/wserror'; import { CoreCourseAnyCourseData, CoreCourses } from '@features/courses/services/courses'; import { CoreSite } from '@classes/sites/site'; import { CoreStatusWithWarningsWSResponse, CoreWSExternalWarning } from '@services/ws'; @@ -25,6 +25,7 @@ import { asyncObservable } from '@/core/utils/rxjs'; import { map } from 'rxjs/operators'; import { CoreSiteWSPreSets, WSObservable } from '@classes/sites/authenticated-site'; import { firstValueFrom } from 'rxjs'; +import { CoreCacheUpdateFrequency } from '@/core/constants'; const ROOT_CACHE_KEY = 'mmaCourseCompletion:'; @@ -173,7 +174,7 @@ export class AddonCourseCompletionProvider { const preSets = { ...(options.preSets ?? {}), cacheKey: this.getCompletionCacheKey(courseId, userId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + updateFrequency: CoreCacheUpdateFrequency.SOMETIMES, cacheErrors: ['notenroled'], ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), }; @@ -283,7 +284,7 @@ export class AddonCourseCompletionProvider { return true; } catch (error) { - if (CoreUtils.isWebServiceError(error)) { + if (CoreWSError.isWebServiceError(error)) { // The WS returned an error, plugin is not enabled. return false; } diff --git a/src/addons/coursecompletion/services/handlers/course-option.ts b/src/addons/coursecompletion/services/handlers/course-option.ts index 928cb66912c..64779e6e9bb 100644 --- a/src/addons/coursecompletion/services/handlers/course-option.ts +++ b/src/addons/coursecompletion/services/handlers/course-option.ts @@ -13,7 +13,7 @@ // limitations under the License. import { Injectable } from '@angular/core'; -import { CoreCourseAccessDataType } from '@features/course/services/course'; +import { CoreCourseAccessDataType } from '@features/course/constants'; import { CoreCourseAccess, CoreCourseOptionsHandler, diff --git a/src/addons/enrol/guest/services/guest.ts b/src/addons/enrol/guest/services/guest.ts index dc5bcfdf956..333bb5a792e 100644 --- a/src/addons/enrol/guest/services/guest.ts +++ b/src/addons/enrol/guest/services/guest.ts @@ -12,9 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +import { CoreCacheUpdateFrequency } from '@/core/constants'; import { Injectable } from '@angular/core'; import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site'; -import { CoreSite } from '@classes/sites/site'; import { CoreEnrolEnrolmentInfo } from '@features/enrol/services/enrol'; import { CoreSites } from '@services/sites'; import { CoreWSExternalWarning } from '@services/ws'; @@ -44,7 +44,7 @@ export class AddonEnrolGuestService { const preSets: CoreSiteWSPreSets = { cacheKey: this.getGuestEnrolmentInfoCacheKey(instanceId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, }; const response = @@ -68,15 +68,11 @@ export class AddonEnrolGuestService { * * @param instanceId Guest instance ID. * @param siteId Site Id. If not defined, use current site. - * @returns Promise resolved when the data is invalidated. */ async invalidateGuestEnrolmentInfo(instanceId: number, siteId?: string): Promise { const site = await CoreSites.getSite(siteId); - await Promise.all([ - site.invalidateWsCacheForKey(this.getGuestEnrolmentInfoCacheKey(instanceId)), - site.invalidateWsCacheForKey(`mmCourses:guestinfo:${instanceId}`), // @todo Remove after 4.3 release. - ]); + await site.invalidateWsCacheForKey(this.getGuestEnrolmentInfoCacheKey(instanceId)); } /** diff --git a/src/addons/enrol/self/services/enrol-handler.ts b/src/addons/enrol/self/services/enrol-handler.ts index 68a667bcdf8..6e74137c9d1 100644 --- a/src/addons/enrol/self/services/enrol-handler.ts +++ b/src/addons/enrol/self/services/enrol-handler.ts @@ -17,11 +17,11 @@ import { CoreEnrolAction, CoreEnrolSelfHandler, CoreEnrolInfoIcon } from '@featu import { Translate, makeSingleton } from '@singletons'; import { AddonEnrolSelf } from './self'; import { CorePasswordModalResponse } from '@components/password-modal/password-modal'; -import { CoreCoursesProvider } from '@features/courses/services/courses'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreEnrol, CoreEnrolEnrolmentMethod } from '@features/enrol/services/enrol'; import { CoreModals } from '@services/modals'; import { CoreLoadings } from '@services/loadings'; +import { CORE_COURSES_ENROL_INVALID_KEY } from '@features/courses/constants'; /** * Enrol handler. @@ -122,7 +122,7 @@ export class AddonEnrolSelfHandlerService implements CoreEnrolSelfHandler { try { response.validated = await AddonEnrolSelf.selfEnrol(method.courseid, password, method.id); } catch (error) { - if (error && error.errorcode === CoreCoursesProvider.ENROL_INVALID_KEY) { + if (error && error.errorcode === CORE_COURSES_ENROL_INVALID_KEY) { response.validated = false; response.error = error.message; } else { diff --git a/src/addons/enrol/self/services/self.ts b/src/addons/enrol/self/services/self.ts index a3aea2138f2..03e7dd1da38 100644 --- a/src/addons/enrol/self/services/self.ts +++ b/src/addons/enrol/self/services/self.ts @@ -12,11 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +import { CoreCacheUpdateFrequency } from '@/core/constants'; import { Injectable } from '@angular/core'; import { CoreWSError } from '@classes/errors/wserror'; import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site'; -import { CoreSite } from '@classes/sites/site'; -import { CoreCoursesProvider } from '@features/courses/services/courses'; +import { CORE_COURSES_ENROL_INVALID_KEY } from '@features/courses/constants'; import { CoreSites } from '@services/sites'; import { CoreStatusWithWarningsWSResponse } from '@services/ws'; import { makeSingleton } from '@singletons'; @@ -45,7 +45,7 @@ export class AddonEnrolSelfService { const preSets: CoreSiteWSPreSets = { cacheKey: this.getSelfEnrolmentInfoCacheKey(instanceId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, }; return await site.read('enrol_self_get_instance_info', params, preSets); @@ -82,7 +82,7 @@ export class AddonEnrolSelfService { * @param instanceId Enrol instance ID. * @param siteId Site ID. If not defined, use current site. * @returns Promise resolved if the user is enrolled. If the password is invalid, the promise is rejected - * with an object with errorcode = CoreCoursesProvider.ENROL_INVALID_KEY. + * with an object with errorcode = CORE_COURSES_ENROL_INVALID_KEY. */ async selfEnrol(courseId: number, password: string = '', instanceId?: number, siteId?: string): Promise { @@ -112,7 +112,7 @@ export class AddonEnrolSelfService { warning.warningcode == '2' || warning.warningcode == '3' || warning.warningcode == '4'); if (warning) { - throw new CoreWSError({ errorcode: CoreCoursesProvider.ENROL_INVALID_KEY, message: warning.message }); + throw new CoreWSError({ errorcode: CORE_COURSES_ENROL_INVALID_KEY, message: warning.message }); } else { throw new CoreWSError(response.warnings[0]); } diff --git a/src/addons/filter/mathjaxloader/services/handlers/mathjaxloader.ts b/src/addons/filter/mathjaxloader/services/handlers/mathjaxloader.ts index 0ad77d343c3..6d96b9607fa 100644 --- a/src/addons/filter/mathjaxloader/services/handlers/mathjaxloader.ts +++ b/src/addons/filter/mathjaxloader/services/handlers/mathjaxloader.ts @@ -19,7 +19,7 @@ import { CoreFilterFilter, CoreFilterFormatTextOptions } from '@features/filter/ import { CoreLang } from '@services/lang'; import { CoreSites } from '@services/sites'; import { CoreText } from '@singletons/text'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreEvents } from '@singletons/events'; import { CoreSite } from '@classes/sites/site'; import { makeSingleton } from '@singletons'; @@ -180,7 +180,7 @@ export class AddonFilterMathJaxLoaderHandlerService extends CoreFilterDefaultHan // Make sure the element is in DOM, otherwise some equations don't work. // Automatically timeout the promise after a certain time, we don't want to wait forever. - await CoreUtils.ignoreErrors(CoreUtils.timeoutPromise(CoreDom.waitToBeInDOM(container), 15000)); + await CorePromiseUtils.ignoreErrors(CorePromiseUtils.timeoutPromise(CoreDom.waitToBeInDOM(container), 15000)); await this.window.M!.filter_mathjaxloader!.typeset(container); } @@ -336,7 +336,7 @@ export class AddonFilterMathJaxLoaderHandlerService extends CoreFilterDefaultHan } await CoreWait.wait(250); - await CoreUtils.ignoreErrors(this.waitForReady(retries + 1)); + await CorePromiseUtils.ignoreErrors(this.waitForReady(retries + 1)); } /** diff --git a/src/addons/messageoutput/airnotifier/pages/devices/devices.ts b/src/addons/messageoutput/airnotifier/pages/devices/devices.ts index 829d4f798f5..3850195ca74 100644 --- a/src/addons/messageoutput/airnotifier/pages/devices/devices.ts +++ b/src/addons/messageoutput/airnotifier/pages/devices/devices.ts @@ -17,7 +17,7 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { CoreDomUtils } from '@services/utils/dom'; import { CorePushNotifications } from '@features/pushnotifications/services/pushnotifications'; import { AddonMessageOutputAirnotifier, AddonMessageOutputAirnotifierDevice } from '../../services/airnotifier'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Page that displays the list of devices. @@ -112,7 +112,7 @@ export class AddonMessageOutputAirnotifierDevicesPage implements OnInit, OnDestr * Fetch devices. The purpose is to store the updated data, it won't be reflected in the view. */ protected async updateDevices(): Promise { - await CoreUtils.ignoreErrors(AddonMessageOutputAirnotifier.invalidateUserDevices()); + await CorePromiseUtils.ignoreErrors(AddonMessageOutputAirnotifier.invalidateUserDevices()); await AddonMessageOutputAirnotifier.getUserDevices(); } @@ -124,7 +124,7 @@ export class AddonMessageOutputAirnotifierDevicesPage implements OnInit, OnDestr */ async refreshDevices(refresher: HTMLIonRefresherElement): Promise { try { - await CoreUtils.ignoreErrors(AddonMessageOutputAirnotifier.invalidateUserDevices()); + await CorePromiseUtils.ignoreErrors(AddonMessageOutputAirnotifier.invalidateUserDevices()); await this.fetchDevices(); } finally { diff --git a/src/addons/messageoutput/airnotifier/services/airnotifier.ts b/src/addons/messageoutput/airnotifier/services/airnotifier.ts index e8c94d7b42b..022c20a523f 100644 --- a/src/addons/messageoutput/airnotifier/services/airnotifier.ts +++ b/src/addons/messageoutput/airnotifier/services/airnotifier.ts @@ -16,14 +16,13 @@ import { Injectable } from '@angular/core'; import { CoreSites, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@services/sites'; import { CoreWSExternalWarning } from '@services/ws'; -import { CoreConstants } from '@/core/constants'; -import { CoreSite } from '@classes/sites/site'; +import { CoreCacheUpdateFrequency, CoreConstants } from '@/core/constants'; import { CoreError } from '@classes/errors/error'; import { CoreWSError } from '@classes/errors/wserror'; import { makeSingleton, Translate } from '@singletons'; import { CoreEvents, CoreEventSiteData } from '@singletons/events'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreOpener } from '@singletons/opener'; import { CorePath } from '@singletons/path'; import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site'; @@ -102,7 +101,7 @@ export class AddonMessageOutputAirnotifierProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getSystemConfiguredCacheKey(), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; @@ -136,7 +135,7 @@ export class AddonMessageOutputAirnotifierProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getUserDevicesCacheKey(), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, }; if (ignoreCache) { @@ -223,7 +222,7 @@ export class AddonMessageOutputAirnotifierProvider { ); // Don't try auto-login, admins cannot use it. - CoreUtils.openInBrowser(url, { + CoreOpener.openInBrowser(url, { showBrowserWarning: false, }); }, diff --git a/src/addons/messages/constants.ts b/src/addons/messages/constants.ts new file mode 100644 index 00000000000..402afc3f59c --- /dev/null +++ b/src/addons/messages/constants.ts @@ -0,0 +1,52 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +export const ADDON_MESSAGES_NEW_MESSAGE_EVENT = 'addon_messages_new_message_event'; +export const ADDON_MESSAGES_READ_CHANGED_EVENT = 'addon_messages_read_changed_event'; +// Notify a conversation should be opened. +export const ADDON_MESSAGES_OPEN_CONVERSATION_EVENT = 'addon_messages_open_conversation_event'; +export const ADDON_MESSAGES_UPDATE_CONVERSATION_LIST_EVENT = 'addon_messages_update_conversation_list_event'; +export const ADDON_MESSAGES_MEMBER_INFO_CHANGED_EVENT = 'addon_messages_member_changed_event'; +export const ADDON_MESSAGES_UNREAD_CONVERSATION_COUNTS_EVENT = 'addon_messages_unread_conversation_counts_event'; +export const ADDON_MESSAGES_CONTACT_REQUESTS_COUNT_EVENT = 'addon_messages_contact_requests_count_event'; + +export const ADDON_MESSAGES_POLL_INTERVAL = 10000; +export const ADDON_MESSAGES_PUSH_SIMULATION_COMPONENT = 'AddonMessagesPushSimulation'; + +export const enum AddonMessagesMessagePrivacy { + COURSEMEMBER = 0, // Privacy setting for being messaged by anyone within courses user is member. + ONLYCONTACTS = 1, // Privacy setting for being messaged only by contacts. + SITE = 2, // Privacy setting for being messaged by anyone on the site. +} + +export const enum AddonMessagesMessageConversationType { + INDIVIDUAL = 1, // An individual conversation. + GROUP = 2, // A group conversation. + SELF = 3, // A self conversation. +} + +export const ADDON_MESSAGES_LIMIT_CONTACTS = 50; +export const ADDON_MESSAGES_LIMIT_MESSAGES = 50; +export const ADDON_MESSAGES_LIMIT_INITIAL_USER_SEARCH = 3; +export const ADDON_MESSAGES_LIMIT_SEARCH = 50; + +export const ADDON_MESSAGES_NOTIFICATION_PREFERENCES_KEY = 'message_provider_moodle_instantmessage'; + +export const ADDON_MESSAGES_AUTO_SYNCED = 'addon_messages_autom_synced'; + +export const enum AddonMessagesUpdateConversationAction { + MUTE = 'mute', + FAVOURITE = 'favourite', + DELETE = 'delete', +} diff --git a/src/addons/messages/messages-lazy.module.ts b/src/addons/messages/messages-lazy.module.ts index e6ec4044241..fd571c34eca 100644 --- a/src/addons/messages/messages-lazy.module.ts +++ b/src/addons/messages/messages-lazy.module.ts @@ -117,7 +117,7 @@ function buildRoutes(injector: Injector): Routes { ...discussionRoutes, { path: 'message-settings', - loadChildren: () => import('./messages-settings-lazy.module').then(m => m.AddonMessagesSettingsLazyModule), + loadChildren: () => import('./messages-settings-lazy.module'), }, ...buildTabMainRoutes(injector, { canActivate: [messagesIndexGuard], @@ -148,4 +148,4 @@ function buildRoutes(injector: Injector): Routes { }, ], }) -export class AddonMessagesLazyModule {} +export default class AddonMessagesLazyModule {} diff --git a/src/addons/messages/messages-settings-lazy.module.ts b/src/addons/messages/messages-settings-lazy.module.ts index dbfc1d4dae7..15f93258b62 100644 --- a/src/addons/messages/messages-settings-lazy.module.ts +++ b/src/addons/messages/messages-settings-lazy.module.ts @@ -34,4 +34,4 @@ const routes: Routes = [ AddonMessagesSettingsPage, ], }) -export class AddonMessagesSettingsLazyModule {} +export default class AddonMessagesSettingsLazyModule {} diff --git a/src/addons/messages/messages.module.ts b/src/addons/messages/messages.module.ts index 88ee85b7503..5f2bfa18376 100644 --- a/src/addons/messages/messages.module.ts +++ b/src/addons/messages/messages.module.ts @@ -58,13 +58,13 @@ export async function getMessagesServices(): Promise[]> { const mainMenuChildrenRoutes: Routes = [ { path: AddonMessagesMainMenuHandlerService.PAGE_NAME, - loadChildren: () => import('./messages-lazy.module').then(m => m.AddonMessagesLazyModule), + loadChildren: () => import('./messages-lazy.module'), }, ]; const preferencesRoutes: Routes = [ { path: AddonMessagesSettingsHandlerService.PAGE_NAME, - loadChildren: () => import('./messages-settings-lazy.module').then(m => m.AddonMessagesSettingsLazyModule), + loadChildren: () => import('./messages-settings-lazy.module'), }, ]; diff --git a/src/addons/messages/pages/contacts-35/contacts.ts b/src/addons/messages/pages/contacts-35/contacts.ts index a75496fb5fd..80d292b7404 100644 --- a/src/addons/messages/pages/contacts-35/contacts.ts +++ b/src/addons/messages/pages/contacts-35/contacts.ts @@ -15,7 +15,6 @@ import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { CoreSites } from '@services/sites'; import { - AddonMessagesProvider, AddonMessagesGetContactsWSResponse, AddonMessagesSearchContactsContact, AddonMessagesGetContactsContact, @@ -29,6 +28,7 @@ import { CoreScreen } from '@services/screen'; import { CoreNavigator } from '@services/navigator'; import { CoreSplitViewComponent } from '@components/split-view/split-view'; import { CoreKeyboard } from '@singletons/keyboard'; +import { ADDON_MESSAGES_MEMBER_INFO_CHANGED_EVENT } from '@addons/messages/constants'; /** * Page that displays the list of contacts. @@ -36,7 +36,7 @@ import { CoreKeyboard } from '@singletons/keyboard'; @Component({ selector: 'addon-messages-contacts', templateUrl: 'contacts.html', - styleUrls: ['../../messages-common.scss'], + styleUrl: '../../messages-common.scss', }) export class AddonMessagesContacts35Page implements OnInit, OnDestroy { @@ -73,7 +73,7 @@ export class AddonMessagesContacts35Page implements OnInit, OnDestroy { // Refresh the list when a contact request is confirmed. this.memberInfoObserver = CoreEvents.on( - AddonMessagesProvider.MEMBER_INFO_CHANGED_EVENT, + ADDON_MESSAGES_MEMBER_INFO_CHANGED_EVENT, (data) => { if (data.contactRequestConfirmed) { this.refreshData(); diff --git a/src/addons/messages/pages/contacts/contacts.ts b/src/addons/messages/pages/contacts/contacts.ts index 94766b892c4..2e12a7d427c 100644 --- a/src/addons/messages/pages/contacts/contacts.ts +++ b/src/addons/messages/pages/contacts/contacts.ts @@ -18,12 +18,12 @@ import { CoreSites } from '@services/sites'; import { AddonMessages, AddonMessagesConversationMember, - AddonMessagesProvider, } from '../../services/messages'; import { CoreNavigator } from '@services/navigator'; import { CoreScreen } from '@services/screen'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreSplitViewComponent } from '@components/split-view/split-view'; +import { ADDON_MESSAGES_CONTACT_REQUESTS_COUNT_EVENT, ADDON_MESSAGES_MEMBER_INFO_CHANGED_EVENT } from '@addons/messages/constants'; /** * Page that displays contacts and contact requests. @@ -31,9 +31,7 @@ import { CoreSplitViewComponent } from '@components/split-view/split-view'; @Component({ selector: 'page-addon-messages-contacts', templateUrl: 'contacts.html', - styleUrls: [ - '../../messages-common.scss', - ], + styleUrl: '../../messages-common.scss', }) export class AddonMessagesContactsPage implements OnInit, OnDestroy { @@ -65,7 +63,7 @@ export class AddonMessagesContactsPage implements OnInit, OnDestroy { // Update the contact requests badge. this.contactRequestsCountObserver = CoreEvents.on( - AddonMessagesProvider.CONTACT_REQUESTS_COUNT_EVENT, + ADDON_MESSAGES_CONTACT_REQUESTS_COUNT_EVENT, (data) => { this.requestsBadge = data.count > 0 ? String(data.count) : ''; }, @@ -74,7 +72,7 @@ export class AddonMessagesContactsPage implements OnInit, OnDestroy { // Update block status of a user. this.memberInfoObserver = CoreEvents.on( - AddonMessagesProvider.MEMBER_INFO_CHANGED_EVENT, + ADDON_MESSAGES_MEMBER_INFO_CHANGED_EVENT, (data) => { if (data.userBlocked || data.userUnblocked) { const user = this.confirmedContacts.find((user) => user.id == data.userId); diff --git a/src/addons/messages/pages/discussion/discussion.ts b/src/addons/messages/pages/discussion/discussion.ts index 7fa11ca0242..9da031d1b71 100644 --- a/src/addons/messages/pages/discussion/discussion.ts +++ b/src/addons/messages/pages/discussion/discussion.ts @@ -18,20 +18,18 @@ import { AlertOptions } from '@ionic/core'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreSites } from '@services/sites'; import { - AddonMessagesProvider, AddonMessagesConversationFormatted, AddonMessagesConversationMember, AddonMessagesGetMessagesMessage, AddonMessages, AddonMessagesConversationMessageFormatted, AddonMessagesSendMessageResults, - AddonMessagesUpdateConversationAction, } from '../../services/messages'; import { AddonMessagesOffline, AddonMessagesOfflineMessagesDBRecordFormatted } from '../../services/messages-offline'; -import { AddonMessagesSync, AddonMessagesSyncProvider } from '../../services/messages-sync'; +import { AddonMessagesSync } from '../../services/messages-sync'; import { CoreUser } from '@features/user/services/user'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreLogger } from '@singletons/logger'; import { CoreInfiniteLoadingComponent } from '@components/infinite-loading/infinite-loading'; import { Md5 } from 'ts-md5/dist/md5'; @@ -48,6 +46,18 @@ import { CoreText } from '@singletons/text'; import { CoreWait } from '@singletons/wait'; import { CoreModals } from '@services/modals'; import { CoreLoadings } from '@services/loadings'; +import { + ADDON_MESSAGES_AUTO_SYNCED, + ADDON_MESSAGES_LIMIT_MESSAGES, + ADDON_MESSAGES_MEMBER_INFO_CHANGED_EVENT, + ADDON_MESSAGES_NEW_MESSAGE_EVENT, + ADDON_MESSAGES_OPEN_CONVERSATION_EVENT, + ADDON_MESSAGES_POLL_INTERVAL, + ADDON_MESSAGES_READ_CHANGED_EVENT, + ADDON_MESSAGES_UPDATE_CONVERSATION_LIST_EVENT, + AddonMessagesMessageConversationType, + AddonMessagesUpdateConversationAction, +} from '@addons/messages/constants'; /** * Page that displays a message discussion page. @@ -125,7 +135,7 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView this.logger = CoreLogger.getInstance('AddonMessagesDiscussionPage'); // Refresh data if this discussion is synchronized automatically. - this.syncObserver = CoreEvents.on(AddonMessagesSyncProvider.AUTO_SYNCED, (data) => { + this.syncObserver = CoreEvents.on(ADDON_MESSAGES_AUTO_SYNCED, (data) => { if ((data.userId && data.userId == this.userId) || (data.conversationId && data.conversationId == this.conversationId)) { // Fetch messages. @@ -140,7 +150,7 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView // Refresh data if info of a mamber of the conversation have changed. this.memberInfoObserver = CoreEvents.on( - AddonMessagesProvider.MEMBER_INFO_CHANGED_EVENT, + ADDON_MESSAGES_MEMBER_INFO_CHANGED_EVENT, (data) => { if (data.userId && (this.members[data.userId] || this.otherMember && data.userId == this.otherMember.id)) { this.fetchData(); @@ -595,13 +605,13 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView this.conversationId = this.conversation.id; this.title = this.conversation.name; this.conversationImage = this.conversation.imageurl; - this.isGroup = this.conversation.type == AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_GROUP; + this.isGroup = this.conversation.type === AddonMessagesMessageConversationType.GROUP; this.favouriteIcon = 'fas-star'; this.muteIcon = this.conversation.ismuted ? 'fas-bell' : 'fas-bell-slash'; if (!this.isGroup) { this.userId = this.conversation.userid; } - this.isSelf = this.conversation.type == AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_SELF; + this.isSelf = this.conversation.type === AddonMessagesMessageConversationType.SELF; return true; } else { @@ -645,7 +655,7 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView const messages: AddonMessagesConversationMessageFormatted[] = result.messages; if (pagesToLoad > 0 && result.canLoadMore) { - offset += AddonMessagesProvider.LIMIT_MESSAGES; + offset += ADDON_MESSAGES_LIMIT_MESSAGES; // Get more messages. const nextMessages = await this.getConversationMessages(pagesToLoad, offset); @@ -764,7 +774,7 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView } if (readChanged) { - CoreEvents.trigger(AddonMessagesProvider.READ_CHANGED_EVENT, { + CoreEvents.trigger(ADDON_MESSAGES_READ_CHANGED_EVENT, { conversationId: this.conversationId, userId: this.userId, }, this.siteId); @@ -789,7 +799,7 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView if (trigger) { // Update discussions last message. - CoreEvents.trigger(AddonMessagesProvider.NEW_MESSAGE_EVENT, { + CoreEvents.trigger(ADDON_MESSAGES_NEW_MESSAGE_EVENT, { conversationId: this.conversationId, userId: this.userId, message: this.lastMessage?.text, @@ -887,7 +897,7 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView } await CoreWait.wait(400); - await CoreUtils.ignoreErrors(this.waitForFetch()); + await CorePromiseUtils.ignoreErrors(this.waitForFetch()); } /** @@ -905,7 +915,7 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView this.fetchMessages().catch(() => { // Ignore errors. }); - }, AddonMessagesProvider.POLL_INTERVAL); + }, ADDON_MESSAGES_POLL_INTERVAL); } } @@ -1265,7 +1275,7 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView if (splitViewLoaded) { // Notify the left pane to load it, this way the right conversation will be highlighted. CoreEvents.trigger( - AddonMessagesProvider.OPEN_CONVERSATION_EVENT, + ADDON_MESSAGES_OPEN_CONVERSATION_EVENT, { userId }, this.siteId, ); @@ -1300,7 +1310,7 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView // Get the conversation data so it's cached. Don't block the user for this. AddonMessages.getConversation(this.conversation.id, undefined, true); - CoreEvents.trigger(AddonMessagesProvider.UPDATE_CONVERSATION_LIST_EVENT, { + CoreEvents.trigger(ADDON_MESSAGES_UPDATE_CONVERSATION_LIST_EVENT, { conversationId: this.conversation.id, action: AddonMessagesUpdateConversationAction.FAVOURITE, value: this.conversation.isfavourite, @@ -1332,7 +1342,7 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView // Get the conversation data so it's cached. Don't block the user for this. AddonMessages.getConversation(this.conversation.id, undefined, true); - CoreEvents.trigger(AddonMessagesProvider.UPDATE_CONVERSATION_LIST_EVENT, { + CoreEvents.trigger(ADDON_MESSAGES_UPDATE_CONVERSATION_LIST_EVENT, { conversationId: this.conversation.id, action: AddonMessagesUpdateConversationAction.MUTE, value: this.conversation.ismuted, @@ -1447,7 +1457,7 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView await AddonMessages.deleteConversation(this.conversation.id); CoreEvents.trigger( - AddonMessagesProvider.UPDATE_CONVERSATION_LIST_EVENT, + ADDON_MESSAGES_UPDATE_CONVERSATION_LIST_EVENT, { conversationId: this.conversation.id, action: AddonMessagesUpdateConversationAction.DELETE, diff --git a/src/addons/messages/pages/discussions-35/discussions.ts b/src/addons/messages/pages/discussions-35/discussions.ts index b6375820b7f..2051d36d02c 100644 --- a/src/addons/messages/pages/discussions-35/discussions.ts +++ b/src/addons/messages/pages/discussions-35/discussions.ts @@ -19,10 +19,9 @@ import { AddonMessages, AddonMessagesDiscussion, AddonMessagesMessageAreaContact, - AddonMessagesProvider, } from '../../services/messages'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { ActivatedRoute, Params } from '@angular/router'; import { CorePushNotificationsNotificationBasicData } from '@features/pushnotifications/services/pushnotifications'; import { CorePushNotificationsDelegate } from '@features/pushnotifications/services/push-delegate'; @@ -33,6 +32,8 @@ import { CoreScreen } from '@services/screen'; import { CorePlatform } from '@services/platform'; import { CoreSplitViewComponent } from '@components/split-view/split-view'; import { CoreKeyboard } from '@singletons/keyboard'; +import { ADDON_MESSAGES_NEW_MESSAGE_EVENT, ADDON_MESSAGES_READ_CHANGED_EVENT } from '@addons/messages/constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Page that displays the list of discussions. @@ -40,7 +41,7 @@ import { CoreKeyboard } from '@singletons/keyboard'; @Component({ selector: 'addon-messages-discussions', templateUrl: 'discussions.html', - styleUrls: ['../../messages-common.scss'], + styleUrl: '../../messages-common.scss', }) export class AddonMessagesDiscussions35Page implements OnInit, OnDestroy { @@ -75,7 +76,7 @@ export class AddonMessagesDiscussions35Page implements OnInit, OnDestroy { // Update discussions when new message is received. this.newMessagesObserver = CoreEvents.on( - AddonMessagesProvider.NEW_MESSAGE_EVENT, + ADDON_MESSAGES_NEW_MESSAGE_EVENT, (data) => { if (data.userId && this.discussions) { const discussion = this.discussions.find((disc) => disc.message?.user === data.userId); @@ -97,7 +98,7 @@ export class AddonMessagesDiscussions35Page implements OnInit, OnDestroy { // Update discussions when a message is read. this.readChangedObserver = CoreEvents.on( - AddonMessagesProvider.READ_CHANGED_EVENT, + ADDON_MESSAGES_READ_CHANGED_EVENT, (data) => { if (data.userId && this.discussions) { const discussion = this.discussions.find((disc) => disc.message?.user === data.userId); @@ -170,7 +171,7 @@ export class AddonMessagesDiscussions35Page implements OnInit, OnDestroy { promises.push(AddonMessages.invalidateUnreadConversationCounts(this.siteId)); } - await CoreUtils.allPromises(promises).finally(() => this.fetchData().finally(() => { + await CorePromiseUtils.allPromises(promises).finally(() => this.fetchData().finally(() => { if (refresher) { refresher?.complete(); } diff --git a/src/addons/messages/pages/group-conversations/group-conversations.ts b/src/addons/messages/pages/group-conversations/group-conversations.ts index 03f1eca6e60..363307e019d 100644 --- a/src/addons/messages/pages/group-conversations/group-conversations.ts +++ b/src/addons/messages/pages/group-conversations/group-conversations.ts @@ -17,12 +17,10 @@ import { AccordionGroupChangeEventDetail, IonAccordionGroup, IonContent } from ' import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreSites } from '@services/sites'; import { - AddonMessagesProvider, AddonMessagesConversationFormatted, AddonMessages, AddonMessagesNewMessagedEventData, AddonMessagesUnreadConversationCountsEventData, - AddonMessagesUpdateConversationAction, } from '../../services/messages'; import { AddonMessagesOffline, @@ -35,11 +33,22 @@ import { Translate } from '@singletons'; import { Subscription } from 'rxjs'; import { CorePushNotificationsNotificationBasicData } from '@features/pushnotifications/services/pushnotifications'; import { ActivatedRoute, Params } from '@angular/router'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { CoreNavigator } from '@services/navigator'; import { CoreScreen } from '@services/screen'; import { CorePlatform } from '@services/platform'; import { CoreSplitViewComponent } from '@components/split-view/split-view'; +import { + ADDON_MESSAGES_CONTACT_REQUESTS_COUNT_EVENT, + ADDON_MESSAGES_MEMBER_INFO_CHANGED_EVENT, + ADDON_MESSAGES_NEW_MESSAGE_EVENT, + ADDON_MESSAGES_OPEN_CONVERSATION_EVENT, + ADDON_MESSAGES_READ_CHANGED_EVENT, + ADDON_MESSAGES_UNREAD_CONVERSATION_COUNTS_EVENT, + ADDON_MESSAGES_UPDATE_CONVERSATION_LIST_EVENT, + AddonMessagesMessageConversationType, + AddonMessagesUpdateConversationAction, +} from '@addons/messages/constants'; const enum AddonMessagesGroupConversationOptionNames { FAVOURITES = 'favourites', @@ -53,7 +62,7 @@ const enum AddonMessagesGroupConversationOptionNames { @Component({ selector: 'page-addon-messages-group-conversations', templateUrl: 'group-conversations.html', - styleUrls: ['../../messages-common.scss'], + styleUrl: '../../messages-common.scss', }) export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { @@ -82,7 +91,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { optionName: AddonMessagesGroupConversationOptionNames.GROUP, titleString: 'addon.messages.groupconversations', emptyString: 'addon.messages.nogroupconversations', - type: AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_GROUP, + type: AddonMessagesMessageConversationType.GROUP, favourites: false, count: 0, unread: 0, @@ -92,7 +101,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { optionName: AddonMessagesGroupConversationOptionNames.INDIVIDUAL, titleString: 'addon.messages.individualconversations', emptyString: 'addon.messages.noindividualconversations', - type: AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, + type: AddonMessagesMessageConversationType.INDIVIDUAL, favourites: false, count: 0, unread: 0, @@ -100,7 +109,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { }, ]; - typeGroup = AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_GROUP; + typeGroup = AddonMessagesMessageConversationType.GROUP; protected siteId: string; protected currentUserId: number; @@ -124,7 +133,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { // Update conversations when new message is received. this.newMessagesObserver = CoreEvents.on( - AddonMessagesProvider.NEW_MESSAGE_EVENT, + ADDON_MESSAGES_NEW_MESSAGE_EVENT, (data) => { // Check if the new message belongs to the option that is currently expanded. const expandedOption = this.getExpandedOption(); @@ -163,7 +172,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { conversation.lastmessagedate = data.timecreated / 1000; if (data.userFrom) { conversation.sentfromcurrentuser = data.userFrom.id === this.currentUserId; - if (conversation.type === AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_GROUP) { + if (conversation.type === AddonMessagesMessageConversationType.GROUP) { conversation.members[0] = data.userFrom; } } @@ -183,7 +192,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { ); // Update conversations when a message is read. - this.readChangedObserver = CoreEvents.on(AddonMessagesProvider.READ_CHANGED_EVENT, (data) => { + this.readChangedObserver = CoreEvents.on(ADDON_MESSAGES_READ_CHANGED_EVENT, (data) => { if (data.conversationId) { const conversation = this.findConversation(data.conversationId); @@ -200,7 +209,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { // Load a discussion if we receive an event to do so. this.openConversationObserver = CoreEvents.on( - AddonMessagesProvider.OPEN_CONVERSATION_EVENT, + ADDON_MESSAGES_OPEN_CONVERSATION_EVENT, (data) => { if (data.conversationId || data.userId) { this.gotoConversation(data.conversationId, data.userId); @@ -222,7 +231,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { // Update conversations if we receive an event to do so. this.updateConversationListObserver = CoreEvents.on( - AddonMessagesProvider.UPDATE_CONVERSATION_LIST_EVENT, + ADDON_MESSAGES_UPDATE_CONVERSATION_LIST_EVENT, (data) => { if (data?.action === AddonMessagesUpdateConversationAction.MUTE) { // If the conversation is displayed, change its muted value. @@ -256,7 +265,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { // Update unread conversation counts. this.cronObserver = CoreEvents.on( - AddonMessagesProvider.UNREAD_CONVERSATION_COUNTS_EVENT, + ADDON_MESSAGES_UNREAD_CONVERSATION_COUNTS_EVENT, (data) => { this.setCounts(data, 'unread'); }, @@ -265,7 +274,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { // Update the contact requests badge. this.contactRequestsCountObserver = CoreEvents.on( - AddonMessagesProvider.CONTACT_REQUESTS_COUNT_EVENT, + ADDON_MESSAGES_CONTACT_REQUESTS_COUNT_EVENT, (data) => { this.contactRequestsCount = data.count; }, @@ -274,7 +283,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { // Update block status of a user. this.memberInfoObserver = CoreEvents.on( - AddonMessagesProvider.MEMBER_INFO_CHANGED_EVENT, + ADDON_MESSAGES_MEMBER_INFO_CHANGED_EVENT, (data) => { if (!data.userBlocked && !data.userUnblocked) { // The block status has not changed, ignore. @@ -638,7 +647,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { // Conversation not found, it could be an old one or the message could belong to another option. conversation = { id: message.conversationid, - type: message.conversation?.type || AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, + type: message.conversation?.type || AddonMessagesMessageConversationType.INDIVIDUAL, membercount: message.conversation?.membercount || 0, ismuted: message.conversation?.ismuted || false, isfavourite: message.conversation?.isfavourite || false, @@ -657,7 +666,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { this.addOfflineConversation(conversation, option); } } - } else if (option.type === AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_INDIVIDUAL) { + } else if (option.type === AddonMessagesMessageConversationType.INDIVIDUAL) { // It's a new conversation. Check if we already created it (there is more than one message for the same user). const conversation = this.findConversation(undefined, message.touserid, option); @@ -675,7 +684,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { }).then((user) => { const conversation: AddonMessagesConversationForList = { id: 0, - type: AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, + type: AddonMessagesMessageConversationType.INDIVIDUAL, membercount: 0, // Faked data. ismuted: false, // Faked data. isfavourite: false, // Faked data. @@ -742,7 +751,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { return AddonMessagesGroupConversationOptionNames.FAVOURITES; } - if (conversation.type === AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_GROUP) { + if (conversation.type === AddonMessagesMessageConversationType.GROUP) { return AddonMessagesGroupConversationOptionNames.GROUP; } diff --git a/src/addons/messages/pages/search/search.ts b/src/addons/messages/pages/search/search.ts index 501d50ca846..81d6af6aaff 100644 --- a/src/addons/messages/pages/search/search.ts +++ b/src/addons/messages/pages/search/search.ts @@ -16,7 +16,6 @@ import { Component, OnDestroy, ViewChild } from '@angular/core'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreSites } from '@services/sites'; import { - AddonMessagesProvider, AddonMessagesConversationMember, AddonMessagesMessageAreaContact, AddonMessages, @@ -26,6 +25,11 @@ import { CoreNavigator } from '@services/navigator'; import { CoreScreen } from '@services/screen'; import { CoreSplitViewComponent } from '@components/split-view/split-view'; import { CoreKeyboard } from '@singletons/keyboard'; +import { + ADDON_MESSAGES_MEMBER_INFO_CHANGED_EVENT, + ADDON_MESSAGES_LIMIT_SEARCH, + ADDON_MESSAGES_LIMIT_INITIAL_USER_SEARCH, +} from '@addons/messages/constants'; /** * Page for searching users. @@ -74,7 +78,7 @@ export class AddonMessagesSearchPage implements OnDestroy { constructor() { // Update block status of a user. this.memberInfoObserver = CoreEvents.on( - AddonMessagesProvider.MEMBER_INFO_CHANGED_EVENT, + ADDON_MESSAGES_MEMBER_INFO_CHANGED_EVENT, (data) => { if (!data.userBlocked && !data.userUnblocked) { // The block status has not changed, ignore. @@ -139,7 +143,7 @@ export class AddonMessagesSearchPage implements OnDestroy { let canLoadMoreMessages = false; if (!loadMore || loadMore == 'contacts' || loadMore == 'noncontacts') { - const limitNum = loadMore ? AddonMessagesProvider.LIMIT_SEARCH : AddonMessagesProvider.LIMIT_INITIAL_USER_SEARCH; + const limitNum = loadMore ? ADDON_MESSAGES_LIMIT_SEARCH : ADDON_MESSAGES_LIMIT_INITIAL_USER_SEARCH; let limitFrom = 0; if (loadMore == 'contacts') { limitFrom = this.contacts.results.length; diff --git a/src/addons/messages/pages/settings/settings.ts b/src/addons/messages/pages/settings/settings.ts index 9fcb9ede739..1d8142ffb29 100644 --- a/src/addons/messages/pages/settings/settings.ts +++ b/src/addons/messages/pages/settings/settings.ts @@ -14,7 +14,7 @@ import { Component, OnDestroy, OnInit, signal } from '@angular/core'; import { - AddonMessagesProvider, AddonMessagesMessagePreferences, + AddonMessagesMessagePreferences, AddonMessagesMessagePreferencesNotification, AddonMessagesMessagePreferencesNotificationProcessor, AddonMessages, @@ -29,6 +29,7 @@ import { AddonNotificationsPreferencesNotificationProcessorState } from '@addons import { CorePlatform } from '@services/platform'; import { CoreErrorHelper } from '@services/error-helper'; import { CoreLoadings } from '@services/loadings'; +import { ADDON_MESSAGES_NOTIFICATION_PREFERENCES_KEY, AddonMessagesMessagePrivacy } from '@addons/messages/constants'; /** * Page that displays the messages settings page. @@ -46,9 +47,9 @@ export class AddonMessagesSettingsPage implements OnInit, OnDestroy { contactablePrivacy?: number | boolean; advancedContactable = false; // Whether the site supports "advanced" contactable privacy. allowSiteMessaging = false; - onlyContactsValue = AddonMessagesProvider.MESSAGE_PRIVACY_ONLYCONTACTS; - courseMemberValue = AddonMessagesProvider.MESSAGE_PRIVACY_COURSEMEMBER; - siteValue = AddonMessagesProvider.MESSAGE_PRIVACY_SITE; + onlyContactsValue = AddonMessagesMessagePrivacy.ONLYCONTACTS; + courseMemberValue = AddonMessagesMessagePrivacy.COURSEMEMBER; + siteValue = AddonMessagesMessagePrivacy.SITE; groupMessagingEnabled = false; sendOnEnter = false; warningMessage = signal(undefined); @@ -91,7 +92,7 @@ export class AddonMessagesSettingsPage implements OnInit, OnDestroy { for (const component of preferences.components) { // Only display get the notification preferences. component.notifications = component.notifications.filter((notification) => - notification.preferencekey == AddonMessagesProvider.NOTIFICATION_PREFERENCES_KEY); + notification.preferencekey === ADDON_MESSAGES_NOTIFICATION_PREFERENCES_KEY); if (this.loggedInOffLegacyMode) { // Load enabled from loggedin / loggedoff values. diff --git a/src/addons/messages/services/handlers/mainmenu.ts b/src/addons/messages/services/handlers/mainmenu.ts index fe778d9ed9d..c7a08eedd35 100644 --- a/src/addons/messages/services/handlers/mainmenu.ts +++ b/src/addons/messages/services/handlers/mainmenu.ts @@ -14,20 +14,23 @@ import { Injectable } from '@angular/core'; import { - AddonMessagesProvider, AddonMessages, } from '../messages'; import { CoreMainMenuHandler, CoreMainMenuHandlerToDisplay } from '@features/mainmenu/services/mainmenu-delegate'; import { CoreCronHandler } from '@services/cron'; import { CoreSites } from '@services/sites'; import { CoreEvents } from '@singletons/events'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { CorePushNotificationsNotificationBasicData, } from '@features/pushnotifications/services/pushnotifications'; import { CorePushNotificationsDelegate } from '@features/pushnotifications/services/push-delegate'; import { makeSingleton } from '@singletons'; -import { CoreMainMenuProvider } from '@features/mainmenu/services/mainmenu'; +import { + ADDON_MESSAGES_UNREAD_CONVERSATION_COUNTS_EVENT, + ADDON_MESSAGES_CONTACT_REQUESTS_COUNT_EVENT, +} from '@addons/messages/constants'; +import { MAIN_MENU_HANDLER_BADGE_UPDATED_EVENT } from '@features/mainmenu/constants'; /** * Handler to inject an option into main menu. @@ -58,14 +61,14 @@ export class AddonMessagesMainMenuHandlerService implements CoreMainMenuHandler, constructor() { - CoreEvents.on(AddonMessagesProvider.UNREAD_CONVERSATION_COUNTS_EVENT, (data) => { + CoreEvents.on(ADDON_MESSAGES_UNREAD_CONVERSATION_COUNTS_EVENT, (data) => { this.unreadCount = data.favourites + data.individual + data.group + data.self; this.orMore = !!data.orMore; data.siteId && this.updateBadge(data.siteId); }); - CoreEvents.on(AddonMessagesProvider.CONTACT_REQUESTS_COUNT_EVENT, (data) => { + CoreEvents.on(ADDON_MESSAGES_CONTACT_REQUESTS_COUNT_EVENT, (data) => { this.contactRequestsCount = data.count; data.siteId && this.updateBadge(data.siteId); @@ -174,7 +177,7 @@ export class AddonMessagesMainMenuHandlerService implements CoreMainMenuHandler, // Update push notifications badge. CoreEvents.trigger( - CoreMainMenuProvider.MAIN_MENU_HANDLER_BADGE_UPDATED, + MAIN_MENU_HANDLER_BADGE_UPDATED_EVENT, { handler: AddonMessagesMainMenuHandlerService.name, value: totalCount, diff --git a/src/addons/messages/services/handlers/push-click.ts b/src/addons/messages/services/handlers/push-click.ts index dd20e1e3d90..c915b88c064 100644 --- a/src/addons/messages/services/handlers/push-click.ts +++ b/src/addons/messages/services/handlers/push-click.ts @@ -16,7 +16,7 @@ import { Injectable } from '@angular/core'; import { CorePushNotificationsClickHandler } from '@features/pushnotifications/services/push-delegate'; import { CorePushNotificationsNotificationBasicData } from '@features/pushnotifications/services/pushnotifications'; import { CoreNavigator } from '@services/navigator'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { makeSingleton } from '@singletons'; import { AddonMessages } from '../messages'; import { AddonMessagesMainMenuHandlerService } from './mainmenu'; diff --git a/src/addons/messages/services/messages-sync.ts b/src/addons/messages/services/messages-sync.ts index c4c415d4c69..dd0c0991aa0 100644 --- a/src/addons/messages/services/messages-sync.ts +++ b/src/addons/messages/services/messages-sync.ts @@ -18,12 +18,11 @@ import { AddonMessagesOffline, AddonMessagesOfflineAnyMessagesFormatted, } from './messages-offline'; import { - AddonMessagesProvider, AddonMessages, AddonMessagesGetMessagesWSParams, } from './messages'; import { CoreEvents } from '@singletons/events'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreWSError } from '@classes/errors/wserror'; import { makeSingleton, Translate } from '@singletons'; import { CoreSites } from '@services/sites'; import { CoreNetwork } from '@services/network'; @@ -33,6 +32,20 @@ import { CoreError } from '@classes/errors/error'; import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site'; import { CoreWait } from '@singletons/wait'; import { CoreErrorHelper, CoreErrorObject } from '@services/error-helper'; +import { ADDON_MESSAGES_AUTO_SYNCED, ADDON_MESSAGES_LIMIT_MESSAGES } from '../constants'; + +declare module '@singletons/events' { + + /** + * Augment CoreEventsData interface with events specific to this service. + * + * @see https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation + */ + export interface CoreEventsData { + [ADDON_MESSAGES_AUTO_SYNCED]: AddonMessagesSyncEvents; + } + +} /** * Service to sync messages. @@ -40,8 +53,6 @@ import { CoreErrorHelper, CoreErrorObject } from '@services/error-helper'; @Injectable({ providedIn: 'root' }) export class AddonMessagesSyncProvider extends CoreSyncBaseProvider { - static readonly AUTO_SYNCED = 'addon_messages_autom_synced'; - constructor() { super('AddonMessagesSync'); } @@ -113,7 +124,7 @@ export class AddonMessagesSyncProvider extends CoreSyncBaseProvider { const data: AddonMessagesMemberInfoChangedEventData = { userId, contactRequestConfirmed: true }; - CoreEvents.trigger(AddonMessagesProvider.MEMBER_INFO_CHANGED_EVENT, data, site.id); + CoreEvents.trigger(ADDON_MESSAGES_MEMBER_INFO_CHANGED_EVENT, data, site.id); }); } @@ -212,7 +283,7 @@ export class AddonMessagesProvider { await this.invalidateAllMemberInfo(userId, site).finally(() => { const data: AddonMessagesMemberInfoChangedEventData = { userId, contactRequestCreated: true }; - CoreEvents.trigger(AddonMessagesProvider.MEMBER_INFO_CHANGED_EVENT, data, site.id); + CoreEvents.trigger(ADDON_MESSAGES_MEMBER_INFO_CHANGED_EVENT, data, site.id); }); } @@ -234,12 +305,12 @@ export class AddonMessagesProvider { await site.write('core_message_decline_contact_request', params); - await CoreUtils.allPromises([ + await CorePromiseUtils.allPromises([ this.invalidateAllMemberInfo(userId, site), this.refreshContactRequestsCount(site.id), ]).finally(() => { const data: AddonMessagesMemberInfoChangedEventData = { userId, contactRequestDeclined: true }; - CoreEvents.trigger(AddonMessagesProvider.MEMBER_INFO_CHANGED_EVENT, data, site.id); + CoreEvents.trigger(ADDON_MESSAGES_MEMBER_INFO_CHANGED_EVENT, data, site.id); }); } @@ -373,8 +444,8 @@ export class AddonMessagesProvider { conversation.lastmessagedate = lastMessage ? lastMessage.timecreated : undefined; conversation.sentfromcurrentuser = lastMessage ? lastMessage.useridfrom == userId : undefined; - if (conversation.type != AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_GROUP) { - const isIndividual = conversation.type == AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_INDIVIDUAL; + if (conversation.type != AddonMessagesMessageConversationType.GROUP) { + const isIndividual = conversation.type == AddonMessagesMessageConversationType.INDIVIDUAL; const otherUser = conversation.members.find((member) => (isIndividual && member.id != userId) || (!isIndividual && member.id == userId)); @@ -628,7 +699,7 @@ export class AddonMessagesProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getCacheKeyForBlockedContacts(userId), - updateFrequency: CoreSite.FREQUENCY_OFTEN, + updateFrequency: CoreCacheUpdateFrequency.OFTEN, }; return site.read('core_message_get_blocked_users', params, preSets); @@ -648,7 +719,7 @@ export class AddonMessagesProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getCacheKeyForContacts(), - updateFrequency: CoreSite.FREQUENCY_OFTEN, + updateFrequency: CoreCacheUpdateFrequency.OFTEN, }; const contacts = await site.read('core_message_get_contacts', undefined, preSets); @@ -679,14 +750,14 @@ export class AddonMessagesProvider { * Get the list of user contacts. * * @param limitFrom Position of the first contact to fetch. - * @param limitNum Number of contacts to fetch. Default is AddonMessagesProvider.LIMIT_CONTACTS. + * @param limitNum Number of contacts to fetch. Default is ADDON_MESSAGES_LIMIT_CONTACTS. * @param siteId Site ID. If not defined, use current site. * @returns Promise resolved with the list of user contacts. * @since 3.6 */ async getUserContacts( limitFrom: number = 0, - limitNum: number = AddonMessagesProvider.LIMIT_CONTACTS, + limitNum: number = ADDON_MESSAGES_LIMIT_CONTACTS, siteId?: string, ): Promise<{contacts: AddonMessagesConversationMember[]; canLoadMore: boolean}> { const site = await CoreSites.getSite(siteId); @@ -699,7 +770,7 @@ export class AddonMessagesProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getCacheKeyForUserContacts(), - updateFrequency: CoreSite.FREQUENCY_OFTEN, + updateFrequency: CoreCacheUpdateFrequency.OFTEN, }; const contacts = await site.read('core_message_get_user_contacts', params, preSets); @@ -723,14 +794,14 @@ export class AddonMessagesProvider { * Get the contact request sent to the current user. * * @param limitFrom Position of the first contact request to fetch. - * @param limitNum Number of contact requests to fetch. Default is AddonMessagesProvider.LIMIT_CONTACTS. + * @param limitNum Number of contact requests to fetch. Default is ADDON_MESSAGES_LIMIT_CONTACTS. * @param siteId Site ID. If not defined, use current site. * @returns Promise resolved with the list of contact requests. * @since 3.6 */ async getContactRequests( limitFrom: number = 0, - limitNum: number = AddonMessagesProvider.LIMIT_CONTACTS, + limitNum: number = ADDON_MESSAGES_LIMIT_CONTACTS, siteId?: string, ): Promise<{requests: AddonMessagesConversationMember[]; canLoadMore: boolean}> { const site = await CoreSites.getSite(siteId); @@ -743,7 +814,7 @@ export class AddonMessagesProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getCacheKeyForContactRequests(), - updateFrequency: CoreSite.FREQUENCY_OFTEN, + updateFrequency: CoreCacheUpdateFrequency.OFTEN, }; const requests = await site.read( @@ -791,7 +862,7 @@ export class AddonMessagesProvider { }; // Notify the new count so all badges are updated. - CoreEvents.trigger(AddonMessagesProvider.CONTACT_REQUESTS_COUNT_EVENT, data , site.id); + CoreEvents.trigger(ADDON_MESSAGES_CONTACT_REQUESTS_COUNT_EVENT, data , site.id); return data.count; @@ -935,11 +1006,11 @@ export class AddonMessagesProvider { ): Promise<{members: AddonMessagesConversationMember[]; canLoadMore: boolean}> { const site = await CoreSites.getSite(siteId); userId = userId || site.getUserId(); - limitTo = limitTo ?? AddonMessagesProvider.LIMIT_MESSAGES; + limitTo = limitTo ?? ADDON_MESSAGES_LIMIT_MESSAGES; const preSets: CoreSiteWSPreSets = { cacheKey: this.getCacheKeyForConversationMembers(userId, conversationId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + updateFrequency: CoreCacheUpdateFrequency.SOMETIMES, }; const params: AddonMessagesGetConversationMembersWSParams = { @@ -983,7 +1054,7 @@ export class AddonMessagesProvider { options.userId = options.userId || site.getUserId(); options.limitFrom = options.limitFrom || 0; - options.limitTo = options.limitTo ?? AddonMessagesProvider.LIMIT_MESSAGES; + options.limitTo = options.limitTo ?? ADDON_MESSAGES_LIMIT_MESSAGES; options.timeFrom = options.timeFrom || 0; options.newestFirst = options.newestFirst ?? true; @@ -1076,7 +1147,7 @@ export class AddonMessagesProvider { const params: AddonMessagesGetConversationsWSParams = { userid: userId, limitfrom: limitFrom, - limitnum: AddonMessagesProvider.LIMIT_MESSAGES + 1, + limitnum: ADDON_MESSAGES_LIMIT_MESSAGES + 1, }; if (forceCache) { @@ -1091,7 +1162,7 @@ export class AddonMessagesProvider { if (favourites !== undefined && favourites != null) { params.favourites = !!favourites; } - if (site.isVersionGreaterEqualThan('3.7') && type != AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_GROUP) { + if (site.isVersionGreaterEqualThan('3.7') && type != AddonMessagesMessageConversationType.GROUP) { // Add self conversation to the list. params.mergeself = true; } @@ -1112,12 +1183,12 @@ export class AddonMessagesProvider { // Format the conversations, adding some calculated fields. const conversations = response.conversations - .slice(0, AddonMessagesProvider.LIMIT_MESSAGES) + .slice(0, ADDON_MESSAGES_LIMIT_MESSAGES) .map((conversation) => this.formatConversation(conversation, userId!)); return { conversations, - canLoadMore: response.conversations.length > AddonMessagesProvider.LIMIT_MESSAGES, + canLoadMore: response.conversations.length > ADDON_MESSAGES_LIMIT_MESSAGES, }; } @@ -1144,9 +1215,9 @@ export class AddonMessagesProvider { const counts = { favourites: result.favourites, - individual: result.types[AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_INDIVIDUAL], - group: result.types[AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_GROUP], - self: result.types[AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_SELF] || 0, + individual: result.types[AddonMessagesMessageConversationType.INDIVIDUAL], + group: result.types[AddonMessagesMessageConversationType.GROUP], + self: result.types[AddonMessagesMessageConversationType.SELF] || 0, }; return counts; @@ -1189,7 +1260,7 @@ export class AddonMessagesProvider { const params: AddonMessagesGetMessagesWSParams = { useridto: site.getUserId(), useridfrom: userId, - limitnum: AddonMessagesProvider.LIMIT_MESSAGES, + limitnum: ADDON_MESSAGES_LIMIT_MESSAGES, }; if (lfReceivedUnread > 0 || lfReceivedRead > 0 || lfSentUnread > 0 || lfSentRead > 0) { @@ -1212,13 +1283,13 @@ export class AddonMessagesProvider { result.messages = result.messages.concat(sent); const hasSent = sent.length > 0; - if (result.messages.length > AddonMessagesProvider.LIMIT_MESSAGES) { + if (result.messages.length > ADDON_MESSAGES_LIMIT_MESSAGES) { // Sort messages and get the more recent ones. result.canLoadMore = true; result.messages = this.sortMessages(result['messages']); - result.messages = result.messages.slice(-AddonMessagesProvider.LIMIT_MESSAGES); + result.messages = result.messages.slice(-ADDON_MESSAGES_LIMIT_MESSAGES); } else { - result.canLoadMore = result.messages.length == AddonMessagesProvider.LIMIT_MESSAGES && (!hasReceived || !hasSent); + result.canLoadMore = result.messages.length == ADDON_MESSAGES_LIMIT_MESSAGES && (!hasReceived || !hasSent); } if (excludePending) { @@ -1289,7 +1360,7 @@ export class AddonMessagesProvider { const params: AddonMessagesGetMessagesWSParams = { useridto: currentUserId, useridfrom: 0, - limitnum: AddonMessagesProvider.LIMIT_MESSAGES, + limitnum: ADDON_MESSAGES_LIMIT_MESSAGES, }; const preSets: CoreSiteWSPreSets = { @@ -1371,7 +1442,7 @@ export class AddonMessagesProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getCacheKeyForMemberInfo(userId, otherUserId), - updateFrequency: CoreSite.FREQUENCY_OFTEN, + updateFrequency: CoreCacheUpdateFrequency.OFTEN, }; const params: AddonMessagesGetMemberInfoWSParams = { referenceuserid: userId, @@ -1410,7 +1481,7 @@ export class AddonMessagesProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getMessagePreferencesCacheKey(), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + updateFrequency: CoreCacheUpdateFrequency.SOMETIMES, }; const data = await site.read( @@ -1572,9 +1643,9 @@ export class AddonMessagesProvider { counts = { favourites: result.favourites, - individual: result.types[AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_INDIVIDUAL], - group: result.types[AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_GROUP], - self: result.types[AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_SELF] || 0, + individual: result.types[AddonMessagesMessageConversationType.INDIVIDUAL], + group: result.types[AddonMessagesMessageConversationType.GROUP], + self: result.types[AddonMessagesMessageConversationType.SELF] || 0, }; } else { @@ -1592,7 +1663,7 @@ export class AddonMessagesProvider { } // Notify the new counts so all views are updated. - CoreEvents.trigger(AddonMessagesProvider.UNREAD_CONVERSATION_COUNTS_EVENT, counts, site.id); + CoreEvents.trigger(ADDON_MESSAGES_UNREAD_CONVERSATION_COUNTS_EVENT, counts, site.id); return counts; } @@ -1617,7 +1688,7 @@ export class AddonMessagesProvider { const params: AddonMessagesGetMessagesWSParams = { read: false, limitfrom: 0, - limitnum: AddonMessagesProvider.LIMIT_MESSAGES, + limitnum: ADDON_MESSAGES_LIMIT_MESSAGES, useridto: site.getUserId(), useridfrom: 0, }; @@ -1863,7 +1934,7 @@ export class AddonMessagesProvider { * @returns Promise resolved when done. */ protected async invalidateAllMemberInfo(userId: number, site: CoreSite): Promise { - await CoreUtils.allPromises([ + await CorePromiseUtils.allPromises([ this.invalidateMemberInfo(userId, site.id), this.invalidateUserContacts(site.id), this.invalidateBlockedContactsCache(site.id), @@ -1881,7 +1952,7 @@ export class AddonMessagesProvider { site.id, undefined, true, - ).then((conversation) => CoreUtils.allPromises([ + ).then((conversation) => CorePromiseUtils.allPromises([ this.invalidateConversation(conversation.id), this.invalidateConversationMembers(conversation.id, site.id), ])).catch(() => { @@ -2243,13 +2314,13 @@ export class AddonMessagesProvider { await site.write('core_message_delete_contacts', params, preSets); - return CoreUtils.allPromises([ + return CorePromiseUtils.allPromises([ this.invalidateUserContacts(site.id), this.invalidateAllMemberInfo(userId, site), this.invalidateContactsCache(site.id), ]).then(() => { const data: AddonMessagesMemberInfoChangedEventData = { userId, contactRemoved: true }; - CoreEvents.trigger(AddonMessagesProvider.MEMBER_INFO_CHANGED_EVENT, data, site.id); + CoreEvents.trigger(ADDON_MESSAGES_MEMBER_INFO_CHANGED_EVENT, data, site.id); return; }); @@ -2296,7 +2367,7 @@ export class AddonMessagesProvider { * @param query The query string. * @param userId The user ID. If not defined, current user. * @param limitFrom Position of the first result to get. Defaults to 0. - * @param limitNum Number of results to get. Defaults to AddonMessagesProvider.LIMIT_SEARCH. + * @param limitNum Number of results to get. Defaults to ADDON_MESSAGES_LIMIT_SEARCH. * @param siteId Site ID. If not defined, current site. * @returns Promise resolved with the results. */ @@ -2304,7 +2375,7 @@ export class AddonMessagesProvider { query: string, userId?: number, limitFrom: number = 0, - limitNum: number = AddonMessagesProvider.LIMIT_SEARCH, + limitNum: number = ADDON_MESSAGES_LIMIT_SEARCH, siteId?: string, ): Promise<{messages: AddonMessagesMessageAreaContact[]; canLoadMore: boolean}> { const site = await CoreSites.getSite(siteId); @@ -2349,7 +2420,7 @@ export class AddonMessagesProvider { * * @param query Text to search for. * @param limitFrom Position of the first found user to fetch. - * @param limitNum Number of found users to fetch. Defaults to AddonMessagesProvider.LIMIT_SEARCH. + * @param limitNum Number of found users to fetch. Defaults to ADDON_MESSAGES_LIMIT_SEARCH. * @param siteId Site ID. If not defined, use current site. * @returns Resolved with two lists of found users: contacts and non-contacts. * @since 3.6 @@ -2357,7 +2428,7 @@ export class AddonMessagesProvider { async searchUsers( query: string, limitFrom: number = 0, - limitNum: number = AddonMessagesProvider.LIMIT_SEARCH, + limitNum: number = ADDON_MESSAGES_LIMIT_SEARCH, siteId?: string, ): Promise<{ contacts: AddonMessagesConversationMember[]; @@ -2459,7 +2530,7 @@ export class AddonMessagesProvider { message: result, }; } catch (error) { - if (CoreUtils.isWebServiceError(error)) { + if (CoreWSError.isWebServiceError(error)) { // It's a WebService error, the user cannot send the message so don't store it. throw error; } @@ -2590,7 +2661,7 @@ export class AddonMessagesProvider { message: result, }; } catch (error) { - if (CoreUtils.isWebServiceError(error)) { + if (CoreWSError.isWebServiceError(error)) { // It's a WebService error, the user cannot send the message so don't store it. throw error; } @@ -2827,7 +2898,7 @@ export class AddonMessagesProvider { } finally { const data: AddonMessagesMemberInfoChangedEventData = { userId, userUnblocked: true }; - CoreEvents.trigger(AddonMessagesProvider.MEMBER_INFO_CHANGED_EVENT, data, site.id); + CoreEvents.trigger(ADDON_MESSAGES_MEMBER_INFO_CHANGED_EVENT, data, site.id); } } diff --git a/src/addons/mod/assign/classes/submissions-source.ts b/src/addons/mod/assign/classes/submissions-source.ts index d630dbc3223..aa7f320e102 100644 --- a/src/addons/mod/assign/classes/submissions-source.ts +++ b/src/addons/mod/assign/classes/submissions-source.ts @@ -16,7 +16,7 @@ import { Params } from '@angular/router'; import { CoreRoutedItemsManagerSource } from '@classes/items-management/routed-items-manager-source'; import { CoreGroupInfo, CoreGroups } from '@services/groups'; import { CoreSites } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { Translate } from '@singletons'; import { CoreIonicColorNames } from '@singletons/colors'; import { CoreEvents } from '@singletons/events'; @@ -194,7 +194,7 @@ export class AddonModAssignSubmissionsSource extends CoreRoutedItemsManagerSourc const showSubmissions: AddonModAssignSubmissionForList[] = await Promise.all( submissions.map(async (submission: AddonModAssignSubmissionForList) => { const gradeData = - await CoreUtils.ignoreErrors(AddonModAssignOffline.getSubmissionGrade(assign.id, submission.userid)); + await CorePromiseUtils.ignoreErrors(AddonModAssignOffline.getSubmissionGrade(assign.id, submission.userid)); // Load offline grades. const notSynced = !!gradeData && submission.timemodified < gradeData.timemodified; diff --git a/src/addons/mod/assign/components/edit-feedback-modal/edit-feedback-modal.ts b/src/addons/mod/assign/components/edit-feedback-modal/edit-feedback-modal.ts index aafd35365b7..9f05e1b043f 100644 --- a/src/addons/mod/assign/components/edit-feedback-modal/edit-feedback-modal.ts +++ b/src/addons/mod/assign/components/edit-feedback-modal/edit-feedback-modal.ts @@ -16,7 +16,7 @@ import { Component, Input, ViewChild, ElementRef } from '@angular/core'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreFormFields, CoreForms } from '@singletons/form'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { ModalController, Translate } from '@singletons'; import { AddonModAssignAssign, AddonModAssignPlugin, AddonModAssignSubmission } from '../../services/assign'; import { AddonModAssignFeedbackDelegate } from '../../services/feedback-delegate'; @@ -88,7 +88,7 @@ export class AddonModAssignEditFeedbackModalComponent { * @returns Promise resolved with boolean: whether the data has changed. */ protected async hasDataChanged(): Promise { - const changed = await CoreUtils.ignoreErrors( + const changed = await CorePromiseUtils.ignoreErrors( AddonModAssignFeedbackDelegate.hasPluginDataChanged( this.assign, this.submission, diff --git a/src/addons/mod/assign/components/index/index.ts b/src/addons/mod/assign/components/index/index.ts index eb31d7487f4..0913fd8af97 100644 --- a/src/addons/mod/assign/components/index/index.ts +++ b/src/addons/mod/assign/components/index/index.ts @@ -24,7 +24,7 @@ import { CoreNavigator } from '@services/navigator'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreTimeUtils } from '@services/utils/time'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { Translate } from '@singletons'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreTime } from '@singletons/time'; @@ -182,7 +182,7 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo if (sync) { // Try to synchronize the assign. - await CoreUtils.ignoreErrors(this.syncActivity(showErrors)); + await CorePromiseUtils.ignoreErrors(this.syncActivity(showErrors)); } // Check if there's any offline data for this assign. @@ -250,18 +250,18 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo return; // Shouldn't happen. } - await CoreUtils.ignoreErrors(AddonModAssign.logView(this.assign.id)); + await CorePromiseUtils.ignoreErrors(AddonModAssign.logView(this.assign.id)); this.analyticsLogEvent('mod_assign_view_assign'); if (this.canViewAllSubmissions) { // User can see all submissions, log grading view. - await CoreUtils.ignoreErrors(AddonModAssign.logGradingView(this.assign.id)); + await CorePromiseUtils.ignoreErrors(AddonModAssign.logGradingView(this.assign.id)); this.analyticsLogEvent('mod_assign_view_grading_table', { sendUrl: false }); } else if (this.canViewOwnSubmission) { // User can only see their own submission, log view the user submission. - await CoreUtils.ignoreErrors(AddonModAssign.logSubmissionView(this.assign.id)); + await CorePromiseUtils.ignoreErrors(AddonModAssign.logSubmissionView(this.assign.id)); this.analyticsLogEvent('mod_assign_view_submission_status', { sendUrl: false }); } diff --git a/src/addons/mod/assign/components/submission/submission.ts b/src/addons/mod/assign/components/submission/submission.ts index fd68f8c1b44..ae4849e103f 100644 --- a/src/addons/mod/assign/components/submission/submission.ts +++ b/src/addons/mod/assign/components/submission/submission.ts @@ -37,7 +37,7 @@ import { CoreTabsComponent } from '@components/tabs/tabs'; import { CoreTabComponent } from '@components/tabs/tab'; import { CoreSplitViewComponent } from '@components/split-view/split-view'; import { CoreGradesFormattedItem, CoreGradesHelper } from '@features/grades/services/grades-helper'; -import { CoreMenuItem, CoreUtils } from '@services/utils/utils'; +import { CoreMenuItem, CoreUtils } from '@singletons/utils'; import { AddonModAssignHelper, AddonModAssignSubmissionFormatted } from '../../services/assign-helper'; import { CoreDomUtils } from '@services/utils/dom'; import { Translate } from '@singletons'; @@ -70,6 +70,7 @@ import { } from '../../constants'; import { CoreViewer } from '@features/viewer/services/viewer'; import { CoreLoadings } from '@services/loadings'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Component that displays an assignment submission. @@ -77,7 +78,7 @@ import { CoreLoadings } from '@services/loadings'; @Component({ selector: 'addon-mod-assign-submission', templateUrl: 'addon-mod-assign-submission.html', - styleUrls: ['submission.scss'], + styleUrl: 'submission.scss', }) export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, CanLeave { @@ -327,7 +328,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can const previousSubmission = this.previousAttempt.submission; let modal = await CoreLoadings.show(); - const size = await CoreUtils.ignoreErrors( + const size = await CorePromiseUtils.ignoreErrors( AddonModAssignHelper.getSubmissionSizeForCopy(this.assign, previousSubmission), -1, ); // Error calculating size, return -1. @@ -548,7 +549,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can }); } - await CoreUtils.ignoreErrors(Promise.all(promises)); + await CorePromiseUtils.ignoreErrors(Promise.all(promises)); await this.loadData(sync); } @@ -716,7 +717,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can // If we have data about the grader, get its profile. if (feedback.grade && feedback.grade.grader > 0) { - this.grader = await CoreUtils.ignoreErrors(CoreUser.getProfile(feedback.grade.grader, this.courseId)); + this.grader = await CorePromiseUtils.ignoreErrors(CoreUser.getProfile(feedback.grade.grader, this.courseId)); } else { delete this.grader; } @@ -803,7 +804,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can // Submission grades aren't identified by attempt number so it can retrieve the feedback for a previous attempt. // The app will not treat that as an special case. - const submissionGrade = await CoreUtils.ignoreErrors( + const submissionGrade = await CorePromiseUtils.ignoreErrors( AddonModAssignOffline.getSubmissionGrade(assign.id, this.submitId), ); @@ -1075,7 +1076,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can const [gradebookGrades, assignGrades] = await Promise.all([ CoreGradesHelper.getGradeModuleItems(this.courseId, this.moduleId, this.submitId), gradeNotReleased ? - CoreUtils.ignoreErrors(AddonModAssign.getAssignmentGrades(assign.id, { cmId: assign.cmid })) : + CorePromiseUtils.ignoreErrors(AddonModAssign.getAssignmentGrades(assign.id, { cmId: assign.cmid })) : undefined, ]); diff --git a/src/addons/mod/assign/feedback/comments/component/comments.ts b/src/addons/mod/assign/feedback/comments/component/comments.ts index 0513d89ef13..686166d7a14 100644 --- a/src/addons/mod/assign/feedback/comments/component/comments.ts +++ b/src/addons/mod/assign/feedback/comments/component/comments.ts @@ -23,7 +23,7 @@ import { } from '../services/handler'; import { AddonModAssignFeedbackDelegate } from '@addons/mod/assign/services/feedback-delegate'; import { AddonModAssignOffline } from '@addons/mod/assign/services/assign-offline'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { AddonModAssignFeedbackPluginBaseComponent } from '@addons/mod/assign/classes/base-feedback-plugin-component'; import { ContextLevel } from '@/core/constants'; import { ADDON_MOD_ASSIGN_COMPONENT } from '@addons/mod/assign/constants'; @@ -124,7 +124,7 @@ export class AddonModAssignFeedbackCommentsComponent extends AddonModAssignFeedb } // There is no draft saved. Check if we have anything offline. - const offlineData = await CoreUtils.ignoreErrors( + const offlineData = await CorePromiseUtils.ignoreErrors( AddonModAssignOffline.getSubmissionGrade(this.assign.id, this.userId), undefined, ); diff --git a/src/addons/mod/assign/feedback/comments/services/handler.ts b/src/addons/mod/assign/feedback/comments/services/handler.ts index 88c2ecb7dc4..0ccb29a4f32 100644 --- a/src/addons/mod/assign/feedback/comments/services/handler.ts +++ b/src/addons/mod/assign/feedback/comments/services/handler.ts @@ -25,10 +25,9 @@ import { AddonModAssignFeedbackHandler } from '@addons/mod/assign/services/feedb import { Injectable, Type } from '@angular/core'; import { CoreSites } from '@services/sites'; import { CoreText } from '@singletons/text'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreWSFile } from '@services/ws'; import { makeSingleton } from '@singletons'; -import { AddonModAssignFeedbackCommentsComponent } from '../component/comments'; import { CoreFileHelper } from '@services/file-helper'; /** @@ -73,7 +72,9 @@ export class AddonModAssignFeedbackCommentsHandlerService implements AddonModAss /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { + const { AddonModAssignFeedbackCommentsComponent } = await import('../component/comments'); + return AddonModAssignFeedbackCommentsComponent; } @@ -124,7 +125,7 @@ export class AddonModAssignFeedbackCommentsHandlerService implements AddonModAss userId: number, ): Promise { // Get it from plugin or offline. - const offlineData = await CoreUtils.ignoreErrors( + const offlineData = await CorePromiseUtils.ignoreErrors( AddonModAssignOffline.getSubmissionGrade(assign.id, userId), undefined, ); diff --git a/src/addons/mod/assign/feedback/editpdf/services/handler.ts b/src/addons/mod/assign/feedback/editpdf/services/handler.ts index c40c2e36b80..71aa6f758e2 100644 --- a/src/addons/mod/assign/feedback/editpdf/services/handler.ts +++ b/src/addons/mod/assign/feedback/editpdf/services/handler.ts @@ -22,7 +22,6 @@ import { AddonModAssignFeedbackHandler } from '@addons/mod/assign/services/feedb import { Injectable, Type } from '@angular/core'; import { CoreWSFile } from '@services/ws'; import { makeSingleton } from '@singletons'; -import { AddonModAssignFeedbackEditPdfComponent } from '../component/editpdf'; import type { IAddonModAssignFeedbackPluginComponent } from '@addons/mod/assign/classes/base-feedback-plugin-component'; /** @@ -37,7 +36,9 @@ export class AddonModAssignFeedbackEditPdfHandlerService implements AddonModAssi /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { + const { AddonModAssignFeedbackEditPdfComponent } = await import('../component/editpdf'); + return AddonModAssignFeedbackEditPdfComponent; } diff --git a/src/addons/mod/assign/feedback/file/services/handler.ts b/src/addons/mod/assign/feedback/file/services/handler.ts index dad819815bb..7643efe8f3d 100644 --- a/src/addons/mod/assign/feedback/file/services/handler.ts +++ b/src/addons/mod/assign/feedback/file/services/handler.ts @@ -23,7 +23,6 @@ import { AddonModAssignFeedbackHandler } from '@addons/mod/assign/services/feedb import { Injectable, Type } from '@angular/core'; import { CoreWSFile } from '@services/ws'; import { makeSingleton } from '@singletons'; -import { AddonModAssignFeedbackFileComponent } from '../component/file'; /** * Handler for file feedback plugin. @@ -37,7 +36,9 @@ export class AddonModAssignFeedbackFileHandlerService implements AddonModAssignF /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { + const { AddonModAssignFeedbackFileComponent } = await import('../component/file'); + return AddonModAssignFeedbackFileComponent; } diff --git a/src/addons/mod/assign/pages/edit/edit.ts b/src/addons/mod/assign/pages/edit/edit.ts index 6b6154c4378..811a677d8d3 100644 --- a/src/addons/mod/assign/pages/edit/edit.ts +++ b/src/addons/mod/assign/pages/edit/edit.ts @@ -36,7 +36,7 @@ import { import { AddonModAssignHelper } from '../../services/assign-helper'; import { AddonModAssignOffline } from '../../services/assign-offline'; import { AddonModAssignSync } from '../../services/assign-sync'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreWSError } from '@classes/errors/wserror'; import { CoreWSExternalFile } from '@services/ws'; import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics'; import { @@ -47,6 +47,7 @@ import { } from '../../constants'; import { CoreToasts, ToastDuration } from '@services/toasts'; import { CoreLoadings } from '@services/loadings'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Page that allows adding or editing an assigment submission. @@ -54,7 +55,7 @@ import { CoreLoadings } from '@services/loadings'; @Component({ selector: 'page-addon-mod-assign-edit', templateUrl: 'edit.html', - styleUrls: ['edit.scss'], + styleUrl: 'edit.scss', }) export class AddonModAssignEditPage implements OnInit, OnDestroy, CanLeave { @@ -232,7 +233,7 @@ export class AddonModAssignEditPage implements OnInit, OnDestroy, CanLeave { } // Check if there's any offline data for this submission. - this.hasOffline = await CoreUtils.promiseWorks(AddonModAssignOffline.getSubmission(this.assign.id, this.userId)); + this.hasOffline = await CorePromiseUtils.promiseWorks(AddonModAssignOffline.getSubmission(this.assign.id, this.userId)); CoreAnalytics.logEvent({ type: CoreAnalyticsEventType.VIEW_ITEM, @@ -343,7 +344,7 @@ export class AddonModAssignEditPage implements OnInit, OnDestroy, CanLeave { this.hasOffline, ); } catch (error) { - if (this.allowOffline && !this.saveOffline && !CoreUtils.isWebServiceError(error)) { + if (this.allowOffline && !this.saveOffline && !CoreWSError.isWebServiceError(error)) { // Cannot submit in online, prepare for offline usage. this.saveOffline = true; diff --git a/src/addons/mod/assign/services/assign-helper.ts b/src/addons/mod/assign/services/assign-helper.ts index a816ecab6de..0b197f99dc4 100644 --- a/src/addons/mod/assign/services/assign-helper.ts +++ b/src/addons/mod/assign/services/assign-helper.ts @@ -27,7 +27,7 @@ import { AddonModAssignSubmissionStatusValues, } from './assign'; import { AddonModAssignOffline } from './assign-offline'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreObject } from '@singletons/object'; import { CoreFile } from '@services/file'; import { CoreCourseCommonModWSOptions } from '@features/course/services/course'; import { CoreGroups } from '@services/groups'; @@ -37,6 +37,7 @@ import { makeSingleton } from '@singletons'; import { CoreFormFields } from '@singletons/form'; import { CoreFileEntry } from '@services/file-helper'; import { ADDON_MOD_ASSIGN_COMPONENT } from '../constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Service that provides some helper functions for assign. @@ -81,7 +82,7 @@ export class AddonModAssignHelperProvider { return true; } - if (await CoreUtils.promiseWorks(AddonModAssignOffline.getSubmission(assign.id, submission.userid))) { + if (await CorePromiseUtils.promiseWorks(AddonModAssignOffline.getSubmission(assign.id, submission.userid))) { // Submission was saved or deleted offline, allow editing it or creating a new one. return true; } @@ -313,7 +314,7 @@ export class AddonModAssignHelperProvider { await Promise.all(promises); - return CoreUtils.objectToArray(participantsIndexed); + return CoreObject.toArray(participantsIndexed); } /** @@ -530,7 +531,7 @@ export class AddonModAssignHelperProvider { const promises = feedback.plugins.map((plugin) => this.prepareFeedbackPluginData(assign.id, userId, feedback).then(async (inputData) => { - const changed = await CoreUtils.ignoreErrors( + const changed = await CorePromiseUtils.ignoreErrors( AddonModAssignFeedbackDelegate.hasPluginDataChanged(assign, submission, plugin, inputData, userId), false, ); @@ -541,7 +542,7 @@ export class AddonModAssignHelperProvider { return; })); - await CoreUtils.allPromises(promises); + await CorePromiseUtils.allPromises(promises); return hasChanged; } @@ -579,7 +580,7 @@ export class AddonModAssignHelperProvider { })) : []; - await CoreUtils.allPromises(promises); + await CorePromiseUtils.allPromises(promises); return hasChanged; } diff --git a/src/addons/mod/assign/services/assign-sync.ts b/src/addons/mod/assign/services/assign-sync.ts index 063e3f1cdb3..43923bd8211 100644 --- a/src/addons/mod/assign/services/assign-sync.ts +++ b/src/addons/mod/assign/services/assign-sync.ts @@ -33,13 +33,14 @@ import { } from './assign-offline'; import { CoreSync, CoreSyncResult } from '@services/sync'; import { CoreCourseLogHelper } from '@features/course/services/log-helper'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreWSError } from '@classes/errors/wserror'; import { CoreNetwork } from '@services/network'; import { CoreNetworkError } from '@classes/errors/network-error'; import { CoreGradesFormattedItem, CoreGradesHelper } from '@features/grades/services/grades-helper'; import { AddonModAssignSubmissionDelegate } from './submission-delegate'; import { AddonModAssignFeedbackDelegate } from './feedback-delegate'; -import { ADDON_MOD_ASSIGN_AUTO_SYNCED, ADDON_MOD_ASSIGN_COMPONENT } from '../constants'; +import { ADDON_MOD_ASSIGN_AUTO_SYNCED, ADDON_MOD_ASSIGN_COMPONENT, ADDON_MOD_ASSIGN_MANUAL_SYNCED } from '../constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Service to sync assigns. @@ -189,7 +190,7 @@ export class AddonModAssignSyncProvider extends CoreCourseActivitySyncBaseProvid */ protected async performSyncAssign(assignId: number, siteId: string): Promise { // Sync offline logs. - await CoreUtils.ignoreErrors( + await CorePromiseUtils.ignoreErrors( CoreCourseLogHelper.syncActivity(ADDON_MOD_ASSIGN_COMPONENT, assignId, siteId), ); @@ -207,7 +208,7 @@ export class AddonModAssignSyncProvider extends CoreCourseActivitySyncBaseProvid if (!submissions.length && !grades.length) { // Nothing to sync. - await CoreUtils.ignoreErrors(this.setSyncTime(assignId, siteId)); + await CorePromiseUtils.ignoreErrors(this.setSyncTime(assignId, siteId)); return result; } @@ -246,15 +247,15 @@ export class AddonModAssignSyncProvider extends CoreCourseActivitySyncBaseProvid } })); - await CoreUtils.allPromises(promises); + await CorePromiseUtils.allPromises(promises); if (result.updated) { // Data has been sent to server. Now invalidate the WS calls. - await CoreUtils.ignoreErrors(AddonModAssign.invalidateContent(assign.cmid, courseId, siteId)); + await CorePromiseUtils.ignoreErrors(AddonModAssign.invalidateContent(assign.cmid, courseId, siteId)); } // Sync finished, set sync time. - await CoreUtils.ignoreErrors(this.setSyncTime(assignId, siteId)); + await CorePromiseUtils.ignoreErrors(this.setSyncTime(assignId, siteId)); // All done, return the result. return result; @@ -272,7 +273,7 @@ export class AddonModAssignSyncProvider extends CoreCourseActivitySyncBaseProvid siteId: string, ): Promise { // If no offline data found, return empty array. - return CoreUtils.ignoreErrors(AddonModAssignOffline.getAssignSubmissionsGrade(assignId, siteId), []); + return CorePromiseUtils.ignoreErrors(AddonModAssignOffline.getAssignSubmissionsGrade(assignId, siteId), []); } /** @@ -287,7 +288,7 @@ export class AddonModAssignSyncProvider extends CoreCourseActivitySyncBaseProvid siteId: string, ): Promise { // If no offline data found, return empty array. - return CoreUtils.ignoreErrors(AddonModAssignOffline.getAssignSubmissions(assignId, siteId), []); + return CorePromiseUtils.ignoreErrors(AddonModAssignOffline.getAssignSubmissions(assignId, siteId), []); } /** @@ -359,7 +360,7 @@ export class AddonModAssignSyncProvider extends CoreCourseActivitySyncBaseProvid // Submission data sent, update cached data. No need to block the user for this. AddonModAssign.getSubmissionStatus(assign.id, options); } catch (error) { - if (!error || !CoreUtils.isWebServiceError(error)) { + if (!CoreWSError.isWebServiceError(error)) { // Local error, reject. throw error; } @@ -508,9 +509,9 @@ export class AddonModAssignSyncProvider extends CoreCourseActivitySyncBaseProvid // Update cached data. promises.push(AddonModAssign.getSubmissionStatus(assign.id, options)); - await CoreUtils.allPromises(promises); + await CorePromiseUtils.allPromises(promises); } catch (error) { - if (!error || !CoreUtils.isWebServiceError(error)) { + if (!CoreWSError.isWebServiceError(error)) { // Local error, reject. throw error; } @@ -535,7 +536,7 @@ export type AddonModAssignSyncResult = CoreSyncResult & { }; /** - * Data passed to AUTO_SYNCED event. + * Data passed to ADDON_MOD_ASSIGN_AUTO_SYNCED event. */ export type AddonModAssignAutoSyncData = { assignId: number; @@ -544,9 +545,23 @@ export type AddonModAssignAutoSyncData = { }; /** - * Data passed to MANUAL_SYNCED event. + * Data passed to ADDON_MOD_ASSIGN_MANUAL_SYNCED event. */ export type AddonModAssignManualSyncData = AddonModAssignAutoSyncData & { context: string; submitId?: number; }; + +declare module '@singletons/events' { + + /** + * Augment CoreEventsData interface with events specific to this service. + * + * @see https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation + */ + export interface CoreEventsData { + [ADDON_MOD_ASSIGN_MANUAL_SYNCED]: AddonModAssignManualSyncData; + [ADDON_MOD_ASSIGN_AUTO_SYNCED]: AddonModAssignAutoSyncData; + } + +} diff --git a/src/addons/mod/assign/services/assign.ts b/src/addons/mod/assign/services/assign.ts index a4d5216cd36..488b6758769 100644 --- a/src/addons/mod/assign/services/assign.ts +++ b/src/addons/mod/assign/services/assign.ts @@ -24,23 +24,19 @@ import { CoreTimeUtils } from '@services/utils/time'; import { CoreCourseLogHelper } from '@features/course/services/log-helper'; import { CoreError } from '@classes/errors/error'; import { CoreNetwork } from '@services/network'; -import { CoreUtils } from '@services/utils/utils'; import { AddonModAssignOffline } from './assign-offline'; import { AddonModAssignSubmissionDelegate } from './submission-delegate'; import { CoreComments } from '@features/comments/services/comments'; import { AddonModAssignSubmissionFormatted } from './assign-helper'; import { CoreWSError } from '@classes/errors/wserror'; -import { AddonModAssignAutoSyncData, AddonModAssignManualSyncData } from './assign-sync'; import { CoreFormFields } from '@singletons/form'; import { CoreFileHelper } from '@services/file-helper'; import { CoreIonicColorNames } from '@singletons/colors'; import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site'; -import { ContextLevel } from '@/core/constants'; +import { ContextLevel, CoreCacheUpdateFrequency } from '@/core/constants'; import { - ADDON_MOD_ASSIGN_AUTO_SYNCED, ADDON_MOD_ASSIGN_COMPONENT, ADDON_MOD_ASSIGN_GRADED_EVENT, - ADDON_MOD_ASSIGN_MANUAL_SYNCED, ADDON_MOD_ASSIGN_STARTED_EVENT, ADDON_MOD_ASSIGN_SUBMISSION_REMOVED_EVENT, ADDON_MOD_ASSIGN_SUBMISSION_SAVED_EVENT, @@ -60,8 +56,6 @@ declare module '@singletons/events' { [ADDON_MOD_ASSIGN_SUBMITTED_FOR_GRADING_EVENT]: AddonModAssignSubmittedForGradingEventData; [ADDON_MOD_ASSIGN_GRADED_EVENT]: AddonModAssignGradedEventData; [ADDON_MOD_ASSIGN_STARTED_EVENT]: AddonModAssignStartedEventData; - [ADDON_MOD_ASSIGN_MANUAL_SYNCED]: AddonModAssignManualSyncData; - [ADDON_MOD_ASSIGN_AUTO_SYNCED]: AddonModAssignAutoSyncData; } } @@ -174,7 +168,7 @@ export class AddonModAssignProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getAssignmentCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, component: ADDON_MOD_ASSIGN_COMPONENT, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; @@ -246,7 +240,7 @@ export class AddonModAssignProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getAssignmentUserMappingsCacheKey(assignId), - updateFrequency: CoreSite.FREQUENCY_OFTEN, + updateFrequency: CoreCacheUpdateFrequency.OFTEN, component: ADDON_MOD_ASSIGN_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), @@ -456,7 +450,7 @@ export class AddonModAssignProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getSubmissionsCacheKey(assignId), - updateFrequency: CoreSite.FREQUENCY_OFTEN, + updateFrequency: CoreCacheUpdateFrequency.OFTEN, component: ADDON_MOD_ASSIGN_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), @@ -654,7 +648,7 @@ export class AddonModAssignProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.listParticipantsCacheKey(assignId, groupId), - updateFrequency: CoreSite.FREQUENCY_OFTEN, + updateFrequency: CoreCacheUpdateFrequency.OFTEN, component: ADDON_MOD_ASSIGN_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), @@ -1025,7 +1019,7 @@ export class AddonModAssignProvider { return true; } catch (error) { - if (allowOffline && error && !CoreUtils.isWebServiceError(error)) { + if (allowOffline && error && !CoreWSError.isWebServiceError(error)) { // Couldn't connect to server, store in offline. return storeOffline(); } else { @@ -1135,7 +1129,7 @@ export class AddonModAssignProvider { return true; } catch (error) { - if (error && !CoreUtils.isWebServiceError(error)) { + if (error && !CoreWSError.isWebServiceError(error)) { // Couldn't connect to server, store in offline. return storeOffline(); } else { @@ -1243,7 +1237,7 @@ export class AddonModAssignProvider { return true; } catch (error) { - if (error && !CoreUtils.isWebServiceError(error)) { + if (error && !CoreWSError.isWebServiceError(error)) { // Couldn't connect to server, store in offline. return storeOffline(); } else { @@ -1384,7 +1378,7 @@ export class AddonModAssignProvider { return true; } catch (error) { - if (error && !CoreUtils.isWebServiceError(error)) { + if (error && !CoreWSError.isWebServiceError(error)) { // Couldn't connect to server, store in offline. return storeOffline(); } else { diff --git a/src/addons/mod/assign/services/handlers/prefetch.ts b/src/addons/mod/assign/services/handlers/prefetch.ts index bede2324a8c..63817e54b58 100644 --- a/src/addons/mod/assign/services/handlers/prefetch.ts +++ b/src/addons/mod/assign/services/handlers/prefetch.ts @@ -27,7 +27,7 @@ import { CoreCourseActivityPrefetchHandlerBase } from '@features/course/classes/ import { CoreCourse, CoreCourseAnyModuleData, CoreCourseCommonModWSOptions } from '@features/course/services/course'; import { CoreWSFile } from '@services/ws'; import { AddonModAssignHelper, AddonModAssignSubmissionFormatted } from '../assign-helper'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreFilepool } from '@services/filepool'; import { CoreGroups } from '@services/groups'; import { AddonModAssignSync, AddonModAssignSyncResult } from '../assign-sync'; @@ -252,7 +252,7 @@ export class AddonModAssignPrefetchHandlerService extends CoreCourseActivityPref if (blindMarking) { promises.push( - CoreUtils.ignoreErrors(AddonModAssign.getAssignmentUserMappings(assign.id, -1, modOptions)), + CorePromiseUtils.ignoreErrors(AddonModAssign.getAssignmentUserMappings(assign.id, -1, modOptions)), ); } @@ -260,7 +260,7 @@ export class AddonModAssignPrefetchHandlerService extends CoreCourseActivityPref promises.push(CoreCourse.getModuleBasicInfoByInstance(assign.id, 'assign', { siteId })); // Get course data, needed to determine upload max size if it's configured to be course limit. - promises.push(CoreUtils.ignoreErrors(CoreCourses.getCourseByField('id', courseId, siteId))); + promises.push(CorePromiseUtils.ignoreErrors(CoreCourses.getCourseByField('id', courseId, siteId))); // Download intro files and attachments. Do not call getFiles because it'd call some WS twice. let files: CoreWSFile[] = assign.introattachments || []; diff --git a/src/addons/mod/assign/services/handlers/push-click.ts b/src/addons/mod/assign/services/handlers/push-click.ts index 9388eb4d245..02b9231da64 100644 --- a/src/addons/mod/assign/services/handlers/push-click.ts +++ b/src/addons/mod/assign/services/handlers/push-click.ts @@ -17,10 +17,11 @@ import { CoreCourseHelper } from '@features/course/services/course-helper'; import { CorePushNotificationsClickHandler } from '@features/pushnotifications/services/push-delegate'; import { CorePushNotificationsNotificationBasicData } from '@features/pushnotifications/services/pushnotifications'; import { CoreUrl } from '@singletons/url'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { makeSingleton } from '@singletons'; import { AddonModAssign } from '../assign'; import { ADDON_MOD_ASSIGN_FEATURE_NAME } from '../../constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Handler for assign push notifications clicks. @@ -54,7 +55,7 @@ export class AddonModAssignPushClickHandlerService implements CorePushNotificati const courseId = Number(notification.courseid); const moduleId = Number(contextUrlParams.id); - await CoreUtils.ignoreErrors(AddonModAssign.invalidateContent(moduleId, courseId, notification.site)); + await CorePromiseUtils.ignoreErrors(AddonModAssign.invalidateContent(moduleId, courseId, notification.site)); await CoreCourseHelper.navigateToModule(moduleId, { courseId, siteId: notification.site, diff --git a/src/addons/mod/assign/submission/file/component/file.ts b/src/addons/mod/assign/submission/file/component/file.ts index e142c842f5c..a3e9becfde3 100644 --- a/src/addons/mod/assign/submission/file/component/file.ts +++ b/src/addons/mod/assign/submission/file/component/file.ts @@ -18,7 +18,7 @@ import { AddonModAssignOffline } from '@addons/mod/assign/services/assign-offlin import { Component, OnInit } from '@angular/core'; import { CoreFileUploaderStoreFilesResult } from '@features/fileuploader/services/fileuploader'; import { CoreFileSession } from '@services/file-session'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { AddonModAssignSubmissionFileHandlerService } from '../services/handler'; import { FileEntry } from '@awesome-cordova-plugins/file/ngx'; import { AddonModAssignSubmissionPluginBaseComponent } from '@addons/mod/assign/classes/base-submission-plugin-component'; @@ -54,7 +54,7 @@ export class AddonModAssignSubmissionFileComponent extends AddonModAssignSubmiss : undefined; // Get the offline data. - const offlineData = await CoreUtils.ignoreErrors( + const offlineData = await CorePromiseUtils.ignoreErrors( AddonModAssignOffline.getSubmission(this.assign.id), undefined, ); @@ -67,7 +67,7 @@ export class AddonModAssignSubmissionFileComponent extends AddonModAssignSubmiss // It has offline data. let offlineFiles: FileEntry[] = []; if (offlineDataFiles.offline) { - offlineFiles = await CoreUtils.ignoreErrors( + offlineFiles = await CorePromiseUtils.ignoreErrors( AddonModAssignHelper.getStoredSubmissionFiles( this.assign.id, AddonModAssignSubmissionFileHandlerService.FOLDER_NAME, diff --git a/src/addons/mod/assign/submission/file/services/handler.ts b/src/addons/mod/assign/submission/file/services/handler.ts index 838c26a1eeb..e55694ce67c 100644 --- a/src/addons/mod/assign/submission/file/services/handler.ts +++ b/src/addons/mod/assign/submission/file/services/handler.ts @@ -25,13 +25,13 @@ import { Injectable, Type } from '@angular/core'; import { CoreFileUploader, CoreFileUploaderStoreFilesResult } from '@features/fileuploader/services/fileuploader'; import { CoreFileEntry, CoreFileHelper } from '@services/file-helper'; import { CoreFileSession } from '@services/file-session'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreFileUtils } from '@singletons/file-utils'; import { CoreWSFile } from '@services/ws'; import { makeSingleton } from '@singletons'; -import { AddonModAssignSubmissionFileComponent } from '../component/file'; import { FileEntry } from '@awesome-cordova-plugins/file/ngx'; import type { AddonModAssignSubmissionPluginBaseComponent } from '@addons/mod/assign/classes/base-submission-plugin-component'; import { ADDON_MOD_ASSIGN_COMPONENT } from '@addons/mod/assign/constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Handler for file submission plugin. @@ -101,7 +101,9 @@ export class AddonModAssignSubmissionFileHandlerService implements AddonModAssig /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { + const { AddonModAssignSubmissionFileComponent } = await import('../component/file'); + return AddonModAssignSubmissionFileComponent; } @@ -116,7 +118,7 @@ export class AddonModAssignSubmissionFileHandlerService implements AddonModAssig siteId?: string, ): Promise { - await CoreUtils.ignoreErrors( + await CorePromiseUtils.ignoreErrors( AddonModAssignHelper.deleteStoredSubmissionFiles( assign.id, AddonModAssignSubmissionFileHandlerService.FOLDER_NAME, @@ -174,7 +176,7 @@ export class AddonModAssignSubmissionFileHandlerService implements AddonModAssig submission: AddonModAssignSubmission, plugin: AddonModAssignPlugin, ): Promise { - const offlineData = await CoreUtils.ignoreErrors( + const offlineData = await CorePromiseUtils.ignoreErrors( // Check if there's any offline data. AddonModAssignOffline.getSubmission(assign.id, submission.userid), undefined, @@ -240,7 +242,7 @@ export class AddonModAssignSubmissionFileHandlerService implements AddonModAssig // Data has changed, we need to upload new files and re-upload all the existing files. const currentFiles = CoreFileSession.getFiles(ADDON_MOD_ASSIGN_COMPONENT, assign.id); - const error = CoreUtils.hasRepeatedFilenames(currentFiles); + const error = CoreFileUtils.hasRepeatedFilenames(currentFiles); if (error) { throw error; @@ -302,7 +304,7 @@ export class AddonModAssignSubmissionFileHandlerService implements AddonModAssig if (filesData.offline) { // Has offline files, get them and add them to the list. - const storedFiles = await CoreUtils.ignoreErrors( + const storedFiles = await CorePromiseUtils.ignoreErrors( AddonModAssignHelper.getStoredSubmissionFiles( assign.id, AddonModAssignSubmissionFileHandlerService.FOLDER_NAME, diff --git a/src/addons/mod/assign/submission/onlinetext/component/onlinetext.ts b/src/addons/mod/assign/submission/onlinetext/component/onlinetext.ts index 04447b4d330..de3766f1d25 100644 --- a/src/addons/mod/assign/submission/onlinetext/component/onlinetext.ts +++ b/src/addons/mod/assign/submission/onlinetext/component/onlinetext.ts @@ -19,7 +19,7 @@ import { Component, OnInit, ElementRef } from '@angular/core'; import { FormBuilder, FormControl } from '@angular/forms'; import { CoreSites } from '@services/sites'; import { CoreText } from '@singletons/text'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { AddonModAssignSubmissionOnlineTextPluginData } from '../services/handler'; import { ContextLevel } from '@/core/constants'; import { ADDON_MOD_ASSIGN_COMPONENT } from '@addons/mod/assign/constants'; @@ -60,7 +60,7 @@ export class AddonModAssignSubmissionOnlineTextComponent extends AddonModAssignS */ async ngOnInit(): Promise { // Get the text. Check if we have anything offline. - const offlineData = await CoreUtils.ignoreErrors( + const offlineData = await CorePromiseUtils.ignoreErrors( AddonModAssignOffline.getSubmission(this.assign.id), undefined, ); diff --git a/src/addons/mod/assign/submission/onlinetext/services/handler.ts b/src/addons/mod/assign/submission/onlinetext/services/handler.ts index 73a5b318d94..22e262032db 100644 --- a/src/addons/mod/assign/submission/onlinetext/services/handler.ts +++ b/src/addons/mod/assign/submission/onlinetext/services/handler.ts @@ -26,10 +26,9 @@ import { Injectable, Type } from '@angular/core'; import { CoreError } from '@classes/errors/error'; import { CoreFileHelper } from '@services/file-helper'; import { CoreText } from '@singletons/text'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreWSFile } from '@services/ws'; import { makeSingleton, Translate } from '@singletons'; -import { AddonModAssignSubmissionOnlineTextComponent } from '../component/onlinetext'; /** * Handler for online text submission plugin. @@ -111,7 +110,9 @@ export class AddonModAssignSubmissionOnlineTextHandlerService implements AddonMo /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { + const { AddonModAssignSubmissionOnlineTextComponent } = await import('../component/onlinetext'); + return AddonModAssignSubmissionOnlineTextComponent; } @@ -177,7 +178,7 @@ export class AddonModAssignSubmissionOnlineTextHandlerService implements AddonMo // Get the original text from plugin or offline. const offlineData = - await CoreUtils.ignoreErrors(AddonModAssignOffline.getSubmission(assign.id, submission.userid)); + await CorePromiseUtils.ignoreErrors(AddonModAssignOffline.getSubmission(assign.id, submission.userid)); let initialText = ''; if (offlineData && offlineData.plugindata && offlineData.plugindata.onlinetext_editor) { diff --git a/src/addons/mod/bigbluebuttonbn/components/index/index.ts b/src/addons/mod/bigbluebuttonbn/components/index/index.ts index 2c3e5d53fd7..e17df7e76d3 100644 --- a/src/addons/mod/bigbluebuttonbn/components/index/index.ts +++ b/src/addons/mod/bigbluebuttonbn/components/index/index.ts @@ -23,7 +23,7 @@ import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreText } from '@singletons/text'; import { CoreTimeUtils } from '@services/utils/time'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreArray } from '@singletons/array'; import { Translate } from '@singletons'; import { AddonModBBB, @@ -34,6 +34,8 @@ import { import { ADDON_MOD_BBB_COMPONENT } from '../../constants'; import { CoreLoadings } from '@services/loadings'; import { convertTextToHTMLElement } from '@/core/utils/create-html-element'; +import { CorePromiseUtils } from '@singletons/promise-utils'; +import { CoreOpener } from '@singletons/opener'; /** * Component that displays a Big Blue Button activity. @@ -41,7 +43,7 @@ import { convertTextToHTMLElement } from '@/core/utils/create-html-element'; @Component({ selector: 'addon-mod-bbb-index', templateUrl: 'index.html', - styleUrls: ['index.scss'], + styleUrl: 'index.scss', }) export class AddonModBBBIndexComponent extends CoreCourseModuleMainActivityComponent implements OnInit { @@ -144,7 +146,7 @@ export class AddonModBBBIndexComponent extends CoreCourseModuleMainActivityCompo const recordingsTable = await AddonModBBB.getRecordings(this.bbb.id, this.groupId, { cmId: this.module.id, }); - const columns = CoreUtils.arrayToObject(recordingsTable.columns, 'key'); + const columns = CoreArray.toObject(recordingsTable.columns, 'key'); this.recordings = recordingsTable.parsedData.map(recordingData => { const details: RecordingDetail[] = []; @@ -228,7 +230,7 @@ export class AddonModBBBIndexComponent extends CoreCourseModuleMainActivityCompo return; // Shouldn't happen. } - await CoreUtils.ignoreErrors(AddonModBBB.logView(this.bbb.id)); + await CorePromiseUtils.ignoreErrors(AddonModBBB.logView(this.bbb.id)); this.analyticsLogEvent('mod_bigbluebuttonbn_view_bigbluebuttonbn'); } @@ -302,7 +304,7 @@ export class AddonModBBBIndexComponent extends CoreCourseModuleMainActivityCompo try { const joinUrl = await AddonModBBB.getJoinUrl(this.module.id, this.groupId); - await CoreUtils.openInBrowser(joinUrl, { + await CoreOpener.openInBrowser(joinUrl, { showBrowserWarning: false, }); diff --git a/src/addons/mod/bigbluebuttonbn/services/bigbluebuttonbn.ts b/src/addons/mod/bigbluebuttonbn/services/bigbluebuttonbn.ts index 042cc9ce0a4..0b838178007 100644 --- a/src/addons/mod/bigbluebuttonbn/services/bigbluebuttonbn.ts +++ b/src/addons/mod/bigbluebuttonbn/services/bigbluebuttonbn.ts @@ -16,15 +16,15 @@ import { Injectable } from '@angular/core'; import { CoreError } from '@classes/errors/error'; import { CoreWSError } from '@classes/errors/wserror'; import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site'; -import { CoreSite } from '@classes/sites/site'; import { CoreCourseCommonModWSOptions } from '@features/course/services/course'; import { CoreCourseLogHelper } from '@features/course/services/log-helper'; import { CoreSites, CoreSitesCommonWSOptions } from '@services/sites'; import { CoreText } from '@singletons/text'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreObject } from '@singletons/object'; import { CoreWSExternalFile, CoreWSExternalWarning } from '@services/ws'; import { makeSingleton, Translate } from '@singletons'; import { ADDON_MOD_BBB_COMPONENT } from '../constants'; +import { CoreCacheUpdateFrequency } from '@/core/constants'; /** * Service that provides some features for Big Blue Button activity. @@ -73,7 +73,7 @@ export class AddonModBBBService { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getBBBsCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, component: ADDON_MOD_BBB_COMPONENT, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; @@ -182,7 +182,7 @@ export class AddonModBBBService { return { ...meetingInfo, - features: meetingInfo.features ? CoreUtils.objectToKeyValueMap(meetingInfo.features, 'name', 'isenabled') : undefined, + features: meetingInfo.features ? CoreObject.toKeyValueMap(meetingInfo.features, 'name', 'isenabled') : undefined, }; } diff --git a/src/addons/mod/bigbluebuttonbn/services/handlers/module.ts b/src/addons/mod/bigbluebuttonbn/services/handlers/module.ts index b458cc6188a..7d6d0ba6c08 100644 --- a/src/addons/mod/bigbluebuttonbn/services/handlers/module.ts +++ b/src/addons/mod/bigbluebuttonbn/services/handlers/module.ts @@ -18,7 +18,6 @@ import { CoreModuleHandlerBase } from '@features/course/classes/module-base-hand import { CoreCourseModuleData } from '@features/course/services/course-helper'; import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate'; import { CoreSitePluginsModuleHandler } from '@features/siteplugins/classes/handlers/module-handler'; -import { CoreSitePlugins } from '@features/siteplugins/services/siteplugins'; import { makeSingleton } from '@singletons'; import { AddonModBBBIndexComponent } from '../../components/index'; import { AddonModBBB } from '../bigbluebuttonbn'; @@ -60,6 +59,8 @@ export class AddonModBBBModuleHandlerService extends CoreModuleHandlerBase imple return true; } + const { CoreSitePlugins } = await import('@features/siteplugins/services/siteplugins'); + // Native support not available in this site. Check if it's supported by site plugin. this.sitePluginHandler = CoreSitePlugins.getModuleHandlerInstance(this.modName); // Change the handler name to be able to retrieve the plugin data in component. diff --git a/src/addons/mod/book/components/index/index.ts b/src/addons/mod/book/components/index/index.ts index 1a03cb0db02..ed9b5f425bc 100644 --- a/src/addons/mod/book/components/index/index.ts +++ b/src/addons/mod/book/components/index/index.ts @@ -18,7 +18,7 @@ import { AddonModBook, AddonModBookBookWSData, AddonModBookTocChapter } from '.. 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'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { ADDON_MOD_BOOK_PAGE_NAME, AddonModBookNumbering } from '../../constants'; /** @@ -104,7 +104,7 @@ export class AddonModBookIndexComponent extends CoreCourseModuleMainResourceComp * @inheritdoc */ protected async logActivity(): Promise { - await CoreUtils.ignoreErrors(AddonModBook.logView(this.module.instance)); + await CorePromiseUtils.ignoreErrors(AddonModBook.logView(this.module.instance)); this.analyticsLogEvent('mod_book_view_book'); } diff --git a/src/addons/mod/book/pages/contents/contents.ts b/src/addons/mod/book/pages/contents/contents.ts index 29a8a5f43e9..b9c72d5561d 100644 --- a/src/addons/mod/book/pages/contents/contents.ts +++ b/src/addons/mod/book/pages/contents/contents.ts @@ -26,7 +26,7 @@ import { CoreNetwork } from '@services/network'; import { CoreNavigator } from '@services/navigator'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreErrorHelper } from '@services/error-helper'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { Translate } from '@singletons'; import { AddonModBook, @@ -45,7 +45,7 @@ import { CoreModals } from '@services/modals'; @Component({ selector: 'page-addon-mod-book-contents', templateUrl: 'contents.html', - styleUrls: ['contents.scss'], + styleUrl: 'contents.scss', }) export class AddonModBookContentsPage implements OnInit, OnDestroy { @@ -163,13 +163,13 @@ export class AddonModBookContentsPage implements OnInit, OnDestroy { */ async doRefresh(refresher?: HTMLIonRefresherElement): Promise { if (this.manager) { - await CoreUtils.ignoreErrors(Promise.all([ + await CorePromiseUtils.ignoreErrors(Promise.all([ this.manager.getSource().invalidateContent(), CoreCourseModulePrefetchDelegate.invalidateCourseUpdates(this.courseId), // To detect if book was updated. ])); } - await CoreUtils.ignoreErrors(this.fetchContent(true)); + await CorePromiseUtils.ignoreErrors(this.fetchContent(true)); refresher?.complete(); } @@ -219,7 +219,7 @@ export class AddonModBookContentsPage implements OnInit, OnDestroy { } // Chapter loaded, log view. - await CoreUtils.ignoreErrors(AddonModBook.logView(this.module.instance, chapterId)); + await CorePromiseUtils.ignoreErrors(AddonModBook.logView(this.module.instance, chapterId)); CoreAnalytics.logEvent({ type: CoreAnalyticsEventType.VIEW_ITEM, diff --git a/src/addons/mod/book/services/book.ts b/src/addons/mod/book/services/book.ts index 0973838764e..e6765d299f2 100644 --- a/src/addons/mod/book/services/book.ts +++ b/src/addons/mod/book/services/book.ts @@ -14,13 +14,12 @@ import { Injectable } from '@angular/core'; import { CoreSites, CoreSitesCommonWSOptions } from '@services/sites'; -import { CoreSite } from '@classes/sites/site'; import { CoreTagItem } from '@features/tag/services/tag'; import { CoreWSExternalWarning, CoreWSExternalFile, CoreWS } from '@services/ws'; import { makeSingleton, Translate } from '@singletons'; import { CoreCourseLogHelper } from '@features/course/services/log-helper'; import { CoreCourse, CoreCourseModuleContentFile } from '@features/course/services/course'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreFilepool } from '@services/filepool'; import { CoreText } from '@singletons/text'; import { CoreDomUtils } from '@services/utils/dom'; @@ -29,6 +28,7 @@ import { CoreError } from '@classes/errors/error'; import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site'; import { ADDON_MOD_BOOK_COMPONENT } from '../constants'; import { CoreUrl } from '@singletons/url'; +import { CoreCacheUpdateFrequency } from '@/core/constants'; /** * Service that provides some features for books. @@ -72,7 +72,7 @@ export class AddonModBookProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getBookDataCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, component: ADDON_MOD_BOOK_COMPONENT, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), }; @@ -311,7 +311,7 @@ export class AddonModBookProvider { promises.push(CoreFilepool.invalidateFilesByComponent(siteId, ADDON_MOD_BOOK_COMPONENT, moduleId)); promises.push(CoreCourse.invalidateModule(moduleId, siteId)); - return CoreUtils.allPromises(promises); + return CorePromiseUtils.allPromises(promises); } /** diff --git a/src/addons/mod/book/services/handlers/prefetch.ts b/src/addons/mod/book/services/handlers/prefetch.ts index 640a69e40e0..aebf2a92fcf 100644 --- a/src/addons/mod/book/services/handlers/prefetch.ts +++ b/src/addons/mod/book/services/handlers/prefetch.ts @@ -16,7 +16,7 @@ import { Injectable } from '@angular/core'; import { CoreCourseResourcePrefetchHandlerBase } from '@features/course/classes/resource-prefetch-handler'; import { CoreCourseAnyModuleData } from '@features/course/services/course'; import { CoreCourseModuleData } from '@features/course/services/course-helper'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreWSFile } from '@services/ws'; import { makeSingleton } from '@singletons'; import { AddonModBook } from '../book'; @@ -46,7 +46,7 @@ export class AddonModBookPrefetchHandlerService extends CoreCourseResourcePrefet promises.push(super.downloadOrPrefetch(module, courseId, prefetch)); // Ignore errors since this WS isn't available in some Moodle versions. - promises.push(CoreUtils.ignoreErrors(AddonModBook.getBook(courseId, module.id))); + promises.push(CorePromiseUtils.ignoreErrors(AddonModBook.getBook(courseId, module.id))); await Promise.all(promises); } @@ -58,7 +58,7 @@ export class AddonModBookPrefetchHandlerService extends CoreCourseResourcePrefet * @returns Promise resolved with list of intro files. */ async getIntroFiles(module: CoreCourseAnyModuleData, courseId: number): Promise { - const book = await CoreUtils.ignoreErrors(AddonModBook.getBook(courseId, module.id)); + const book = await CorePromiseUtils.ignoreErrors(AddonModBook.getBook(courseId, module.id)); return this.getIntroFilesFromInstance(module, book); } diff --git a/src/addons/mod/book/services/handlers/tag-area.ts b/src/addons/mod/book/services/handlers/tag-area.ts index 5bcc9c0545a..25a4d78beed 100644 --- a/src/addons/mod/book/services/handlers/tag-area.ts +++ b/src/addons/mod/book/services/handlers/tag-area.ts @@ -14,7 +14,6 @@ import { Injectable, Type } from '@angular/core'; import { CoreCourse } from '@features/course/services/course'; -import { CoreTagFeedComponent } from '@features/tag/components/feed/feed'; import { CoreTagAreaHandler } from '@features/tag/services/tag-area-delegate'; import { CoreTagFeedElement, CoreTagHelper } from '@features/tag/services/tag-helper'; import { CoreSitesReadingStrategy } from '@services/sites'; @@ -68,11 +67,11 @@ export class AddonModBookTagAreaHandlerService implements CoreTagAreaHandler { } /** - * Get the component to use to display items. - * - * @returns The component (or promise resolved with component) to use, undefined if not found. + * @inheritdoc */ - getComponent(): Type | Promise> { + async getComponent(): Promise> { + const { CoreTagFeedComponent } = await import('@features/tag/components/feed/feed'); + return CoreTagFeedComponent; } diff --git a/src/addons/mod/chat/classes/chat-sessions-source.ts b/src/addons/mod/chat/classes/chat-sessions-source.ts index 30c59c06cc0..1308b3b3ea7 100644 --- a/src/addons/mod/chat/classes/chat-sessions-source.ts +++ b/src/addons/mod/chat/classes/chat-sessions-source.ts @@ -16,7 +16,7 @@ import { Params } from '@angular/router'; import { CoreRoutedItemsManagerSource } from '@classes/items-management/routed-items-manager-source'; import { CoreUser } from '@features/user/services/user'; import { CoreGroupInfo, CoreGroups } from '@services/groups'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { Translate } from '@singletons'; import { AddonModChat, AddonModChatSession, AddonModChatSessionUser } from '../services/chat'; @@ -45,10 +45,10 @@ export class AddonModChatSessionsSource extends CoreRoutedItemsManagerSource { - await CoreUtils.ignoreErrors(CoreUtils.allPromises([ + await CorePromiseUtils.allPromisesIgnoringErrors([ CoreGroups.invalidateActivityGroupInfo(this.CM_ID), AddonModChat.invalidateSessions(this.CHAT_ID, this.groupId, this.showAll), - ])); + ]); } /** diff --git a/src/addons/mod/chat/pages/chat/chat.ts b/src/addons/mod/chat/pages/chat/chat.ts index 482c0452673..81fb81cb746 100644 --- a/src/addons/mod/chat/pages/chat/chat.ts +++ b/src/addons/mod/chat/pages/chat/chat.ts @@ -20,7 +20,7 @@ import { CoreNetwork } from '@services/network'; import { CoreNavigator } from '@services/navigator'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { NgZone, Translate } from '@singletons'; import { CoreEvents } from '@singletons/events'; import { Subscription } from 'rxjs'; @@ -258,7 +258,7 @@ export class AddonModChatChatPage implements OnInit, OnDestroy, CanLeave { // Start polling. this.polling = window.setInterval(() => { - CoreUtils.ignoreErrors(this.fetchMessagesInterval()); + CorePromiseUtils.ignoreErrors(this.fetchMessagesInterval()); }, AddonModChatChatPage.POLL_INTERVAL); } @@ -324,7 +324,7 @@ export class AddonModChatChatPage implements OnInit, OnDestroy, CanLeave { await AddonModChat.sendMessage(this.sessionId!, text, beep); // Update messages to show the sent message. - CoreUtils.ignoreErrors(this.fetchMessagesInterval()); + CorePromiseUtils.ignoreErrors(this.fetchMessagesInterval()); } catch (error) { // Only close the keyboard if an error happens, we want the user to be able to send multiple // messages without the keyboard being closed. diff --git a/src/addons/mod/chat/pages/session-messages/session-messages.ts b/src/addons/mod/chat/pages/session-messages/session-messages.ts index 682bb738ea0..d26238d1296 100644 --- a/src/addons/mod/chat/pages/session-messages/session-messages.ts +++ b/src/addons/mod/chat/pages/session-messages/session-messages.ts @@ -17,7 +17,7 @@ import { CoreUser } from '@features/user/services/user'; import { CoreNavigator } from '@services/navigator'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { AddonModChat } from '../../services/chat'; import { AddonModChatFormattedSessionMessage, AddonModChatHelper } from '../../services/chat-helper'; import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics'; @@ -48,7 +48,7 @@ export class AddonModChatSessionMessagesPage implements OnInit { constructor() { this.logView = CoreTime.once(async () => { - await CoreUtils.ignoreErrors(AddonModChat.logViewSessions(this.cmId, { + await CorePromiseUtils.ignoreErrors(AddonModChat.logViewSessions(this.cmId, { start: this.sessionStart, end: this.sessionEnd, })); @@ -159,7 +159,9 @@ export class AddonModChatSessionMessagesPage implements OnInit { */ async refreshMessages(refresher: HTMLIonRefresherElement): Promise { try { - await CoreUtils.ignoreErrors(AddonModChat.invalidateSessionMessages(this.chatId, this.sessionStart, this.groupId)); + await CorePromiseUtils.ignoreErrors( + AddonModChat.invalidateSessionMessages(this.chatId, this.sessionStart, this.groupId), + ); await this.fetchMessages(); } finally { diff --git a/src/addons/mod/chat/pages/sessions/sessions.ts b/src/addons/mod/chat/pages/sessions/sessions.ts index 852504dc9a2..517ae447e14 100644 --- a/src/addons/mod/chat/pages/sessions/sessions.ts +++ b/src/addons/mod/chat/pages/sessions/sessions.ts @@ -24,7 +24,7 @@ import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics'; import { CoreTime } from '@singletons/time'; import { Translate } from '@singletons'; import { AddonModChat } from '@addons/mod/chat/services/chat'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreLoadings } from '@services/loadings'; /** @@ -46,7 +46,7 @@ export class AddonModChatSessionsPage implements OnInit, AfterViewInit, OnDestro this.logView = CoreTime.once(async () => { const source = this.sessions.getSource(); - await CoreUtils.ignoreErrors(AddonModChat.logViewSessions(this.sessions.getSource().CM_ID)); + await CorePromiseUtils.ignoreErrors(AddonModChat.logViewSessions(this.sessions.getSource().CM_ID)); CoreAnalytics.logEvent({ type: CoreAnalyticsEventType.VIEW_ITEM_LIST, diff --git a/src/addons/mod/chat/services/chat.ts b/src/addons/mod/chat/services/chat.ts index 46302eebc2b..9afa91688c3 100644 --- a/src/addons/mod/chat/services/chat.ts +++ b/src/addons/mod/chat/services/chat.ts @@ -15,7 +15,6 @@ import { Injectable } from '@angular/core'; import { CoreError } from '@classes/errors/error'; import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site'; -import { CoreSite } from '@classes/sites/site'; import { CoreCourseCommonModWSOptions } from '@features/course/services/course'; import { CoreCourseLogHelper } from '@features/course/services/log-helper'; import { CoreUser } from '@features/user/services/user'; @@ -23,6 +22,7 @@ import { CoreSites, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@ import { CoreWSExternalFile, CoreWSExternalWarning } from '@services/ws'; import { makeSingleton, Translate } from '@singletons'; import { ADDON_MOD_CHAT_COMPONENT } from '../constants'; +import { CoreCacheUpdateFrequency } from '@/core/constants'; /** * Service that provides some features for chats. @@ -48,7 +48,7 @@ export class AddonModChatProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getChatsCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, component: ADDON_MOD_CHAT_COMPONENT, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; @@ -262,7 +262,7 @@ export class AddonModChatProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getSessionsCacheKey(chatId, groupId, showAll), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + updateFrequency: CoreCacheUpdateFrequency.SOMETIMES, component: ADDON_MOD_CHAT_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. @@ -300,7 +300,7 @@ export class AddonModChatProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getSessionMessagesCacheKey(chatId, sessionStart, groupId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, component: ADDON_MOD_CHAT_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. diff --git a/src/addons/mod/chat/services/handlers/prefetch-lazy.ts b/src/addons/mod/chat/services/handlers/prefetch-lazy.ts index 1f92df135a0..8580086182d 100644 --- a/src/addons/mod/chat/services/handlers/prefetch-lazy.ts +++ b/src/addons/mod/chat/services/handlers/prefetch-lazy.ts @@ -15,7 +15,7 @@ import { Injectable } from '@angular/core'; import { AddonModChatPrefetchHandlerService } from './prefetch'; import { AddonModChat, AddonModChatSession } from '../chat'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreCourse, CoreCourseAnyModuleData, CoreCourseCommonModWSOptions } from '@features/course/services/course'; import { CoreSitesReadingStrategy } from '@services/sites'; import { CoreGroups } from '@services/groups'; @@ -34,7 +34,7 @@ export class AddonModChatPrefetchHandlerLazyService extends AddonModChatPrefetch async invalidateContent(moduleId: number, courseId: number): Promise { const chat = await AddonModChat.getChat(courseId, moduleId); - await CoreUtils.allPromises([ + await CorePromiseUtils.allPromises([ AddonModChat.invalidateAllSessions(chat.id), AddonModChat.invalidateAllSessionMessages(chat.id), ]); @@ -44,7 +44,7 @@ export class AddonModChatPrefetchHandlerLazyService extends AddonModChatPrefetch * @inheritdoc */ async invalidateModule(module: CoreCourseAnyModuleData, courseId: number): Promise { - await CoreUtils.allPromises([ + await CorePromiseUtils.allPromises([ AddonModChat.invalidateChats(courseId), CoreCourse.invalidateModule(module.id), ]); diff --git a/src/addons/mod/choice/services/choice-sync.ts b/src/addons/mod/choice/services/choice-sync.ts index 16adaa8884c..8c938276a98 100644 --- a/src/addons/mod/choice/services/choice-sync.ts +++ b/src/addons/mod/choice/services/choice-sync.ts @@ -20,13 +20,27 @@ import { CoreCourse } from '@features/course/services/course'; import { CoreCourseLogHelper } from '@features/course/services/log-helper'; import { CoreNetwork } from '@services/network'; import { CoreSites } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreWSError } from '@classes/errors/wserror'; import { makeSingleton } from '@singletons'; import { CoreEvents } from '@singletons/events'; import { AddonModChoice } from './choice'; import { AddonModChoiceOffline } from './choice-offline'; import { AddonModChoicePrefetchHandler } from './handlers/prefetch'; import { ADDON_MOD_CHOICE_AUTO_SYNCED, ADDON_MOD_CHOICE_COMPONENT } from '../constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; + +declare module '@singletons/events' { + + /** + * Augment CoreEventsData interface with events specific to this service. + * + * @see https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation + */ + export interface CoreEventsData { + [ADDON_MOD_CHOICE_AUTO_SYNCED]: AddonModChoiceAutoSyncData; + } + +} /** * Service to sync choices. @@ -149,9 +163,9 @@ export class AddonModChoiceSyncProvider extends CoreCourseActivitySyncBaseProvid }; // Sync offline logs. - await CoreUtils.ignoreErrors(CoreCourseLogHelper.syncActivity(ADDON_MOD_CHOICE_COMPONENT, choiceId, siteId)); + await CorePromiseUtils.ignoreErrors(CoreCourseLogHelper.syncActivity(ADDON_MOD_CHOICE_COMPONENT, choiceId, siteId)); - const data = await CoreUtils.ignoreErrors(AddonModChoiceOffline.getResponse(choiceId, siteId, userId)); + const data = await CorePromiseUtils.ignoreErrors(AddonModChoiceOffline.getResponse(choiceId, siteId, userId)); if (!data || !data.choiceid) { // Nothing to sync. Set sync time. @@ -181,7 +195,7 @@ export class AddonModChoiceSyncProvider extends CoreCourseActivitySyncBaseProvid await AddonModChoiceOffline.deleteResponse(choiceId, siteId, userId); } catch (error) { - if (!CoreUtils.isWebServiceError(error)) { + if (!CoreWSError.isWebServiceError(error)) { // Couldn't connect to server, reject. throw error; } @@ -220,7 +234,7 @@ export const AddonModChoiceSync = makeSingleton(AddonModChoiceSyncProvider); export type AddonModChoiceSyncResult = CoreSyncResult; /** - * Data passed to AUTO_SYNCED event. + * Data passed to ADDON_MOD_CHOICE_AUTO_SYNCED event. */ export type AddonModChoiceAutoSyncData = { choiceId: number; diff --git a/src/addons/mod/choice/services/choice.ts b/src/addons/mod/choice/services/choice.ts index 60f50c26b6b..460a3bfea71 100644 --- a/src/addons/mod/choice/services/choice.ts +++ b/src/addons/mod/choice/services/choice.ts @@ -15,19 +15,18 @@ import { Injectable } from '@angular/core'; import { CoreError } from '@classes/errors/error'; import { CoreWSError } from '@classes/errors/wserror'; -import { CoreSite } from '@classes/sites/site'; import { CoreCourseCommonModWSOptions } from '@features/course/services/course'; import { CoreCourseLogHelper } from '@features/course/services/log-helper'; import { CoreNetwork } from '@services/network'; import { CoreFilepool } from '@services/filepool'; import { CoreSites, CoreSitesCommonWSOptions } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; import { CoreStatusWithWarningsWSResponse, CoreWSExternalFile, CoreWSExternalWarning } from '@services/ws'; import { makeSingleton, Translate } from '@singletons'; import { AddonModChoiceOffline } from './choice-offline'; -import { AddonModChoiceAutoSyncData } from './choice-sync'; import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site'; -import { ADDON_MOD_CHOICE_AUTO_SYNCED, ADDON_MOD_CHOICE_COMPONENT, AddonModChoiceShowResults } from '../constants'; +import { ADDON_MOD_CHOICE_COMPONENT, AddonModChoiceShowResults } from '../constants'; +import { CoreCacheUpdateFrequency } from '@/core/constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Service that provides some features for choices. @@ -121,7 +120,7 @@ export class AddonModChoiceProvider { return true; } catch (error) { - if (CoreUtils.isWebServiceError(error)) { + if (CoreWSError.isWebServiceError(error)) { // The WebService has thrown an error, this means that responses cannot be submitted. throw error; } @@ -159,7 +158,7 @@ export class AddonModChoiceProvider { } // Invalidate related data. - await CoreUtils.ignoreErrors(Promise.all([ + await CorePromiseUtils.ignoreErrors(Promise.all([ this.invalidateOptions(choiceId, site.id), this.invalidateResults(choiceId, site.id), ])); @@ -217,7 +216,7 @@ export class AddonModChoiceProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getChoiceDataCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, component: ADDON_MOD_CHOICE_COMPONENT, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; @@ -276,7 +275,7 @@ export class AddonModChoiceProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getChoiceOptionsCacheKey(choiceId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, component: ADDON_MOD_CHOICE_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. @@ -434,7 +433,7 @@ export class AddonModChoiceProvider { return true; } catch (error) { - if (CoreUtils.isWebServiceError(error)) { + if (CoreWSError.isWebServiceError(error)) { // The WebService has thrown an error, this means that responses cannot be submitted. throw error; } @@ -463,7 +462,7 @@ export class AddonModChoiceProvider { await site.write('mod_choice_submit_choice_response', params); // Invalidate related data. - await CoreUtils.ignoreErrors(Promise.all([ + await CorePromiseUtils.ignoreErrors(Promise.all([ this.invalidateOptions(choiceId, siteId), this.invalidateResults(choiceId, siteId), ])); @@ -602,16 +601,3 @@ export type AddonModChoiceSubmitChoiceResponseWSParams = { choiceid: number; // Choice instance id. responses: number[]; // Array of response ids. }; - -declare module '@singletons/events' { - - /** - * Augment CoreEventsData interface with events specific to this service. - * - * @see https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation - */ - export interface CoreEventsData { - [ADDON_MOD_CHOICE_AUTO_SYNCED]: AddonModChoiceAutoSyncData; - } - -} diff --git a/src/addons/mod/choice/services/handlers/prefetch.ts b/src/addons/mod/choice/services/handlers/prefetch.ts index 2bb83c85b7a..9eed10b8629 100644 --- a/src/addons/mod/choice/services/handlers/prefetch.ts +++ b/src/addons/mod/choice/services/handlers/prefetch.ts @@ -18,7 +18,7 @@ import { CoreCourseAnyModuleData, CoreCourseCommonModWSOptions } from '@features import { CoreUser } from '@features/user/services/user'; import { CoreFilepool } from '@services/filepool'; import { CoreSitesReadingStrategy } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreWSFile } from '@services/ws'; import { makeSingleton } from '@singletons'; import { AddonModChoice } from '../choice'; @@ -117,7 +117,7 @@ export class AddonModChoicePrefetchHandlerService extends CoreCourseActivityPref * @inheritdoc */ async getIntroFiles(module: CoreCourseAnyModuleData, courseId: number): Promise { - const choice = await CoreUtils.ignoreErrors(AddonModChoice.getChoice(courseId, module.id)); + const choice = await CorePromiseUtils.ignoreErrors(AddonModChoice.getChoice(courseId, module.id)); return this.getIntroFilesFromInstance(module, choice); } diff --git a/src/addons/mod/data/components/index/index.ts b/src/addons/mod/data/components/index/index.ts index 9c341aebf66..92a4e4489e1 100644 --- a/src/addons/mod/data/components/index/index.ts +++ b/src/addons/mod/data/components/index/index.ts @@ -26,7 +26,7 @@ import { CoreNavigator } from '@services/navigator'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreTimeUtils } from '@services/utils/time'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreArray } from '@singletons/array'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { AddonModData, @@ -52,6 +52,8 @@ import { AddonModDataTemplateMode, } from '../../constants'; import { CoreModals } from '@services/modals'; +import { CorePromiseUtils } from '@singletons/promise-utils'; +import { CoreObject } from '@singletons/object'; const contentToken = ''; @@ -61,7 +63,7 @@ const contentToken = ''; @Component({ selector: 'addon-mod-data-index', templateUrl: 'addon-mod-data-index.html', - styleUrls: ['../../data.scss'], + styleUrl: '../../data.scss', }) export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComponent implements OnInit, OnDestroy { @@ -223,7 +225,7 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp if (sync) { // Try to synchronize the data. - await CoreUtils.ignoreErrors(this.syncActivity(showErrors)); + await CorePromiseUtils.ignoreErrors(this.syncActivity(showErrors)); } this.groupInfo = await CoreGroups.getActivityGroupInfo(this.database.coursemodule); @@ -268,8 +270,8 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp const fields = await AddonModData.getFields(this.database.id, { cmId: this.module.id }); this.search.advanced = []; - this.fields = CoreUtils.arrayToObject(fields, 'id'); - this.fieldsArray = CoreUtils.objectToArray(this.fields); + this.fields = CoreArray.toObject(fields, 'id'); + this.fieldsArray = CoreObject.toArray(this.fields); if (this.fieldsArray.length == 0) { canSearch = false; canAdd = false; diff --git a/src/addons/mod/data/components/search-modal/search-modal.ts b/src/addons/mod/data/components/search-modal/search-modal.ts index 993563ea047..2fe0231904a 100644 --- a/src/addons/mod/data/components/search-modal/search-modal.ts +++ b/src/addons/mod/data/components/search-modal/search-modal.ts @@ -18,7 +18,7 @@ import { CoreTag } from '@features/tag/services/tag'; import { CoreSites } from '@services/sites'; import { CoreFormFields, CoreForms } from '@singletons/form'; import { CoreText } from '@singletons/text'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreObject } from '@singletons/object'; import { ModalController } from '@singletons'; import { AddonModDataField, @@ -89,7 +89,7 @@ export class AddonModDataSearchModalComponent implements OnInit { this.searchForm.addControl('firstname', this.fb.control(this.advancedIndexed['firstname'] || '')); this.searchForm.addControl('lastname', this.fb.control(this.advancedIndexed['lastname'] || '')); - this.fieldsArray = CoreUtils.objectToArray(this.fields); + this.fieldsArray = CoreObject.toArray(this.fields); this.advancedSearch = this.renderAdvancedSearchFields(); } diff --git a/src/addons/mod/data/fields/checkbox/services/handler.ts b/src/addons/mod/data/fields/checkbox/services/handler.ts index 569fb55f58d..36c76a9a586 100644 --- a/src/addons/mod/data/fields/checkbox/services/handler.ts +++ b/src/addons/mod/data/fields/checkbox/services/handler.ts @@ -22,7 +22,6 @@ import { AddonModDataFieldHandler } from '@addons/mod/data/services/data-fields- import { Injectable, Type } from '@angular/core'; import { CoreFormFields } from '@singletons/form'; import { makeSingleton, Translate } from '@singletons'; -import { AddonModDataFieldCheckboxComponent } from '../component/checkbox'; import type { AddonModDataFieldPluginBaseComponent } from '@addons/mod/data/classes/base-field-plugin-component'; /** @@ -37,7 +36,9 @@ export class AddonModDataFieldCheckboxHandlerService implements AddonModDataFiel /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { + const { AddonModDataFieldCheckboxComponent } = await import('../component/checkbox'); + return AddonModDataFieldCheckboxComponent; } diff --git a/src/addons/mod/data/fields/date/services/handler.ts b/src/addons/mod/data/fields/date/services/handler.ts index 3552703626e..81f226eb2af 100644 --- a/src/addons/mod/data/fields/date/services/handler.ts +++ b/src/addons/mod/data/fields/date/services/handler.ts @@ -23,7 +23,6 @@ import { Injectable, Type } from '@angular/core'; import { CoreFormFields } from '@singletons/form'; import { CoreTimeUtils } from '@services/utils/time'; import { makeSingleton, Translate } from '@singletons'; -import { AddonModDataFieldDateComponent } from '../component/date'; import type { AddonModDataFieldPluginBaseComponent } from '@addons/mod/data/classes/base-field-plugin-component'; /** @@ -38,7 +37,9 @@ export class AddonModDataFieldDateHandlerService implements AddonModDataFieldHan /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { + const { AddonModDataFieldDateComponent } = await import('../component/date'); + return AddonModDataFieldDateComponent; } diff --git a/src/addons/mod/data/fields/file/services/handler.ts b/src/addons/mod/data/fields/file/services/handler.ts index a4f9d83ef5b..d1a7ad05f4e 100644 --- a/src/addons/mod/data/fields/file/services/handler.ts +++ b/src/addons/mod/data/fields/file/services/handler.ts @@ -25,7 +25,6 @@ import { FileEntry } from '@awesome-cordova-plugins/file/ngx'; import { CoreFileSession } from '@services/file-session'; import { CoreFormFields } from '@singletons/form'; import { makeSingleton, Translate } from '@singletons'; -import { AddonModDataFieldFileComponent } from '../component/file'; import { CoreFileEntry } from '@services/file-helper'; import type { AddonModDataFieldPluginBaseComponent } from '@addons/mod/data/classes/base-field-plugin-component'; import { ADDON_MOD_DATA_COMPONENT } from '@addons/mod/data/constants'; @@ -42,7 +41,9 @@ export class AddonModDataFieldFileHandlerService implements AddonModDataFieldHan /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { + const { AddonModDataFieldFileComponent } = await import('../component/file'); + return AddonModDataFieldFileComponent; } diff --git a/src/addons/mod/data/fields/latlong/services/handler.ts b/src/addons/mod/data/fields/latlong/services/handler.ts index 14335f77f58..72b67911c42 100644 --- a/src/addons/mod/data/fields/latlong/services/handler.ts +++ b/src/addons/mod/data/fields/latlong/services/handler.ts @@ -22,7 +22,6 @@ import { AddonModDataFieldHandler } from '@addons/mod/data/services/data-fields- import { Injectable, Type } from '@angular/core'; import { CoreFormFields } from '@singletons/form'; import { makeSingleton, Translate } from '@singletons'; -import { AddonModDataFieldLatlongComponent } from '../component/latlong'; import type { AddonModDataFieldPluginBaseComponent } from '@addons/mod/data/classes/base-field-plugin-component'; /** @@ -37,7 +36,9 @@ export class AddonModDataFieldLatlongHandlerService implements AddonModDataField /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { + const { AddonModDataFieldLatlongComponent } = await import('../component/latlong'); + return AddonModDataFieldLatlongComponent; } diff --git a/src/addons/mod/data/fields/menu/services/handler.ts b/src/addons/mod/data/fields/menu/services/handler.ts index 02e6f9e9847..c3a8d08a1c4 100644 --- a/src/addons/mod/data/fields/menu/services/handler.ts +++ b/src/addons/mod/data/fields/menu/services/handler.ts @@ -22,7 +22,6 @@ import { AddonModDataFieldHandler } from '@addons/mod/data/services/data-fields- import { Injectable, Type } from '@angular/core'; import { CoreFormFields } from '@singletons/form'; import { makeSingleton, Translate } from '@singletons'; -import { AddonModDataFieldMenuComponent } from '../component/menu'; import type { AddonModDataFieldPluginBaseComponent } from '@addons/mod/data/classes/base-field-plugin-component'; /** @@ -37,7 +36,9 @@ export class AddonModDataFieldMenuHandlerService implements AddonModDataFieldHan /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { + const { AddonModDataFieldMenuComponent } = await import('../component/menu'); + return AddonModDataFieldMenuComponent; } diff --git a/src/addons/mod/data/fields/multimenu/services/handler.ts b/src/addons/mod/data/fields/multimenu/services/handler.ts index 6096dd2f0a1..532c240f06c 100644 --- a/src/addons/mod/data/fields/multimenu/services/handler.ts +++ b/src/addons/mod/data/fields/multimenu/services/handler.ts @@ -22,7 +22,6 @@ import { AddonModDataFieldHandler } from '@addons/mod/data/services/data-fields- import { Injectable, Type } from '@angular/core'; import { CoreFormFields } from '@singletons/form'; import { makeSingleton, Translate } from '@singletons'; -import { AddonModDataFieldMultimenuComponent } from '../component/multimenu'; import type { AddonModDataFieldPluginBaseComponent } from '@addons/mod/data/classes/base-field-plugin-component'; /** @@ -37,7 +36,9 @@ export class AddonModDataFieldMultimenuHandlerService implements AddonModDataFie /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { + const { AddonModDataFieldMultimenuComponent } = await import('../component/multimenu'); + return AddonModDataFieldMultimenuComponent; } diff --git a/src/addons/mod/data/fields/number/services/handler.ts b/src/addons/mod/data/fields/number/services/handler.ts index 5846662c448..07d4fee08e0 100644 --- a/src/addons/mod/data/fields/number/services/handler.ts +++ b/src/addons/mod/data/fields/number/services/handler.ts @@ -17,7 +17,6 @@ import { Injectable, Type } from '@angular/core'; import { CoreFormFields } from '@singletons/form'; import { makeSingleton, Translate } from '@singletons'; import { AddonModDataFieldTextHandlerService } from '../../text/services/handler'; -import { AddonModDataFieldNumberComponent } from '../component/number'; import type { AddonModDataFieldPluginBaseComponent } from '@addons/mod/data/classes/base-field-plugin-component'; /** @@ -32,7 +31,9 @@ export class AddonModDataFieldNumberHandlerService extends AddonModDataFieldText /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { + const { AddonModDataFieldNumberComponent } = await import('../component/number'); + return AddonModDataFieldNumberComponent; } diff --git a/src/addons/mod/data/fields/picture/services/handler.ts b/src/addons/mod/data/fields/picture/services/handler.ts index 4241bd6092a..621a15c7afe 100644 --- a/src/addons/mod/data/fields/picture/services/handler.ts +++ b/src/addons/mod/data/fields/picture/services/handler.ts @@ -25,7 +25,6 @@ import { FileEntry } from '@awesome-cordova-plugins/file/ngx'; import { CoreFileSession } from '@services/file-session'; import { CoreFormFields } from '@singletons/form'; import { makeSingleton, Translate } from '@singletons'; -import { AddonModDataFieldPictureComponent } from '../component/picture'; import { CoreFileEntry } from '@services/file-helper'; import type { AddonModDataFieldPluginBaseComponent } from '@addons/mod/data/classes/base-field-plugin-component'; import { ADDON_MOD_DATA_COMPONENT } from '@addons/mod/data/constants'; @@ -42,7 +41,9 @@ export class AddonModDataFieldPictureHandlerService implements AddonModDataField /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { + const { AddonModDataFieldPictureComponent } = await import('../component/picture'); + return AddonModDataFieldPictureComponent; } diff --git a/src/addons/mod/data/fields/radiobutton/services/handler.ts b/src/addons/mod/data/fields/radiobutton/services/handler.ts index 769c7bdf8d0..9a867511776 100644 --- a/src/addons/mod/data/fields/radiobutton/services/handler.ts +++ b/src/addons/mod/data/fields/radiobutton/services/handler.ts @@ -22,7 +22,6 @@ import { AddonModDataFieldHandler } from '@addons/mod/data/services/data-fields- import { Injectable, Type } from '@angular/core'; import { CoreFormFields } from '@singletons/form'; import { makeSingleton, Translate } from '@singletons'; -import { AddonModDataFieldRadiobuttonComponent } from '../component/radiobutton'; import type { AddonModDataFieldPluginBaseComponent } from '@addons/mod/data/classes/base-field-plugin-component'; /** @@ -37,7 +36,9 @@ export class AddonModDataFieldRadiobuttonHandlerService implements AddonModDataF /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { + const { AddonModDataFieldRadiobuttonComponent } = await import('../component/radiobutton'); + return AddonModDataFieldRadiobuttonComponent; } diff --git a/src/addons/mod/data/fields/text/services/handler.ts b/src/addons/mod/data/fields/text/services/handler.ts index 40088d2304e..1ded07ee807 100644 --- a/src/addons/mod/data/fields/text/services/handler.ts +++ b/src/addons/mod/data/fields/text/services/handler.ts @@ -22,7 +22,6 @@ import { AddonModDataFieldHandler } from '@addons/mod/data/services/data-fields- import { Injectable, Type } from '@angular/core'; import { CoreFormFields } from '@singletons/form'; import { makeSingleton, Translate } from '@singletons'; -import { AddonModDataFieldTextComponent } from '../component/text'; import type { AddonModDataFieldPluginBaseComponent } from '@addons/mod/data/classes/base-field-plugin-component'; /** @@ -37,7 +36,9 @@ export class AddonModDataFieldTextHandlerService implements AddonModDataFieldHan /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { + const { AddonModDataFieldTextComponent } = await import('../component/text'); + return AddonModDataFieldTextComponent; } diff --git a/src/addons/mod/data/fields/textarea/services/handler.ts b/src/addons/mod/data/fields/textarea/services/handler.ts index 5b58cda67fc..d3226184a84 100644 --- a/src/addons/mod/data/fields/textarea/services/handler.ts +++ b/src/addons/mod/data/fields/textarea/services/handler.ts @@ -19,7 +19,6 @@ import { CoreText } from '@singletons/text'; import { CoreWSFile } from '@services/ws'; import { makeSingleton, Translate } from '@singletons'; import { AddonModDataFieldTextHandlerService } from '../../text/services/handler'; -import { AddonModDataFieldTextareaComponent } from '../component/textarea'; import { CoreFileEntry, CoreFileHelper } from '@services/file-helper'; import type { AddonModDataFieldPluginBaseComponent } from '@addons/mod/data/classes/base-field-plugin-component'; import { CoreDom } from '@singletons/dom'; @@ -36,7 +35,9 @@ export class AddonModDataFieldTextareaHandlerService extends AddonModDataFieldTe /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { + const { AddonModDataFieldTextareaComponent } = await import('../component/textarea'); + return AddonModDataFieldTextareaComponent; } diff --git a/src/addons/mod/data/fields/url/services/handler.ts b/src/addons/mod/data/fields/url/services/handler.ts index 700129ba56b..bca86183f23 100644 --- a/src/addons/mod/data/fields/url/services/handler.ts +++ b/src/addons/mod/data/fields/url/services/handler.ts @@ -17,7 +17,6 @@ import { Injectable, Type } from '@angular/core'; import { CoreFormFields } from '@singletons/form'; import { Translate, makeSingleton } from '@singletons'; import { AddonModDataFieldTextHandlerService } from '../../text/services/handler'; -import { AddonModDataFieldUrlComponent } from '../component/url'; import type { AddonModDataFieldPluginBaseComponent } from '@addons/mod/data/classes/base-field-plugin-component'; /** @@ -32,7 +31,9 @@ export class AddonModDataFieldUrlHandlerService extends AddonModDataFieldTextHan /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { + const { AddonModDataFieldUrlComponent } = await import('../component/url'); + return AddonModDataFieldUrlComponent; } diff --git a/src/addons/mod/data/pages/edit/edit.ts b/src/addons/mod/data/pages/edit/edit.ts index 610ad8eef5b..a24bcfa1a9b 100644 --- a/src/addons/mod/data/pages/edit/edit.ts +++ b/src/addons/mod/data/pages/edit/edit.ts @@ -23,7 +23,7 @@ import { CoreNavigator } from '@services/navigator'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreForms } from '@singletons/form'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { Translate } from '@singletons'; import { CoreEvents } from '@singletons/events'; import { AddonModDataComponentsCompileModule } from '../../components/components-compile.module'; @@ -45,6 +45,8 @@ import { CoreTime } from '@singletons/time'; import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics'; import { ADDON_MOD_DATA_COMPONENT, ADDON_MOD_DATA_ENTRY_CHANGED, AddonModDataTemplateType } from '../../constants'; import { CoreLoadings } from '@services/loadings'; +import { CoreWSError } from '@classes/errors/wserror'; +import { CoreArray } from '@singletons/array'; /** * Page that displays the view edit page. @@ -179,7 +181,7 @@ export class AddonModDataEditPage implements OnInit { this.cssClass = 'addon-data-entries-' + this.database.id; this.fieldsArray = await AddonModData.getFields(this.database.id, { cmId: this.moduleId }); - this.fields = CoreUtils.arrayToObject(this.fieldsArray, 'id'); + this.fields = CoreArray.toObject(this.fieldsArray, 'id'); const entry = await AddonModDataHelper.fetchEntry(this.database, this.fieldsArray, this.entryId || 0); this.entry = entry.entry; @@ -303,7 +305,7 @@ export class AddonModDataEditPage implements OnInit { this.offline, ); } catch (error) { - if (this.offline || CoreUtils.isWebServiceError(error)) { + if (this.offline || CoreWSError.isWebServiceError(error)) { throw error; } diff --git a/src/addons/mod/data/pages/entry/entry.ts b/src/addons/mod/data/pages/entry/entry.ts index d76cdab956d..4991ebf63a9 100644 --- a/src/addons/mod/data/pages/entry/entry.ts +++ b/src/addons/mod/data/pages/entry/entry.ts @@ -22,7 +22,7 @@ import { CoreGroups, CoreGroupInfo } from '@services/groups'; import { CoreNavigator } from '@services/navigator'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreArray } from '@singletons/array'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { AddonModDataComponentsCompileModule } from '../../components/components-compile.module'; import { @@ -43,6 +43,7 @@ import { AddonModDataTemplateType, AddonModDataTemplateMode, } from '../../constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Page that displays the view entry page. @@ -50,7 +51,7 @@ import { @Component({ selector: 'page-addon-mod-data-entry', templateUrl: 'entry.html', - styleUrls: ['../../data.scss'], + styleUrl: '../../data.scss', }) export class AddonModDataEntryPage implements OnInit, OnDestroy { @@ -182,7 +183,7 @@ export class AddonModDataEntryPage implements OnInit, OnDestroy { this.title = this.database.name || this.title; this.fieldsArray = await AddonModData.getFields(this.database.id, { cmId: this.moduleId }); - this.fields = CoreUtils.arrayToObject(this.fieldsArray, 'id'); + this.fields = CoreArray.toObject(this.fieldsArray, 'id'); await this.setEntryFromOffset(); @@ -432,7 +433,7 @@ export class AddonModDataEntryPage implements OnInit, OnDestroy { return; } - await CoreUtils.ignoreErrors(AddonModData.logView(this.database.id)); + await CorePromiseUtils.ignoreErrors(AddonModData.logView(this.database.id)); // Store module viewed because this page also updates recent accessed items block. CoreCourse.storeModuleViewed(this.courseId, this.moduleId); diff --git a/src/addons/mod/data/services/data-fields-delegate.ts b/src/addons/mod/data/services/data-fields-delegate.ts index a175cf3349e..185398f6a01 100644 --- a/src/addons/mod/data/services/data-fields-delegate.ts +++ b/src/addons/mod/data/services/data-fields-delegate.ts @@ -45,7 +45,7 @@ export interface AddonModDataFieldHandler extends CoreDelegateHandler { * @param field The field object. * @returns The component to use, undefined if not found. */ - getComponent?(plugin: AddonModDataField): Type | undefined; + getComponent?(plugin: AddonModDataField): Promise | undefined>; /** * Get field search data in the input data. diff --git a/src/addons/mod/data/services/data-helper.ts b/src/addons/mod/data/services/data-helper.ts index a8d828af7bd..3abf1351744 100644 --- a/src/addons/mod/data/services/data-helper.ts +++ b/src/addons/mod/data/services/data-helper.ts @@ -22,7 +22,7 @@ import { CoreSites, CoreSitesReadingStrategy } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreFormFields } from '@singletons/form'; import { CoreText } from '@singletons/text'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { makeSingleton, Translate } from '@singletons'; import { CoreEvents } from '@singletons/events'; import { @@ -174,7 +174,7 @@ export class AddonModDataHelperProvider { promises.push(AddonModData.invalidateEntryData(dataId, entryId, siteId)); promises.push(AddonModData.invalidateEntriesData(dataId, siteId)); - await CoreUtils.ignoreErrors(Promise.all(promises)); + await CorePromiseUtils.ignoreErrors(Promise.all(promises)); CoreEvents.trigger(ADDON_MOD_DATA_ENTRY_CHANGED, { dataId: dataId, entryId: entryId }, siteId); diff --git a/src/addons/mod/data/services/data-offline.ts b/src/addons/mod/data/services/data-offline.ts index 69b363586d4..a2ab5c134e2 100644 --- a/src/addons/mod/data/services/data-offline.ts +++ b/src/addons/mod/data/services/data-offline.ts @@ -17,7 +17,7 @@ import { CoreFileUploader, CoreFileUploaderStoreFilesResult } from '@features/fi import { CoreFile } from '@services/file'; import { CoreSites } from '@services/sites'; import { CoreText } from '@singletons/text'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { makeSingleton } from '@singletons'; import { CorePath } from '@singletons/path'; import { AddonModDataEntryWSField } from './data'; @@ -80,7 +80,7 @@ export class AddonModDataOfflineProvider { */ protected async deleteEntryFiles(dataId: number, entryId: number, action: AddonModDataAction, siteId?: string): Promise { const site = await CoreSites.getSite(siteId); - const entry = await CoreUtils.ignoreErrors(this.getEntry(dataId, entryId, action, site.id)); + const entry = await CorePromiseUtils.ignoreErrors(this.getEntry(dataId, entryId, action, site.id)); if (!entry || !entry.fields) { // Entry not found or no fields, ignore. @@ -191,7 +191,7 @@ export class AddonModDataOfflineProvider { async hasOfflineData(dataId: number, siteId?: string): Promise { const site = await CoreSites.getSite(siteId); - return CoreUtils.promiseWorks( + return CorePromiseUtils.promiseWorks( site.getDb().recordExists(DATA_ENTRY_TABLE, { dataid: dataId }), ); } diff --git a/src/addons/mod/data/services/data-sync.ts b/src/addons/mod/data/services/data-sync.ts index 26ba459d051..dba2f6dc830 100644 --- a/src/addons/mod/data/services/data-sync.ts +++ b/src/addons/mod/data/services/data-sync.ts @@ -26,7 +26,7 @@ import { CoreFileEntry } from '@services/file-helper'; import { CoreSites, CoreSitesReadingStrategy } from '@services/sites'; import { CoreSync, CoreSyncResult } from '@services/sync'; import { CoreErrorHelper } from '@services/error-helper'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreObject } from '@singletons/object'; import { Translate, makeSingleton } from '@singletons'; import { CoreEvents } from '@singletons/events'; import { AddonModData, AddonModDataData } from './data'; @@ -34,6 +34,8 @@ import { AddonModDataHelper } from './data-helper'; import { AddonModDataOffline, AddonModDataOfflineAction } from './data-offline'; import { ADDON_MOD_DATA_AUTO_SYNCED, ADDON_MOD_DATA_COMPONENT, AddonModDataAction } from '../constants'; import { CoreText } from '@singletons/text'; +import { CorePromiseUtils } from '@singletons/promise-utils'; +import { CoreWSError } from '@classes/errors/wserror'; /** * Service to sync databases. @@ -164,7 +166,7 @@ export class AddonModDataSyncProvider extends CoreCourseActivitySyncBaseProvider */ protected async performSyncDatabase(dataId: number, siteId: string): Promise { // Sync offline logs. - await CoreUtils.ignoreErrors( + await CorePromiseUtils.ignoreErrors( CoreCourseLogHelper.syncActivity(ADDON_MOD_DATA_COMPONENT, dataId, siteId), ); @@ -175,11 +177,11 @@ export class AddonModDataSyncProvider extends CoreCourseActivitySyncBaseProvider // Get answers to be sent. const offlineActions: AddonModDataOfflineAction[] = - await CoreUtils.ignoreErrors(AddonModDataOffline.getDatabaseEntries(dataId, siteId), []); + await CorePromiseUtils.ignoreErrors(AddonModDataOffline.getDatabaseEntries(dataId, siteId), []); if (!offlineActions.length) { // Nothing to sync. - await CoreUtils.ignoreErrors(this.setSyncTime(dataId, siteId)); + await CorePromiseUtils.ignoreErrors(this.setSyncTime(dataId, siteId)); return result; } @@ -204,18 +206,18 @@ export class AddonModDataSyncProvider extends CoreCourseActivitySyncBaseProvider offlineEntries[entry.entryid].push(entry); }); - const promises = CoreUtils.objectToArray(offlineEntries).map((entryActions) => + const promises = CoreObject.toArray(offlineEntries).map((entryActions) => this.syncEntry(database, entryActions, result, siteId)); await Promise.all(promises); if (result.updated) { // Data has been sent to server. Now invalidate the WS calls. - await CoreUtils.ignoreErrors(AddonModData.invalidateContent(database.coursemodule, courseId, siteId)); + await CorePromiseUtils.ignoreErrors(AddonModData.invalidateContent(database.coursemodule, courseId, siteId)); } // Sync finished, set sync time. - await CoreUtils.ignoreErrors(this.setSyncTime(dataId, siteId)); + await CorePromiseUtils.ignoreErrors(this.setSyncTime(dataId, siteId)); return result; } @@ -293,7 +295,7 @@ export class AddonModDataSyncProvider extends CoreCourseActivitySyncBaseProvider timemodified = entry.entry.timemodified; } catch (error) { - if (error && CoreUtils.isWebServiceError(error)) { + if (CoreWSError.isWebServiceError(error)) { // The WebService has thrown an error, this means the entry has been deleted. timemodified = -1; } else { @@ -325,7 +327,7 @@ export class AddonModDataSyncProvider extends CoreCourseActivitySyncBaseProvider await AddonModData.deleteEntryOnline(entryId, siteId); entryResult.deleted = true; } catch (error) { - if (error && CoreUtils.isWebServiceError(error)) { + if (CoreWSError.isWebServiceError(error)) { // The WebService has thrown an error, this means it cannot be performed. Discard. entryResult.discardError = CoreErrorHelper.getErrorMessageFromError(error); } else { @@ -383,7 +385,7 @@ export class AddonModDataSyncProvider extends CoreCourseActivitySyncBaseProvider await AddonModData.editEntryOnline(entryId, editAction.fields, siteId); } } catch (error) { - if (error && CoreUtils.isWebServiceError(error)) { + if (CoreWSError.isWebServiceError(error)) { // The WebService has thrown an error, this means it cannot be performed. Discard. entryResult.discardError = CoreErrorHelper.getErrorMessageFromError(error); } else { @@ -401,7 +403,7 @@ export class AddonModDataSyncProvider extends CoreCourseActivitySyncBaseProvider try { await AddonModData.approveEntryOnline(entryId, approveAction.action == AddonModDataAction.APPROVE, siteId); } catch (error) { - if (error && CoreUtils.isWebServiceError(error)) { + if (CoreWSError.isWebServiceError(error)) { // The WebService has thrown an error, this means it cannot be performed. Discard. entryResult.discardError = CoreErrorHelper.getErrorMessageFromError(error); } else { @@ -453,7 +455,7 @@ export class AddonModDataSyncProvider extends CoreCourseActivitySyncBaseProvider }); } - return CoreUtils.allPromises(subPromises); + return CorePromiseUtils.allPromises(subPromises); })); await Promise.all(promises); @@ -486,3 +488,15 @@ export type AddonModDataAutoSyncData = { offlineEntryId?: number; deleted?: boolean; }; + +declare module '@singletons/events' { + + /** + * Augment CoreEventsData interface with events specific to this service. + * + * @see https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation + */ + export interface CoreEventsData { + [ADDON_MOD_DATA_AUTO_SYNCED]: AddonModDataAutoSyncData; + } +} diff --git a/src/addons/mod/data/services/data.ts b/src/addons/mod/data/services/data.ts index b7d1997b4f4..ce0823581ed 100644 --- a/src/addons/mod/data/services/data.ts +++ b/src/addons/mod/data/services/data.ts @@ -14,7 +14,6 @@ import { Injectable } from '@angular/core'; import { CoreError } from '@classes/errors/error'; -import { CoreSite } from '@classes/sites/site'; import { CoreCourseCommonModWSOptions } from '@features/course/services/course'; import { CoreCourseLogHelper } from '@features/course/services/log-helper'; import { CoreRatingInfo } from '@features/rating/services/rating'; @@ -23,20 +22,21 @@ import { CoreNetwork } from '@services/network'; import { CoreFileEntry } from '@services/file-helper'; import { CoreFilepool } from '@services/filepool'; import { CoreSites, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreArray } from '@singletons/array'; import { CoreWSExternalFile, CoreWSExternalWarning } from '@services/ws'; import { makeSingleton, Translate } from '@singletons'; import { AddonModDataFieldsDelegate } from './data-fields-delegate'; import { AddonModDataOffline } from './data-offline'; -import { AddonModDataAutoSyncData } from './data-sync'; import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site'; import { - ADDON_MOD_DATA_AUTO_SYNCED, ADDON_MOD_DATA_COMPONENT, ADDON_MOD_DATA_ENTRIES_PER_PAGE, ADDON_MOD_DATA_ENTRY_CHANGED, AddonModDataAction, } from '../constants'; +import { CoreCacheUpdateFrequency } from '@/core/constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; +import { CoreWSError } from '@classes/errors/wserror'; declare module '@singletons/events' { @@ -46,7 +46,6 @@ declare module '@singletons/events' { * @see https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation */ export interface CoreEventsData { - [ADDON_MOD_DATA_AUTO_SYNCED]: AddonModDataAutoSyncData; [ADDON_MOD_DATA_ENTRY_CHANGED]: AddonModDataEntryChangedEventData; } } @@ -126,7 +125,7 @@ export class AddonModDataProvider { return result; } catch (error) { - if (CoreUtils.isWebServiceError(error)) { + if (CoreWSError.isWebServiceError(error)) { // The WebService has thrown an error, this means that responses cannot be submitted. throw error; } @@ -215,7 +214,7 @@ export class AddonModDataProvider { sent: true, }; } catch (error) { - if (CoreUtils.isWebServiceError(error)) { + if (CoreWSError.isWebServiceError(error)) { // The WebService has thrown an error, this means that responses cannot be submitted. throw error; } @@ -252,7 +251,7 @@ export class AddonModDataProvider { */ protected checkFields(fields: AddonModDataField[], contents: AddonModDataSubfieldData[]): AddonModDataFieldNotification[] { const notifications: AddonModDataFieldNotification[] = []; - const contentsIndexed = CoreUtils.arrayToObjectMultiple(contents, 'fieldid'); + const contentsIndexed = CoreArray.toObjectMultiple(contents, 'fieldid'); // App is offline, check required fields. fields.forEach((field) => { @@ -310,7 +309,7 @@ export class AddonModDataProvider { try { await this.deleteEntryOnline(entryId, siteId); } catch (error) { - if (CoreUtils.isWebServiceError(error)) { + if (CoreWSError.isWebServiceError(error)) { // The WebService has thrown an error, this means that responses cannot be submitted. throw error; } @@ -426,7 +425,7 @@ export class AddonModDataProvider { return result; } catch (error) { - if (CoreUtils.isWebServiceError(error)) { + if (CoreWSError.isWebServiceError(error)) { // The WebService has thrown an error, this means that responses cannot be submitted. throw error; } @@ -543,7 +542,7 @@ export class AddonModDataProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getDatabaseDataCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, component: ADDON_MOD_DATA_COMPONENT, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; @@ -663,7 +662,7 @@ export class AddonModDataProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getEntriesCacheKey(dataId, options.groupId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + updateFrequency: CoreCacheUpdateFrequency.SOMETIMES, component: ADDON_MOD_DATA_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. @@ -721,7 +720,7 @@ export class AddonModDataProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getEntryCacheKey(dataId, entryId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + updateFrequency: CoreCacheUpdateFrequency.SOMETIMES, component: ADDON_MOD_DATA_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. @@ -742,7 +741,7 @@ export class AddonModDataProvider { */ protected formatEntryContents(entry: AddonModDataEntryWS): AddonModDataEntry { return Object.assign(entry, { - contents: CoreUtils.arrayToObject(entry.contents, 'fieldid'), + contents: CoreArray.toObject(entry.contents, 'fieldid'), }); } @@ -773,7 +772,7 @@ export class AddonModDataProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getFieldsCacheKey(dataId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, component: ADDON_MOD_DATA_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. @@ -826,7 +825,7 @@ export class AddonModDataProvider { promises.push(this.invalidateFiles(moduleId, siteId)); - await CoreUtils.allPromises(promises); + await CorePromiseUtils.allPromises(promises); } /** diff --git a/src/addons/mod/data/services/handlers/prefetch-lazy.ts b/src/addons/mod/data/services/handlers/prefetch-lazy.ts index d79159dbd90..27621eb0819 100644 --- a/src/addons/mod/data/services/handlers/prefetch-lazy.ts +++ b/src/addons/mod/data/services/handlers/prefetch-lazy.ts @@ -20,13 +20,14 @@ import { CoreFilepool } from '@services/filepool'; import { CoreGroup, CoreGroups } from '@services/groups'; import { CoreSitesCommonWSOptions, CoreSites, CoreSitesReadingStrategy } from '@services/sites'; import { CoreTimeUtils } from '@services/utils/time'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreObject } from '@singletons/object'; import { CoreWSFile } from '@services/ws'; import { makeSingleton } from '@singletons'; import { AddonModDataEntry, AddonModData, AddonModDataData } from '../data'; import { AddonModDataSync, AddonModDataSyncResult } from '../data-sync'; import { ContextLevel } from '@/core/constants'; import { AddonModDataPrefetchHandlerService } from '@addons/mod/data/services/handlers/prefetch'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Handler to prefetch databases. @@ -62,7 +63,7 @@ export class AddonModDataPrefetchHandlerLazyService extends AddonModDataPrefetch }); }); - return CoreUtils.objectToArray(uniqueEntries); + return CoreObject.toArray(uniqueEntries); } /** @@ -132,7 +133,7 @@ export class AddonModDataPrefetchHandlerLazyService extends AddonModDataPrefetch let files: CoreWSFile[] = []; entries.forEach((entry) => { - CoreUtils.objectToArray(entry.contents).forEach((content) => { + CoreObject.toArray(entry.contents).forEach((content) => { files = files.concat(content.files); }); }); @@ -151,7 +152,7 @@ export class AddonModDataPrefetchHandlerLazyService extends AddonModDataPrefetch * @inheritdoc */ async getIntroFiles(module: CoreCourseAnyModuleData, courseId: number): Promise { - const data = await CoreUtils.ignoreErrors(AddonModData.getDatabase(courseId, module.id)); + const data = await CorePromiseUtils.ignoreErrors(AddonModData.getDatabase(courseId, module.id)); return this.getIntroFilesFromInstance(module, data); } @@ -260,7 +261,7 @@ export class AddonModDataPrefetchHandlerLazyService extends AddonModDataPrefetch promises.push(CoreCourse.getModuleBasicInfoByInstance(database.id, 'data', { siteId })); // Get course data, needed to determine upload max size if it's configured to be course limit. - promises.push(CoreUtils.ignoreErrors(CoreCourses.getCourseByField('id', courseId, siteId))); + promises.push(CorePromiseUtils.ignoreErrors(CoreCourses.getCourseByField('id', courseId, siteId))); await Promise.all(promises); } diff --git a/src/addons/mod/data/services/handlers/tag-area.ts b/src/addons/mod/data/services/handlers/tag-area.ts index f6c4033e088..971af677c48 100644 --- a/src/addons/mod/data/services/handlers/tag-area.ts +++ b/src/addons/mod/data/services/handlers/tag-area.ts @@ -13,7 +13,6 @@ // limitations under the License. import { Injectable, Type } from '@angular/core'; -import { CoreTagFeedComponent } from '@features/tag/components/feed/feed'; import { CoreTagAreaHandler } from '@features/tag/services/tag-area-delegate'; import { CoreTagFeedElement, CoreTagHelper } from '@features/tag/services/tag-helper'; import { makeSingleton } from '@singletons'; @@ -44,7 +43,9 @@ export class AddonModDataTagAreaHandlerService implements CoreTagAreaHandler { /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { + const { CoreTagFeedComponent } = await import('@features/tag/components/feed/feed'); + return CoreTagFeedComponent; } diff --git a/src/addons/mod/feedback/components/index/index.ts b/src/addons/mod/feedback/components/index/index.ts index c76f6b961da..33e571e709b 100644 --- a/src/addons/mod/feedback/components/index/index.ts +++ b/src/addons/mod/feedback/components/index/index.ts @@ -23,7 +23,7 @@ import { CoreNavigator } from '@services/navigator'; import { CoreSites } from '@services/sites'; import { CoreText } from '@singletons/text'; import { CoreTimeUtils } from '@services/utils/time'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { AddonModFeedback, @@ -35,11 +35,11 @@ import { AddonModFeedbackOffline } from '../../services/feedback-offline'; import { AddonModFeedbackAutoSyncData, AddonModFeedbackSync, - AddonModFeedbackSyncProvider, AddonModFeedbackSyncResult, } from '../../services/feedback-sync'; import { AddonModFeedbackPrefetchHandler } from '../../services/handlers/prefetch'; import { + ADDON_MOD_FEEDBACK_AUTO_SYNCED, ADDON_MOD_FEEDBACK_COMPONENT, ADDON_MOD_FEEDBACK_FORM_SUBMITTED, ADDON_MOD_FEEDBACK_PAGE_NAME, @@ -87,7 +87,7 @@ export class AddonModFeedbackIndexComponent extends CoreCourseModuleMainActivity }; protected submitObserver: CoreEventObserver; - protected syncEventName = AddonModFeedbackSyncProvider.AUTO_SYNCED; + protected syncEventName = ADDON_MOD_FEEDBACK_AUTO_SYNCED; protected checkCompletionAfterLog = false; constructor( @@ -108,7 +108,7 @@ export class AddonModFeedbackIndexComponent extends CoreCourseModuleMainActivity // Prefetch data if needed. if (!data.offline && this.isPrefetched()) { - await CoreUtils.ignoreErrors(AddonModFeedbackSync.prefetchAfterUpdate( + await CorePromiseUtils.ignoreErrors(AddonModFeedbackSync.prefetchAfterUpdate( AddonModFeedbackPrefetchHandler.instance, this.module, this.courseId, diff --git a/src/addons/mod/feedback/constants.ts b/src/addons/mod/feedback/constants.ts index 15b72443080..efd3cc0f77e 100644 --- a/src/addons/mod/feedback/constants.ts +++ b/src/addons/mod/feedback/constants.ts @@ -23,6 +23,8 @@ export const ADDON_MOD_FEEDBACK_MULTICHOICE_ADJUST_SEP = '<<<<<'; export const ADDON_MOD_FEEDBACK_MULTICHOICE_HIDENOSELECT = 'h'; export const ADDON_MOD_FEEDBACK_MULTICHOICERATED_VALUE_SEP = '####'; +export const ADDON_MOD_FEEDBACK_AUTO_SYNCED = 'addon_mod_feedback_autom_synced'; + export const ADDON_MOD_FEEDBACK_PER_PAGE = 20; /** diff --git a/src/addons/mod/feedback/pages/attempts/attempts.ts b/src/addons/mod/feedback/pages/attempts/attempts.ts index 17114add8b0..680d0f6ccb6 100644 --- a/src/addons/mod/feedback/pages/attempts/attempts.ts +++ b/src/addons/mod/feedback/pages/attempts/attempts.ts @@ -21,7 +21,7 @@ import { CoreSplitViewComponent } from '@components/split-view/split-view'; import { CoreGroupInfo } from '@services/groups'; import { CoreNavigator } from '@services/navigator'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { AddonModFeedbackAttemptItem, AddonModFeedbackAttemptsSource } from '../../classes/feedback-attempts-source'; import { AddonModFeedbackWSAnonAttempt, AddonModFeedbackWSAttempt } from '../../services/feedback'; import { CoreTime } from '@singletons/time'; @@ -180,7 +180,7 @@ export class AddonModFeedbackAttemptsPage implements AfterViewInit, OnDestroy { try { this.fetchFailed = false; - await CoreUtils.ignoreErrors(attempts.getSource().invalidateCache()); + await CorePromiseUtils.ignoreErrors(attempts.getSource().invalidateCache()); await attempts.getSource().loadFeedback(); await attempts.reload(); } catch (error) { diff --git a/src/addons/mod/feedback/pages/form/form.ts b/src/addons/mod/feedback/pages/form/form.ts index 3302937a762..446684fec0c 100644 --- a/src/addons/mod/feedback/pages/form/form.ts +++ b/src/addons/mod/feedback/pages/form/form.ts @@ -22,7 +22,7 @@ import { CoreNetwork } from '@services/network'; import { CoreNavigator } from '@services/navigator'; import { CoreSites, CoreSitesReadingStrategy } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { NgZone, Translate } from '@singletons'; import { CoreEvents } from '@singletons/events'; import { Subscription } from 'rxjs'; @@ -44,6 +44,9 @@ import { } from '../../constants'; import { CoreLoadings } from '@services/loadings'; import { CoreError } from '@classes/errors/error'; +import { CorePromiseUtils } from '@singletons/promise-utils'; +import { CoreWSError } from '@classes/errors/wserror'; +import { CoreObject } from '@singletons/object'; /** * Page that displays feedback form. @@ -51,7 +54,7 @@ import { CoreError } from '@classes/errors/error'; @Component({ selector: 'page-addon-mod-feedback-form', templateUrl: 'form.html', - styleUrls: ['form.scss'], + styleUrl: 'form.scss', }) export class AddonModFeedbackFormPage implements OnInit, OnDestroy, CanLeave { @@ -156,7 +159,7 @@ export class AddonModFeedbackFormPage implements OnInit, OnDestroy, CanLeave { if (this.items && !this.completed && this.originalData) { // Form submitted. Check if there is any change. - if (!CoreUtils.basicLeftCompare(responses, this.originalData, 3)) { + if (!CoreObject.basicLeftCompare(responses, this.originalData, 3)) { await CoreDomUtils.showConfirm(Translate.instant('core.confirmcanceledit')); } } @@ -218,7 +221,7 @@ export class AddonModFeedbackFormPage implements OnInit, OnDestroy, CanLeave { try { this.access = await AddonModFeedback.getFeedbackAccessInformation(this.feedback.id, options); } catch (error) { - if (this.offline || CoreUtils.isWebServiceError(error)) { + if (this.offline || CoreWSError.isWebServiceError(error)) { // Already offline or shouldn't go offline, fail. throw error; } @@ -245,7 +248,7 @@ export class AddonModFeedbackFormPage implements OnInit, OnDestroy, CanLeave { try { return await AddonModFeedback.getResumePage(this.feedback.id, options); } catch (error) { - if (this.offline || CoreUtils.isWebServiceError(error)) { + if (this.offline || CoreWSError.isWebServiceError(error)) { // Already offline or shouldn't go offline, fail. throw error; } @@ -313,7 +316,7 @@ export class AddonModFeedbackFormPage implements OnInit, OnDestroy, CanLeave { try { response = await AddonModFeedback.getPageItemsWithValues(this.feedback.id, page, options); } catch (error) { - if (this.offline || CoreUtils.isWebServiceError(error)) { + if (this.offline || CoreWSError.isWebServiceError(error)) { // Already offline or shouldn't go offline, fail. throw error; } @@ -349,7 +352,7 @@ export class AddonModFeedbackFormPage implements OnInit, OnDestroy, CanLeave { try { // Sync other pages first. - await CoreUtils.ignoreErrors(AddonModFeedbackSync.syncFeedback(this.feedback.id)); + await CorePromiseUtils.ignoreErrors(AddonModFeedbackSync.syncFeedback(this.feedback.id)); const response = await AddonModFeedback.processPage(this.feedback.id, this.currentPage, responses, { goPrevious, diff --git a/src/addons/mod/feedback/pages/nonrespondents/nonrespondents.ts b/src/addons/mod/feedback/pages/nonrespondents/nonrespondents.ts index 66a88cd4ffe..30073a27ad5 100644 --- a/src/addons/mod/feedback/pages/nonrespondents/nonrespondents.ts +++ b/src/addons/mod/feedback/pages/nonrespondents/nonrespondents.ts @@ -16,7 +16,7 @@ import { Component, OnInit } from '@angular/core'; import { CoreGroupInfo, CoreGroups } from '@services/groups'; import { CoreNavigator } from '@services/navigator'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { AddonModFeedback, AddonModFeedbackWSFeedback } from '../../services/feedback'; import { AddonModFeedbackHelper, AddonModFeedbackNonRespondent } from '../../services/feedback-helper'; import { CoreTime } from '@singletons/time'; @@ -182,7 +182,7 @@ export class AddonModFeedbackNonRespondentsPage implements OnInit { promises.push(AddonModFeedback.invalidateNonRespondentsData(this.feedback.id)); } - await CoreUtils.ignoreErrors(Promise.all(promises)); + await CorePromiseUtils.ignoreErrors(Promise.all(promises)); await this.fetchData(true); } finally { diff --git a/src/addons/mod/feedback/services/feedback-helper.ts b/src/addons/mod/feedback/services/feedback-helper.ts index 80728c8d353..e6e88c46c27 100644 --- a/src/addons/mod/feedback/services/feedback-helper.ts +++ b/src/addons/mod/feedback/services/feedback-helper.ts @@ -20,7 +20,7 @@ import { CoreSites, CoreSitesReadingStrategy } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreFileHelper } from '@services/file-helper'; import { CoreTimeUtils } from '@services/utils/time'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { makeSingleton, Translate } from '@singletons'; import { AddonModFeedback, diff --git a/src/addons/mod/feedback/services/feedback-sync.ts b/src/addons/mod/feedback/services/feedback-sync.ts index aeaf4d6abeb..bb7e2ce287f 100644 --- a/src/addons/mod/feedback/services/feedback-sync.ts +++ b/src/addons/mod/feedback/services/feedback-sync.ts @@ -21,13 +21,14 @@ import { CoreCourseLogHelper } from '@features/course/services/log-helper'; import { CoreNetwork } from '@services/network'; import { CoreSites, CoreSitesReadingStrategy } from '@services/sites'; import { CoreSync, CoreSyncResult } from '@services/sync'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreWSError } from '@classes/errors/wserror'; import { makeSingleton, Translate } from '@singletons'; import { CoreEvents } from '@singletons/events'; import { AddonModFeedback, AddonModFeedbackWSFeedback } from './feedback'; import { AddonModFeedbackOffline, AddonModFeedbackOfflineResponse } from './feedback-offline'; import { AddonModFeedbackPrefetchHandler, AddonModFeedbackPrefetchHandlerService } from './handlers/prefetch'; -import { ADDON_MOD_FEEDBACK_COMPONENT } from '../constants'; +import { ADDON_MOD_FEEDBACK_AUTO_SYNCED, ADDON_MOD_FEEDBACK_COMPONENT } from '../constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Service to sync feedbacks. @@ -35,8 +36,6 @@ import { ADDON_MOD_FEEDBACK_COMPONENT } from '../constants'; @Injectable({ providedIn: 'root' }) export class AddonModFeedbackSyncProvider extends CoreCourseActivitySyncBaseProvider { - static readonly AUTO_SYNCED = 'addon_mod_feedback_autom_synced'; - protected componentTranslatableString = 'feedback'; constructor() { @@ -96,7 +95,7 @@ export class AddonModFeedbackSyncProvider extends CoreCourseActivitySyncBaseProv if (result?.updated) { // Sync successful, send event. - CoreEvents.trigger(AddonModFeedbackSyncProvider.AUTO_SYNCED, { + CoreEvents.trigger(ADDON_MOD_FEEDBACK_AUTO_SYNCED, { feedbackId: response.feedbackid, warnings: result.warnings, }, siteId); @@ -163,14 +162,14 @@ export class AddonModFeedbackSyncProvider extends CoreCourseActivitySyncBaseProv }; // Sync offline logs. - await CoreUtils.ignoreErrors(CoreCourseLogHelper.syncActivity(ADDON_MOD_FEEDBACK_COMPONENT, feedbackId, siteId)); + await CorePromiseUtils.ignoreErrors(CoreCourseLogHelper.syncActivity(ADDON_MOD_FEEDBACK_COMPONENT, feedbackId, siteId)); // Get offline responses to be sent. - const responses = await CoreUtils.ignoreErrors(AddonModFeedbackOffline.getFeedbackResponses(feedbackId, siteId)); + const responses = await CorePromiseUtils.ignoreErrors(AddonModFeedbackOffline.getFeedbackResponses(feedbackId, siteId)); if (!responses || !responses.length) { // Nothing to sync. - await CoreUtils.ignoreErrors(this.setSyncTime(feedbackId, siteId)); + await CorePromiseUtils.ignoreErrors(this.setSyncTime(feedbackId, siteId)); return result; } @@ -200,7 +199,7 @@ export class AddonModFeedbackSyncProvider extends CoreCourseActivitySyncBaseProv Translate.instant('addon.mod_feedback.this_feedback_is_already_submitted'), ); - await CoreUtils.ignoreErrors(this.setSyncTime(feedbackId, siteId)); + await CorePromiseUtils.ignoreErrors(this.setSyncTime(feedbackId, siteId)); return result; } @@ -219,7 +218,7 @@ export class AddonModFeedbackSyncProvider extends CoreCourseActivitySyncBaseProv })); // Execute all the processes in order to solve dependencies. - await CoreUtils.executeOrderedPromises(orderedData); + await CorePromiseUtils.executeOrderedPromises(orderedData); if (result.updated) { // Data has been sent to server, update data. @@ -233,7 +232,7 @@ export class AddonModFeedbackSyncProvider extends CoreCourseActivitySyncBaseProv } // Sync finished, set sync time. - await CoreUtils.ignoreErrors(this.setSyncTime(feedbackId, siteId)); + await CorePromiseUtils.ignoreErrors(this.setSyncTime(feedbackId, siteId)); return result; } @@ -267,7 +266,7 @@ export class AddonModFeedbackSyncProvider extends CoreCourseActivitySyncBaseProv await AddonModFeedbackOffline.deleteFeedbackPageResponses(feedback.id, data.page, siteId); } catch (error) { - if (!CoreUtils.isWebServiceError(error)) { + if (!CoreWSError.isWebServiceError(error)) { // Couldn't connect to server, reject. throw error; } @@ -296,9 +295,22 @@ export const AddonModFeedbackSync = makeSingleton(AddonModFeedbackSyncProvider); export type AddonModFeedbackSyncResult = CoreSyncResult; /** - * Data passed to AUTO_SYNCED event. + * Data passed to ADDON_MOD_FEEDBACK_AUTO_SYNCED event. */ export type AddonModFeedbackAutoSyncData = { feedbackId: number; warnings: string[]; }; + +declare module '@singletons/events' { + + /** + * Augment CoreEventsData interface with events specific to this service. + * + * @see https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation + */ + export interface CoreEventsData { + [ADDON_MOD_FEEDBACK_AUTO_SYNCED]: AddonModFeedbackAutoSyncData; + } + +} diff --git a/src/addons/mod/feedback/services/feedback.ts b/src/addons/mod/feedback/services/feedback.ts index 0f369d71919..f89a6a3ed1f 100644 --- a/src/addons/mod/feedback/services/feedback.ts +++ b/src/addons/mod/feedback/services/feedback.ts @@ -14,17 +14,15 @@ import { Injectable } from '@angular/core'; import { CoreError } from '@classes/errors/error'; -import { CoreSite } from '@classes/sites/site'; import { CoreCourseCommonModWSOptions } from '@features/course/services/course'; import { CoreCourseLogHelper } from '@features/course/services/log-helper'; import { CoreNetwork } from '@services/network'; import { CoreFilepool } from '@services/filepool'; import { CoreSites, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreObject } from '@singletons/object'; import { CoreWSExternalFile, CoreWSExternalWarning, CoreWSStoredFile } from '@services/ws'; import { makeSingleton, Translate } from '@singletons'; import { AddonModFeedbackOffline } from './feedback-offline'; -import { AddonModFeedbackAutoSyncData, AddonModFeedbackSyncProvider } from './feedback-sync'; import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site'; import { ADDON_MOD_FEEDBACK_COMPONENT, @@ -36,6 +34,9 @@ import { ADDON_MOD_FEEDBACK_PER_PAGE, AddonModFeedbackIndexTabName, } from '../constants'; +import { CoreCacheUpdateFrequency } from '@/core/constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; +import { CoreWSError } from '@classes/errors/wserror'; /** * Service that provides some features for feedbacks. @@ -157,7 +158,7 @@ export class AddonModFeedbackProvider { } // Merge with offline data. - const offlineResponses = await CoreUtils.ignoreErrors( + const offlineResponses = await CorePromiseUtils.ignoreErrors( AddonModFeedbackOffline.getFeedbackResponses(feedbackId, options.siteId), ); @@ -169,7 +170,7 @@ export class AddonModFeedbackProvider { // Merge all values into one array. const offlineValuesArray = offlineResponses.reduce((array, entry) => { - const responses = CoreUtils.objectToArrayOfObjects(entry.responses, 'id', 'value'); + const responses = CoreObject.toArrayOfObjects(entry.responses, 'id', 'value'); return array.concat(responses); }, []).map((valueEntry) => { @@ -590,7 +591,7 @@ export class AddonModFeedbackProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getFeedbackCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, component: ADDON_MOD_FEEDBACK_COMPONENT, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; @@ -648,7 +649,7 @@ export class AddonModFeedbackProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getItemsDataCacheKey(feedbackId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + updateFrequency: CoreCacheUpdateFrequency.SOMETIMES, component: ADDON_MOD_FEEDBACK_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. @@ -1082,12 +1083,12 @@ export class AddonModFeedbackProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getCompletedDataCacheKey(feedbackId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, component: ADDON_MOD_FEEDBACK_COMPONENT, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; - return CoreUtils.promiseWorks(site.read('mod_feedback_get_last_completed', params, preSets)); + return CorePromiseUtils.promiseWorks(site.read('mod_feedback_get_last_completed', params, preSets)); } /** @@ -1195,7 +1196,7 @@ export class AddonModFeedbackProvider { try { return await this.processPageOnline(feedbackId, page, responses, !!options.goPrevious, options.siteId); } catch (error) { - if (CoreUtils.isWebServiceError(error)) { + if (CoreWSError.isWebServiceError(error)) { // The WebService has thrown an error, this means that responses cannot be submitted. throw error; } @@ -1227,16 +1228,16 @@ export class AddonModFeedbackProvider { const params: AddonModFeedbackProcessPageWSParams = { feedbackid: feedbackId, page: page, - responses: CoreUtils.objectToArrayOfObjects(responses, 'name', 'value'), + responses: CoreObject.toArrayOfObjects(responses, 'name', 'value'), goprevious: goPrevious, }; const response = await site.write('mod_feedback_process_page', params); // Invalidate and update current values because they will change. - await CoreUtils.ignoreErrors(this.invalidateCurrentValuesData(feedbackId, site.getId())); + await CorePromiseUtils.ignoreErrors(this.invalidateCurrentValuesData(feedbackId, site.getId())); - await CoreUtils.ignoreErrors(this.getCurrentValues(feedbackId, { siteId: site.getId() })); + await CorePromiseUtils.ignoreErrors(this.getCurrentValues(feedbackId, { siteId: site.getId() })); return response; } @@ -1254,7 +1255,6 @@ declare module '@singletons/events' { */ export interface CoreEventsData { [ADDON_MOD_FEEDBACK_FORM_SUBMITTED]: AddonModFeedbackFormSubmittedData; - [AddonModFeedbackSyncProvider.AUTO_SYNCED]: AddonModFeedbackAutoSyncData; } } diff --git a/src/addons/mod/feedback/services/handlers/prefetch.ts b/src/addons/mod/feedback/services/handlers/prefetch.ts index 576c841fd57..3d8ffbdb3c3 100644 --- a/src/addons/mod/feedback/services/handlers/prefetch.ts +++ b/src/addons/mod/feedback/services/handlers/prefetch.ts @@ -19,7 +19,7 @@ import { CoreFilepool } from '@services/filepool'; import { CoreGroups } from '@services/groups'; import { CoreSitesReadingStrategy } from '@services/sites'; import { CoreTimeUtils } from '@services/utils/time'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreWSFile } from '@services/ws'; import { makeSingleton } from '@singletons'; import { @@ -70,7 +70,7 @@ export class AddonModFeedbackPrefetchHandlerService extends CoreCourseActivityPr * @inheritdoc */ async getIntroFiles(module: CoreCourseAnyModuleData, courseId: number): Promise { - const feedback = await CoreUtils.ignoreErrors(AddonModFeedback.getFeedback(courseId, module.id)); + const feedback = await CorePromiseUtils.ignoreErrors(AddonModFeedback.getFeedback(courseId, module.id)); return this.getIntroFilesFromInstance(module, feedback); } diff --git a/src/addons/mod/feedback/services/handlers/push-click.ts b/src/addons/mod/feedback/services/handlers/push-click.ts index d6ae6a84093..a61ca33a481 100644 --- a/src/addons/mod/feedback/services/handlers/push-click.ts +++ b/src/addons/mod/feedback/services/handlers/push-click.ts @@ -17,7 +17,7 @@ import { CoreCourseHelper } from '@features/course/services/course-helper'; import { CorePushNotificationsClickHandler } from '@features/pushnotifications/services/push-delegate'; import { CorePushNotificationsNotificationBasicData } from '@features/pushnotifications/services/pushnotifications'; import { CoreUrl } from '@singletons/url'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { makeSingleton } from '@singletons'; import { AddonModFeedbackHelper } from '../feedback-helper'; diff --git a/src/addons/mod/folder/components/index/index.ts b/src/addons/mod/folder/components/index/index.ts index a9baa74f8af..71b4559be79 100644 --- a/src/addons/mod/folder/components/index/index.ts +++ b/src/addons/mod/folder/components/index/index.ts @@ -21,7 +21,7 @@ import { CoreNavigator } from '@services/navigator'; import { Md5 } from 'ts-md5'; import { AddonModFolder, AddonModFolderFolder } from '../../services/folder'; import { AddonModFolderFolderFormattedData, AddonModFolderHelper } from '../../services/folder-helper'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { ADDON_MOD_FOLDER_COMPONENT, ADDON_MOD_FOLDER_PAGE_NAME } from '../../constants'; /** @@ -121,7 +121,7 @@ export class AddonModFolderIndexComponent extends CoreCourseModuleMainResourceCo * @inheritdoc */ protected async logActivity(): Promise { - await CoreUtils.ignoreErrors(AddonModFolder.logView(this.module.instance)); + await CorePromiseUtils.ignoreErrors(AddonModFolder.logView(this.module.instance)); this.analyticsLogEvent('mod_folder_view_folder'); } diff --git a/src/addons/mod/folder/services/folder.ts b/src/addons/mod/folder/services/folder.ts index ca36e2985f7..272565c56ec 100644 --- a/src/addons/mod/folder/services/folder.ts +++ b/src/addons/mod/folder/services/folder.ts @@ -15,14 +15,14 @@ import { Injectable } from '@angular/core'; import { CoreError } from '@classes/errors/error'; import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site'; -import { CoreSite } from '@classes/sites/site'; import { CoreCourse } from '@features/course/services/course'; import { CoreCourseLogHelper } from '@features/course/services/log-helper'; import { CoreSites, CoreSitesCommonWSOptions } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreWSExternalFile, CoreWSExternalWarning } from '@services/ws'; import { makeSingleton, Translate } from '@singletons'; import { ADDON_MOD_FOLDER_COMPONENT } from '../constants'; +import { CoreCacheUpdateFrequency } from '@/core/constants'; /** * Service that provides some features for folder. @@ -67,7 +67,7 @@ export class AddonModFolderProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getFolderCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, component: ADDON_MOD_FOLDER_COMPONENT, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), }; @@ -106,7 +106,7 @@ export class AddonModFolderProvider { promises.push(this.invalidateFolderData(courseId, siteId)); promises.push(CoreCourse.invalidateModule(moduleId, siteId)); - await CoreUtils.allPromises(promises); + await CorePromiseUtils.allPromises(promises); } /** diff --git a/src/addons/mod/forum/classes/forum-discussions-source.ts b/src/addons/mod/forum/classes/forum-discussions-source.ts index f94712b52c2..079cd4be1f8 100644 --- a/src/addons/mod/forum/classes/forum-discussions-source.ts +++ b/src/addons/mod/forum/classes/forum-discussions-source.ts @@ -16,7 +16,7 @@ import { Params } from '@angular/router'; import { CoreRoutedItemsManagerSource } from '@classes/items-management/routed-items-manager-source'; import { CoreUser } from '@features/user/services/user'; import { CoreGroupInfo, CoreGroups } from '@services/groups'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreWSError } from '@classes/errors/wserror'; import { AddonModForum, AddonModForumCanAddDiscussion, @@ -27,6 +27,7 @@ import { import { AddonModForumOffline, AddonModForumOfflineDiscussion } from '../services/forum-offline'; import { ADDON_MOD_FORUM_DISCUSSIONS_PER_PAGE, AddonModForumType } from '../constants'; import { CoreSites } from '@services/sites'; +import { CorePromiseUtils } from '@singletons/promise-utils'; export class AddonModForumDiscussionsSource extends CoreRoutedItemsManagerSource { @@ -157,7 +158,7 @@ export class AddonModForumDiscussionsSource extends CoreRoutedItemsManagerSource async loadGroupInfo(forumId: number): Promise { [this.groupInfo, this.allPartsPermissions] = await Promise.all([ CoreGroups.getActivityGroupInfo(this.CM_ID, false), - CoreUtils.ignoreErrors(AddonModForum.canAddDiscussionToAll(forumId, { cmId: this.CM_ID })), + CorePromiseUtils.ignoreErrors(AddonModForum.canAddDiscussionToAll(forumId, { cmId: this.CM_ID })), ]); this.supportsChangeGroup = AddonModForum.isGetDiscussionPostsAvailable(); @@ -245,7 +246,7 @@ export class AddonModForumDiscussionsSource extends CoreRoutedItemsManagerSource canLoadMore = response.canLoadMore; this.errorLoadingDiscussions = false; } catch (error) { - if (page > 0 || CoreUtils.isWebServiceError(error)) { + if (page > 0 || CoreWSError.isWebServiceError(error)) { throw error; } diff --git a/src/addons/mod/forum/components/index/index.ts b/src/addons/mod/forum/components/index/index.ts index adcbd68d143..961a9fd9d02 100644 --- a/src/addons/mod/forum/components/index/index.ts +++ b/src/addons/mod/forum/components/index/index.ts @@ -74,7 +74,7 @@ import { CoreLoadings } from '@services/loadings'; @Component({ selector: 'addon-mod-forum-index', templateUrl: 'index.html', - styleUrls: ['index.scss'], + styleUrl: 'index.scss', }) export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityComponent implements OnInit, AfterViewInit, OnDestroy { diff --git a/src/addons/mod/forum/components/post/post.ts b/src/addons/mod/forum/components/post/post.ts index 88acd59900e..04a356ec545 100644 --- a/src/addons/mod/forum/components/post/post.ts +++ b/src/addons/mod/forum/components/post/post.ts @@ -45,7 +45,7 @@ import { CoreSync } from '@services/sync'; import { CoreText } from '@singletons/text'; import { AddonModForumHelper } from '../../services/forum-helper'; import { AddonModForumOffline } from '../../services/forum-offline'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreFileUtils } from '@singletons/file-utils'; import { CoreRatingInfo } from '@features/rating/services/rating'; import { CoreForms } from '@singletons/form'; import { CoreFileEntry, CoreFileHelper } from '@services/file-helper'; @@ -58,6 +58,8 @@ import { toBoolean } from '@/core/transforms/boolean'; import { CorePopovers } from '@services/popovers'; import { CoreLoadings } from '@services/loadings'; import { CoreWSFile } from '@services/ws'; +import { CorePromiseUtils } from '@singletons/promise-utils'; +import { CoreWSError } from '@classes/errors/wserror'; /** * Components that shows a discussion post, its attachments and the action buttons allowed (reply, etc.). @@ -65,7 +67,7 @@ import { CoreWSFile } from '@services/ws'; @Component({ selector: 'addon-mod-forum-post', templateUrl: 'post.html', - styleUrls: ['post.scss'], + styleUrl: 'post.scss', }) export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges { @@ -504,7 +506,7 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges } // Use prepare post for edition to avoid re-uploading all files. - let filesToKeep = files.filter((file): file is CoreWSFile => !CoreUtils.isFileEntry(file)); + let filesToKeep = files.filter((file): file is CoreWSFile => !CoreFileUtils.isFileEntry(file)); let removedFiles: { filepath: string; filename: string }[] | undefined; if (previousAttachments.length && !filesToKeep.length) { @@ -553,7 +555,7 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges return { attachments, saveOffline: false }; } catch (error) { // Cannot upload them in online, save them in offline. - if (!this.forum.id || CoreUtils.isWebServiceError(error)) { + if (!this.forum.id || CoreWSError.isWebServiceError(error)) { // Cannot store them in offline. Reject. throw error; } @@ -599,7 +601,7 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges })); } - await CoreUtils.ignoreErrors(Promise.all(promises)); + await CorePromiseUtils.ignoreErrors(Promise.all(promises)); // Reset data. this.setFormData(); diff --git a/src/addons/mod/forum/forum.module.ts b/src/addons/mod/forum/forum.module.ts index 8dfe7722520..648108b5a0e 100644 --- a/src/addons/mod/forum/forum.module.ts +++ b/src/addons/mod/forum/forum.module.ts @@ -37,7 +37,7 @@ import { CoreTagAreaDelegate } from '@features/tag/services/tag-area-delegate'; import { AddonModForumTagAreaHandler } from './services/handlers/tag-area'; import { CorePushNotificationsDelegate } from '@features/pushnotifications/services/push-delegate'; import { AddonModForumPushClickHandler } from './services/handlers/push-click'; -import { COURSE_CONTENTS_PATH } from '@features/course/constants'; +import { CORE_COURSE_CONTENTS_PATH } from '@features/course/constants'; import { CoreCourseHelper } from '@features/course/services/course-helper'; import { ADDON_MOD_FORUM_COMPONENT, ADDON_MOD_FORUM_PAGE_NAME, ADDON_MOD_FORUM_SEARCH_PAGE_NAME } from './constants'; @@ -58,12 +58,12 @@ const mainMenuRoutes: Routes = [ ...conditionalRoutes( [ { - path: `${COURSE_CONTENTS_PATH}/${ADDON_MOD_FORUM_PAGE_NAME}/new/:timeCreated`, + path: `${CORE_COURSE_CONTENTS_PATH}/${ADDON_MOD_FORUM_PAGE_NAME}/new/:timeCreated`, loadChildren: () => import('./forum-new-discussion-lazy.module'), data: { discussionsPathPrefix: `${ADDON_MOD_FORUM_PAGE_NAME}/` }, }, { - path: `${COURSE_CONTENTS_PATH}/${ADDON_MOD_FORUM_PAGE_NAME}/:discussionId`, + path: `${CORE_COURSE_CONTENTS_PATH}/${ADDON_MOD_FORUM_PAGE_NAME}/:discussionId`, loadChildren: () => import('./forum-discussion-lazy.module'), data: { discussionsPathPrefix: `${ADDON_MOD_FORUM_PAGE_NAME}/` }, }, diff --git a/src/addons/mod/forum/pages/discussion/discussion.ts b/src/addons/mod/forum/pages/discussion/discussion.ts index 111b0abfb2b..df9779cb470 100644 --- a/src/addons/mod/forum/pages/discussion/discussion.ts +++ b/src/addons/mod/forum/pages/discussion/discussion.ts @@ -29,7 +29,7 @@ import { CoreNavigator } from '@services/navigator'; import { CoreScreen } from '@services/screen'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { NgZone, Translate } from '@singletons'; import { CoreDom } from '@singletons/dom'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; @@ -62,6 +62,8 @@ import { import { CoreCourseContentsPage } from '@features/course/pages/contents/contents'; import { CoreToasts } from '@services/toasts'; import { CoreLoadings } from '@services/loadings'; +import { CorePromiseUtils } from '@singletons/promise-utils'; +import { CoreObject } from '@singletons/object'; type SortType = 'flat-newest' | 'flat-oldest' | 'nested'; @@ -73,7 +75,7 @@ type Post = AddonModForumPost & { children?: Post[] }; @Component({ selector: 'page-addon-mod-forum-discussion', templateUrl: 'discussion.html', - styleUrls: ['discussion.scss'], + styleUrl: 'discussion.scss', }) export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDestroy, CanLeave { @@ -414,7 +416,7 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes try { if (sync) { // Try to synchronize the forum. - await CoreUtils.ignoreErrors(this.syncDiscussion(!!showErrors)); + await CorePromiseUtils.ignoreErrors(this.syncDiscussion(!!showErrors)); } const response = await AddonModForum.getDiscussionPosts(this.discussionId, { cmId: this.cmId }); @@ -462,7 +464,7 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes await Promise.all(convertPromises); // Convert back to array. - onlinePosts = CoreUtils.objectToArray(onlinePostsMap); + onlinePosts = CoreObject.toArray(onlinePostsMap); let posts = offlineReplies.concat(onlinePosts); @@ -689,7 +691,7 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes this.forumId && promises.push(AddonModForum.invalidateAccessInformation(this.forumId)); this.forumId && promises.push(AddonModForum.invalidateCanAddDiscussion(this.forumId)); - await CoreUtils.ignoreErrors(CoreUtils.allPromises(promises)); + await CorePromiseUtils.allPromisesIgnoringErrors(promises); await this.fetchPosts(sync, showErrors); } @@ -872,7 +874,7 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes * @param logAnalytics Whether to log analytics too or not. */ protected async logDiscussionView(logAnalytics = false): Promise { - await CoreUtils.ignoreErrors(AddonModForum.logDiscussionView(this.discussionId, this.forumId || -1)); + await CorePromiseUtils.ignoreErrors(AddonModForum.logDiscussionView(this.discussionId, this.forumId || -1)); if (logAnalytics) { CoreAnalytics.logEvent({ diff --git a/src/addons/mod/forum/pages/new-discussion/new-discussion.ts b/src/addons/mod/forum/pages/new-discussion/new-discussion.ts index 2c0c3c18fb5..ab009aa9fad 100644 --- a/src/addons/mod/forum/pages/new-discussion/new-discussion.ts +++ b/src/addons/mod/forum/pages/new-discussion/new-discussion.ts @@ -31,7 +31,7 @@ import { CoreDomUtils } from '@services/utils/dom'; import { Translate } from '@singletons'; import { CoreSync } from '@services/sync'; import { AddonModForumDiscussionOptions, AddonModForumOffline } from '@addons/mod/forum/services/forum-offline'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { AddonModForumHelper } from '@addons/mod/forum/services/forum-helper'; import { CoreFileUploader } from '@features/fileuploader/services/fileuploader'; import { CoreText } from '@singletons/text'; @@ -53,6 +53,7 @@ import { } from '../../constants'; import { CoreCourseContentsPage } from '@features/course/pages/contents/contents'; import { CoreLoadings } from '@services/loadings'; +import { CorePromiseUtils } from '@singletons/promise-utils'; type NewDiscussionData = { subject: string; @@ -70,7 +71,7 @@ type NewDiscussionData = { @Component({ selector: 'page-addon-mod-forum-new-discussion', templateUrl: 'new-discussion.html', - styleUrls: ['new-discussion.scss'], + styleUrl: 'new-discussion.scss', }) export class AddonModForumNewDiscussionPage implements OnInit, OnDestroy, CanLeave { @@ -242,7 +243,7 @@ export class AddonModForumNewDiscussionPage implements OnInit, OnDestroy, CanLea // Use the canAddDiscussion WS to check if the user can add attachments and pin discussions. promises.push( - CoreUtils.ignoreErrors( + CorePromiseUtils.ignoreErrors( AddonModForum.instance .canAddDiscussionToAll(this.forumId, { cmId: this.cmId }) .then((response) => { @@ -611,7 +612,7 @@ export class AddonModForumNewDiscussionPage implements OnInit, OnDestroy, CanLea promises.push(AddonModForumOffline.deleteNewDiscussion(this.forumId, this.timeCreated)); promises.push( - CoreUtils.ignoreErrors( + CorePromiseUtils.ignoreErrors( AddonModForumHelper.deleteNewDiscussionStoredFiles(this.forumId, this.timeCreated), ), ); diff --git a/src/addons/mod/forum/pages/search/search.ts b/src/addons/mod/forum/pages/search/search.ts index 89c701de278..ddf2e48c55b 100644 --- a/src/addons/mod/forum/pages/search/search.ts +++ b/src/addons/mod/forum/pages/search/search.ts @@ -28,8 +28,9 @@ import { CoreNavigator } from '@services/navigator'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreUrl } from '@singletons/url'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { Translate } from '@singletons'; +import { CorePromiseUtils } from '@singletons/promise-utils'; @Component({ selector: 'page-addon-mod-forum-search', @@ -104,7 +105,7 @@ export class AddonModForumSearchPage implements OnInit { await CoreDomUtils.showOperationModals('core.searching', true, async () => { await this.resultsSource.reload(); - await CoreUtils.ignoreErrors( + await CorePromiseUtils.ignoreErrors( CoreSearchGlobalSearch.logViewResults(this.resultsSource.getQuery(), this.resultsSource.getFilters()), ); diff --git a/src/addons/mod/forum/services/forum-helper.ts b/src/addons/mod/forum/services/forum-helper.ts index 29d822ae4c4..783fff12d66 100644 --- a/src/addons/mod/forum/services/forum-helper.ts +++ b/src/addons/mod/forum/services/forum-helper.ts @@ -20,7 +20,7 @@ import { CoreNetwork } from '@services/network'; import { CoreFile } from '@services/file'; import { CoreSites } from '@services/sites'; import { CoreTimeUtils } from '@services/utils/time'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { makeSingleton, Translate } from '@singletons'; import { AddonModForum, @@ -32,6 +32,8 @@ import { import { AddonModForumDiscussionOptions, AddonModForumOffline, AddonModForumOfflineReply } from './forum-offline'; import { CoreFileEntry } from '@services/file-helper'; import { ADDON_MOD_FORUM_ALL_GROUPS, ADDON_MOD_FORUM_COMPONENT } from '../constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; +import { CoreWSError } from '@classes/errors/wserror'; /** * Service that provides some features for forums. @@ -106,7 +108,7 @@ export class AddonModForumHelperProvider { try { await Promise.all(promises); } catch (error) { - if (CoreUtils.isWebServiceError(error)) { + if (CoreWSError.isWebServiceError(error)) { throw error; } @@ -160,7 +162,7 @@ export class AddonModForumHelperProvider { if (errors.length == groupIds.length) { // All requests have failed. for (let i = 0; i < errors.length; i++) { - if (CoreUtils.isWebServiceError(errors[i]) || (attachments && attachments.length > 0)) { + if (CoreWSError.isWebServiceError(errors[i]) || (attachments && attachments.length > 0)) { // The WebService has thrown an error or offline not supported, reject. throw errors[i]; } @@ -224,7 +226,7 @@ export class AddonModForumHelperProvider { // Get user data. promises.push( - CoreUtils.ignoreErrors( + CorePromiseUtils.ignoreErrors( CoreUser.instance .getProfile(offlineReply.userid, offlineReply.courseid, true) .then(user => { @@ -254,8 +256,8 @@ export class AddonModForumHelperProvider { async deleteNewDiscussionStoredFiles(forumId: number, timecreated: number, siteId?: string): Promise { const folderPath = await AddonModForumOffline.getNewDiscussionFolder(forumId, timecreated, siteId); - // Ignore any errors, CoreFileProvider.removeDir fails if folder doesn't exist. - await CoreUtils.ignoreErrors(CoreFile.removeDir(folderPath)); + // Ignore any errors, CoreFile.removeDir fails if folder doesn't exist. + await CorePromiseUtils.ignoreErrors(CoreFile.removeDir(folderPath)); } /** @@ -270,8 +272,8 @@ export class AddonModForumHelperProvider { async deleteReplyStoredFiles(forumId: number, postId: number, siteId?: string, userId?: number): Promise { const folderPath = await AddonModForumOffline.getReplyFolder(forumId, postId, siteId, userId); - // Ignore any errors, CoreFileProvider.removeDir fails if folder doesn't exist. - await CoreUtils.ignoreErrors(CoreFile.removeDir(folderPath)); + // Ignore any errors, CoreFile.removeDir fails if folder doesn't exist. + await CorePromiseUtils.ignoreErrors(CoreFile.removeDir(folderPath)); } /** diff --git a/src/addons/mod/forum/services/forum-sync.ts b/src/addons/mod/forum/services/forum-sync.ts index d7936d0a32c..9868c9cb71d 100644 --- a/src/addons/mod/forum/services/forum-sync.ts +++ b/src/addons/mod/forum/services/forum-sync.ts @@ -22,7 +22,7 @@ import { CoreNetwork } from '@services/network'; import { CoreGroups } from '@services/groups'; import { CoreSites } from '@services/sites'; import { CoreSync, CoreSyncResult } from '@services/sync'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { makeSingleton, Translate } from '@singletons'; import { CoreEvents } from '@singletons/events'; import { @@ -38,6 +38,8 @@ import { ADDON_MOD_FORUM_COMPONENT, ADDON_MOD_FORUM_MANUAL_SYNCED, } from '../constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; +import { CoreWSError } from '@classes/errors/wserror'; declare module '@singletons/events' { @@ -224,12 +226,12 @@ export class AddonModForumSyncProvider extends CoreCourseActivitySyncBaseProvide // Sync offline logs. const syncDiscussions = async (): Promise => { - await CoreUtils.ignoreErrors( + await CorePromiseUtils.ignoreErrors( CoreCourseLogHelper.syncActivity(ADDON_MOD_FORUM_COMPONENT, forumId, siteId), ); // Get offline responses to be sent. - const discussions = await CoreUtils.ignoreErrors( + const discussions = await CorePromiseUtils.ignoreErrors( AddonModForumOffline.getNewDiscussions(forumId, siteId, userId), [] as AddonModForumOfflineDiscussion[], ); @@ -272,7 +274,7 @@ export class AddonModForumSyncProvider extends CoreCourseActivitySyncBaseProvide if (errors.length === groupIds.length) { // All requests have failed, reject if errors were not returned by WS. for (const error of errors) { - if (!CoreUtils.isWebServiceError(error)) { + if (!CoreWSError.isWebServiceError(error)) { throw error; } } @@ -298,11 +300,11 @@ export class AddonModForumSyncProvider extends CoreCourseActivitySyncBaseProvide AddonModForum.invalidateCanAddDiscussion(forumId, siteId), ]; - await CoreUtils.ignoreErrors(Promise.all(promises)); + await CorePromiseUtils.ignoreErrors(Promise.all(promises)); } // Sync finished, set sync time. - await CoreUtils.ignoreErrors(this.setSyncTime(syncId, siteId)); + await CorePromiseUtils.ignoreErrors(this.setSyncTime(syncId, siteId)); return result; }; @@ -350,7 +352,7 @@ export class AddonModForumSyncProvider extends CoreCourseActivitySyncBaseProvide } }); - await CoreUtils.allPromises(promises); + await CorePromiseUtils.allPromises(promises); return { updated, warnings }; } @@ -365,7 +367,7 @@ export class AddonModForumSyncProvider extends CoreCourseActivitySyncBaseProvide */ async syncForumReplies(forumId: number, userId?: number, siteId?: string): Promise { // Get offline forum replies to be sent. - const replies = await CoreUtils.ignoreErrors( + const replies = await CorePromiseUtils.ignoreErrors( AddonModForumOffline.getForumReplies(forumId, siteId, userId), [] as AddonModForumOfflineReply[], ); @@ -457,7 +459,7 @@ export class AddonModForumSyncProvider extends CoreCourseActivitySyncBaseProvide // Get offline responses to be sent. const syncReplies = async () => { - const replies = await CoreUtils.ignoreErrors( + const replies = await CorePromiseUtils.ignoreErrors( AddonModForumOffline.getDiscussionReplies(discussionId, siteId, userId), [] as AddonModForumOfflineReply[], ); @@ -489,7 +491,7 @@ export class AddonModForumSyncProvider extends CoreCourseActivitySyncBaseProvide await this.deleteReply(forumId, reply.postid, siteId, userId); } catch (error) { - if (!CoreUtils.isWebServiceError(error)) { + if (!CoreWSError.isWebServiceError(error)) { throw error; } @@ -515,10 +517,10 @@ export class AddonModForumSyncProvider extends CoreCourseActivitySyncBaseProvide invalidationPromises.push(AddonModForum.invalidateDiscussionPosts(discussionId, forumId, siteId)); - await CoreUtils.ignoreErrors(CoreUtils.allPromises(invalidationPromises)); + await CorePromiseUtils.allPromisesIgnoringErrors(invalidationPromises); // Sync finished, set sync time. - await CoreUtils.ignoreErrors(this.setSyncTime(syncId, siteId)); + await CorePromiseUtils.ignoreErrors(this.setSyncTime(syncId, siteId)); // All done, return the warnings. return result; @@ -539,7 +541,7 @@ export class AddonModForumSyncProvider extends CoreCourseActivitySyncBaseProvide protected async deleteNewDiscussion(forumId: number, timecreated: number, siteId?: string, userId?: number): Promise { await Promise.all([ AddonModForumOffline.deleteNewDiscussion(forumId, timecreated, siteId, userId), - CoreUtils.ignoreErrors( + CorePromiseUtils.ignoreErrors( AddonModForumHelper.deleteNewDiscussionStoredFiles(forumId, timecreated, siteId), ), ]); @@ -557,7 +559,7 @@ export class AddonModForumSyncProvider extends CoreCourseActivitySyncBaseProvide protected async deleteReply(forumId: number, postId: number, siteId?: string, userId?: number): Promise { await Promise.all([ AddonModForumOffline.deleteReply(postId, siteId, userId), - CoreUtils.ignoreErrors(AddonModForumHelper.deleteReplyStoredFiles(forumId, postId, siteId, userId)), + CorePromiseUtils.ignoreErrors(AddonModForumHelper.deleteReplyStoredFiles(forumId, postId, siteId, userId)), ]); } @@ -648,7 +650,7 @@ export const AddonModForumSync = makeSingleton(AddonModForumSyncProvider); export type AddonModForumSyncResult = CoreSyncResult; /** - * Data passed to AUTO_SYNCED event. + * Data passed to ADDON_MOD_FORUM_AUTO_SYNCED event. */ export type AddonModForumAutoSyncData = { forumId: number; @@ -658,7 +660,7 @@ export type AddonModForumAutoSyncData = { }; /** - * Data passed to MANUAL_SYNCED event. + * Data passed to ADDON_MOD_FORUM_MANUAL_SYNCED event. */ export type AddonModForumManualSyncData = { forumId: number; diff --git a/src/addons/mod/forum/services/forum.ts b/src/addons/mod/forum/services/forum.ts index a7a60d76d8f..290c9ae248a 100644 --- a/src/addons/mod/forum/services/forum.ts +++ b/src/addons/mod/forum/services/forum.ts @@ -25,7 +25,7 @@ import { CoreFileEntry } from '@services/file-helper'; import { CoreGroups } from '@services/groups'; import { CoreSitesCommonWSOptions, CoreSites, CoreSitesReadingStrategy } from '@services/sites'; import { CoreUrl } from '@singletons/url'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { CoreStatusWithWarningsWSResponse, CoreWSExternalFile, @@ -49,6 +49,10 @@ import { AddonModForumSortorder, AddonModForumType, } from '../constants'; +import { CoreCacheUpdateFrequency } from '@/core/constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; +import { CoreWSError } from '@classes/errors/wserror'; +import { CoreObject } from '@singletons/object'; declare module '@singletons/events' { @@ -218,7 +222,7 @@ export class AddonModForumProvider { message: message, // eslint-disable-next-line max-len - options: CoreUtils.objectToArrayOfObjects( + options: CoreObject.toArrayOfObjects( options || {}, 'name', 'value', @@ -429,7 +433,7 @@ export class AddonModForumProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getForumDataCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, component: ADDON_MOD_FORUM_COMPONENT, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), }; @@ -458,7 +462,7 @@ export class AddonModForumProvider { }; const preSets = { cacheKey: this.getDiscussionPostDataCacheKey(forumId, discussionId, postId), - updateFrequency: CoreSite.FREQUENCY_USUALLY, + updateFrequency: CoreCacheUpdateFrequency.USUALLY, component: ADDON_MOD_FORUM_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. @@ -732,7 +736,7 @@ export class AddonModForumProvider { let sortOrderValue: number | null = null; if (this.isDiscussionListSortingAvailable()) { - const preferenceValue = await CoreUtils.ignoreErrors( + const preferenceValue = await CorePromiseUtils.ignoreErrors( CoreUser.getUserPreference(ADDON_MOD_FORUM_PREFERENCE_SORTORDER), ); @@ -930,7 +934,7 @@ export class AddonModForumProvider { promises.push(this.invalidateDiscussionPosts(discussion.discussion, forum.id)); }); - return CoreUtils.allPromises(promises); + return CorePromiseUtils.allPromises(promises); }), ); }); @@ -939,7 +943,7 @@ export class AddonModForumProvider { promises.push(CoreUser.invalidateUserPreference(ADDON_MOD_FORUM_PREFERENCE_SORTORDER)); } - return CoreUtils.allPromises(promises); + return CorePromiseUtils.allPromises(promises); } /** @@ -971,7 +975,7 @@ export class AddonModForumProvider { promises.push(site.invalidateWsCacheForKeyStartingWith(this.getForumDiscussionDataCacheKey(forumId, discussionId))); } - await CoreUtils.allPromises(promises); + await CorePromiseUtils.allPromises(promises); } /** @@ -1111,7 +1115,7 @@ export class AddonModForumProvider { return true; } catch (error) { - if (allowOffline && !CoreUtils.isWebServiceError(error)) { + if (allowOffline && !CoreWSError.isWebServiceError(error)) { // Couldn't connect to server, store in offline. return storeOffline(); } else { @@ -1144,7 +1148,7 @@ export class AddonModForumProvider { subject: subject, message: message, - options: CoreUtils.objectToArrayOfObjects< + options: CoreObject.toArrayOfObjects< AddonModForumAddDiscussionPostWSOptionsArray[0], AddonModForumAddDiscussionPostWSOptionsObject >( @@ -1274,7 +1278,7 @@ export class AddonModForumProvider { } }); - CoreUser.storeUsers(CoreUtils.objectToArray(users)); + CoreUser.storeUsers(CoreObject.toArray(users)); } /** @@ -1329,7 +1333,7 @@ export class AddonModForumProvider { subject: subject, message: message, - options: CoreUtils.objectToArrayOfObjects< + options: CoreObject.toArrayOfObjects< AddonModForumUpdateDiscussionPostWSOptionsArray[0], AddonModForumUpdateDiscussionPostWSOptionsObject >( diff --git a/src/addons/mod/forum/services/handlers/prefetch.ts b/src/addons/mod/forum/services/handlers/prefetch.ts index d9aa3750044..c9eaebfd7b0 100644 --- a/src/addons/mod/forum/services/handlers/prefetch.ts +++ b/src/addons/mod/forum/services/handlers/prefetch.ts @@ -21,7 +21,7 @@ import { CoreWSFile } from '@services/ws'; import { CoreCourse, CoreCourseAnyModuleData, CoreCourseCommonModWSOptions } from '@features/course/services/course'; import { CoreUser } from '@features/user/services/user'; import { CoreGroups, CoreGroupsProvider } from '@services/groups'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { AddonModForumSync, AddonModForumSyncResult } from '../forum-sync'; import { makeSingleton } from '@singletons'; import { CoreCourses } from '@features/courses/services/courses'; @@ -251,7 +251,7 @@ export class AddonModForumPrefetchHandlerService extends CoreCourseActivityPrefe promises.push(AddonModForum.getAccessInformation(forum.id, modOptions)); // Get course data, needed to determine upload max size if it's configured to be course limit. - promises.push(CoreUtils.ignoreErrors(CoreCourses.getCourseByField('id', courseId, siteId))); + promises.push(CorePromiseUtils.ignoreErrors(CoreCourses.getCourseByField('id', courseId, siteId))); await Promise.all(promises); } @@ -282,7 +282,7 @@ export class AddonModForumPrefetchHandlerService extends CoreCourseActivityPrefe if (mode !== CoreGroupsProvider.SEPARATEGROUPS && mode !== CoreGroupsProvider.VISIBLEGROUPS) { // Activity doesn't use groups. Prefetch canAddDiscussionToAll to determine if user can pin/attach. - await CoreUtils.ignoreErrors(AddonModForum.canAddDiscussionToAll(forum.id, options)); + await CorePromiseUtils.ignoreErrors(AddonModForum.canAddDiscussionToAll(forum.id, options)); return; } @@ -291,11 +291,11 @@ export class AddonModForumPrefetchHandlerService extends CoreCourseActivityPrefe const result = await CoreGroups.getActivityAllowedGroups(forum.cmid, undefined, siteId); await Promise.all( result.groups.map( - async (group) => CoreUtils.ignoreErrors( + async (group) => CorePromiseUtils.ignoreErrors( AddonModForum.canAddDiscussion(forum.id, group.id, options), ), ).concat( - CoreUtils.ignoreErrors(AddonModForum.canAddDiscussionToAll(forum.id, options)), + CorePromiseUtils.ignoreErrors(AddonModForum.canAddDiscussionToAll(forum.id, options)), ), ); } catch (error) { diff --git a/src/addons/mod/forum/services/handlers/push-click.ts b/src/addons/mod/forum/services/handlers/push-click.ts index 1b17316fc6a..a2b0be78eb6 100644 --- a/src/addons/mod/forum/services/handlers/push-click.ts +++ b/src/addons/mod/forum/services/handlers/push-click.ts @@ -20,11 +20,12 @@ import { CoreNavigator } from '@services/navigator'; import { CorePushNotificationsClickHandler } from '@features/pushnotifications/services/push-delegate'; import { CorePushNotificationsNotificationBasicData } from '@features/pushnotifications/services/pushnotifications'; import { CoreUrl } from '@singletons/url'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { makeSingleton } from '@singletons'; import { isSafeNumber } from '@/core/utils/types'; import { ADDON_MOD_FORUM_PAGE_NAME } from '../../constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Handler for forum push notifications clicks. @@ -75,7 +76,7 @@ export class AddonModForumPushClickHandlerService implements CorePushNotificatio pageParams.postId = Number(data.postid || contextUrlParams.urlHash.replace('p', '')); } - await CoreUtils.ignoreErrors( + await CorePromiseUtils.ignoreErrors( AddonModForum.invalidateDiscussionPosts(discussionId, undefined, notification.site), ); diff --git a/src/addons/mod/forum/services/handlers/tag-area.ts b/src/addons/mod/forum/services/handlers/tag-area.ts index 6681bf05e3d..44824d1061f 100644 --- a/src/addons/mod/forum/services/handlers/tag-area.ts +++ b/src/addons/mod/forum/services/handlers/tag-area.ts @@ -15,7 +15,6 @@ import { Injectable, Type } from '@angular/core'; import { CoreTagAreaHandler } from '@features/tag/services/tag-area-delegate'; -import { CoreTagFeedComponent } from '@features/tag/components/feed/feed'; import { CoreTagHelper, CoreTagFeedElement } from '@features/tag/services/tag-helper'; import { makeSingleton } from '@singletons'; @@ -45,7 +44,9 @@ export class AddonModForumTagAreaHandlerService implements CoreTagAreaHandler { /** * @inheritdoc */ - getComponent(): Type | Promise> { + async getComponent(): Promise> { + const { CoreTagFeedComponent } = await import('@features/tag/components/feed/feed'); + return CoreTagFeedComponent; } diff --git a/src/addons/mod/glossary/components/index/index.ts b/src/addons/mod/glossary/components/index/index.ts index ec6f8bd570a..6e9a4df0702 100644 --- a/src/addons/mod/glossary/components/index/index.ts +++ b/src/addons/mod/glossary/components/index/index.ts @@ -47,7 +47,6 @@ import { AddonModGlossaryOfflineEntry } from '../../services/glossary-offline'; import { AddonModGlossaryAutoSyncedData, AddonModGlossarySyncResult, - GLOSSARY_AUTO_SYNCED, } from '../../services/glossary-sync'; import { AddonModGlossaryPrefetchHandler } from '../../services/handlers/prefetch'; import { CoreTime } from '@singletons/time'; @@ -57,6 +56,7 @@ import { ADDON_MOD_GLOSSARY_ENTRY_DELETED, ADDON_MOD_GLOSSARY_ENTRY_UPDATED, ADDON_MOD_GLOSSARY_PAGE_NAME, + GLOSSARY_AUTO_SYNCED, } from '../../constants'; import { CorePopovers } from '@services/popovers'; @@ -66,7 +66,7 @@ import { CorePopovers } from '@services/popovers'; @Component({ selector: 'addon-mod-glossary-index', templateUrl: 'addon-mod-glossary-index.html', - styleUrls: ['index.scss'], + styleUrl: 'index.scss', }) export class AddonModGlossaryIndexComponent extends CoreCourseModuleMainActivityComponent implements OnInit, AfterViewInit, OnDestroy { diff --git a/src/addons/mod/glossary/constants.ts b/src/addons/mod/glossary/constants.ts index e7eae3be282..c51aad210a5 100644 --- a/src/addons/mod/glossary/constants.ts +++ b/src/addons/mod/glossary/constants.ts @@ -19,5 +19,7 @@ export const ADDON_MOD_GLOSSARY_ENTRY_ADDED = 'addon_mod_glossary_entry_added'; export const ADDON_MOD_GLOSSARY_ENTRY_UPDATED = 'addon_mod_glossary_entry_updated'; export const ADDON_MOD_GLOSSARY_ENTRY_DELETED = 'addon_mod_glossary_entry_deleted'; +export const GLOSSARY_AUTO_SYNCED = 'addon_mod_glossary_auto_synced'; + export const ADDON_MOD_GLOSSARY_LIMIT_ENTRIES = 25; export const ADDON_MOD_GLOSSARY_LIMIT_CATEGORIES = 10; diff --git a/src/addons/mod/glossary/glossary.module.ts b/src/addons/mod/glossary/glossary.module.ts index e2af50f5922..f38f6d745af 100644 --- a/src/addons/mod/glossary/glossary.module.ts +++ b/src/addons/mod/glossary/glossary.module.ts @@ -16,7 +16,7 @@ import { conditionalRoutes } from '@/app/app-routing.module'; import { APP_INITIALIZER, NgModule } from '@angular/core'; import { Routes } from '@angular/router'; import { CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate'; -import { COURSE_CONTENTS_PATH } from '@features/course/constants'; +import { CORE_COURSE_CONTENTS_PATH } from '@features/course/constants'; import { CoreCourseContentsRoutingModule } from '@features/course/course-contents-routing.module'; import { CoreCourseHelper } from '@features/course/services/course-helper'; import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate'; @@ -52,18 +52,18 @@ const mainMenuRoutes: Routes = [ // Single Activity format navigation. { - path: `${COURSE_CONTENTS_PATH}/${ADDON_MOD_GLOSSARY_PAGE_NAME}/entry/new`, + path: `${CORE_COURSE_CONTENTS_PATH}/${ADDON_MOD_GLOSSARY_PAGE_NAME}/entry/new`, loadChildren: () => import('./glossary-edit-lazy.module'), data: { glossaryPathPrefix: `${ADDON_MOD_GLOSSARY_PAGE_NAME}/` }, }, { - path: `${COURSE_CONTENTS_PATH}/${ADDON_MOD_GLOSSARY_PAGE_NAME}/entry/:entrySlug/edit`, + path: `${CORE_COURSE_CONTENTS_PATH}/${ADDON_MOD_GLOSSARY_PAGE_NAME}/entry/:entrySlug/edit`, loadChildren: () => import('./glossary-edit-lazy.module'), data: { glossaryPathPrefix: `${ADDON_MOD_GLOSSARY_PAGE_NAME}/` }, }, ...conditionalRoutes( [{ - path: `${COURSE_CONTENTS_PATH}/${ADDON_MOD_GLOSSARY_PAGE_NAME}/entry/:entrySlug`, + path: `${CORE_COURSE_CONTENTS_PATH}/${ADDON_MOD_GLOSSARY_PAGE_NAME}/entry/:entrySlug`, loadChildren: () => import('./glossary-entry-lazy.module'), data: { glossaryPathPrefix: `${ADDON_MOD_GLOSSARY_PAGE_NAME}/` }, }], diff --git a/src/addons/mod/glossary/pages/edit/edit.ts b/src/addons/mod/glossary/pages/edit/edit.ts index 4c2aa09fee8..59b62abbc02 100644 --- a/src/addons/mod/glossary/pages/edit/edit.ts +++ b/src/addons/mod/glossary/pages/edit/edit.ts @@ -25,7 +25,7 @@ import { CoreNetwork } from '@services/network'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreText } from '@singletons/text'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreWSError } from '@classes/errors/wserror'; import { Translate } from '@singletons'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreForms } from '@singletons/form'; @@ -522,7 +522,7 @@ class AddonModGlossaryNewFormHandler extends AddonModGlossaryFormHandler { try { onlineAttachments = await this.uploadAttachments(glossary); } catch (error) { - if (CoreUtils.isWebServiceError(error)) { + if (CoreWSError.isWebServiceError(error)) { throw error; } diff --git a/src/addons/mod/glossary/pages/entry/entry.ts b/src/addons/mod/glossary/pages/entry/entry.ts index 1286b4e0055..5c0425525a2 100644 --- a/src/addons/mod/glossary/pages/entry/entry.ts +++ b/src/addons/mod/glossary/pages/entry/entry.ts @@ -27,7 +27,7 @@ import { FileEntry } from '@awesome-cordova-plugins/file/ngx'; import { CoreNavigator } from '@services/navigator'; import { CoreNetwork } from '@services/network'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { Translate } from '@singletons'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { AddonModGlossaryEntriesSource, AddonModGlossaryEntryItem } from '../../classes/glossary-entries-source'; @@ -86,7 +86,7 @@ export class AddonModGlossaryEntryPage implements OnInit, OnDestroy { return; } - await CoreUtils.ignoreErrors(AddonModGlossary.logEntryView(this.onlineEntry.id, this.componentId)); + await CorePromiseUtils.ignoreErrors(AddonModGlossary.logEntryView(this.onlineEntry.id, this.componentId)); this.analyticsLogEvent('mod_glossary_get_entry_by_id', `/mod/glossary/showentry.php?eid=${this.onlineEntry.id}`); }); @@ -184,7 +184,7 @@ export class AddonModGlossaryEntryPage implements OnInit, OnDestroy { ); const glossaryId = this.glossary?.id; - const cancelled = await CoreUtils.promiseFails( + const cancelled = await CorePromiseUtils.promiseFails( CoreDomUtils.showConfirm(Translate.instant('addon.mod_glossary.areyousuredelete')), ); @@ -200,13 +200,13 @@ export class AddonModGlossaryEntryPage implements OnInit, OnDestroy { await AddonModGlossary.deleteEntry(glossaryId, entryId); await Promise.all([ - CoreUtils.ignoreErrors(AddonModGlossary.invalidateEntry(entryId)), - CoreUtils.ignoreErrors(AddonModGlossary.invalidateEntriesByLetter(glossaryId)), - CoreUtils.ignoreErrors(AddonModGlossary.invalidateEntriesByAuthor(glossaryId)), - CoreUtils.ignoreErrors(AddonModGlossary.invalidateEntriesByCategory(glossaryId)), - CoreUtils.ignoreErrors(AddonModGlossary.invalidateEntriesByDate(glossaryId, 'CREATION')), - CoreUtils.ignoreErrors(AddonModGlossary.invalidateEntriesByDate(glossaryId, 'UPDATE')), - CoreUtils.ignoreErrors(this.entries.getSource().invalidateCache(false)), + CorePromiseUtils.ignoreErrors(AddonModGlossary.invalidateEntry(entryId)), + CorePromiseUtils.ignoreErrors(AddonModGlossary.invalidateEntriesByLetter(glossaryId)), + CorePromiseUtils.ignoreErrors(AddonModGlossary.invalidateEntriesByAuthor(glossaryId)), + CorePromiseUtils.ignoreErrors(AddonModGlossary.invalidateEntriesByCategory(glossaryId)), + CorePromiseUtils.ignoreErrors(AddonModGlossary.invalidateEntriesByDate(glossaryId, 'CREATION')), + CorePromiseUtils.ignoreErrors(AddonModGlossary.invalidateEntriesByDate(glossaryId, 'UPDATE')), + CorePromiseUtils.ignoreErrors(this.entries.getSource().invalidateCache(false)), ]); } else if (this.offlineEntry) { const concept = this.offlineEntry.concept; @@ -239,12 +239,12 @@ export class AddonModGlossaryEntryPage implements OnInit, OnDestroy { async doRefresh(refresher?: HTMLIonRefresherElement): Promise { if (this.onlineEntry && this.glossary?.allowcomments && this.onlineEntry.id > 0 && this.commentsEnabled && this.comments) { // Refresh comments asynchronously (without blocking the current promise). - CoreUtils.ignoreErrors(this.comments.doRefresh()); + CorePromiseUtils.ignoreErrors(this.comments.doRefresh()); } try { if (this.onlineEntry) { - await CoreUtils.ignoreErrors(AddonModGlossary.invalidateEntry(this.onlineEntry.id)); + await CorePromiseUtils.ignoreErrors(AddonModGlossary.invalidateEntry(this.onlineEntry.id)); await this.loadOnlineEntry(this.onlineEntry.id); } else if (this.offlineEntry) { const timecreated = Number(this.entrySlug.slice(4)); diff --git a/src/addons/mod/glossary/services/glossary-helper.ts b/src/addons/mod/glossary/services/glossary-helper.ts index e4f9e306731..1067ad6060a 100644 --- a/src/addons/mod/glossary/services/glossary-helper.ts +++ b/src/addons/mod/glossary/services/glossary-helper.ts @@ -16,7 +16,7 @@ import { Injectable } from '@angular/core'; import { FileEntry } from '@awesome-cordova-plugins/file/ngx'; import { CoreFileUploader, CoreFileUploaderStoreFilesResult } from '@features/fileuploader/services/fileuploader'; import { CoreFile } from '@services/file'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { AddonModGlossaryOffline } from './glossary-offline'; import { makeSingleton } from '@singletons'; import { CoreFileEntry } from '@services/file-helper'; @@ -39,7 +39,7 @@ export class AddonModGlossaryHelperProvider { async deleteStoredFiles(glossaryId: number, entryName: string, timeCreated: number, siteId?: string): Promise { const folderPath = await AddonModGlossaryOffline.getEntryFolder(glossaryId, entryName, timeCreated, siteId); - await CoreUtils.ignoreErrors(CoreFile.removeDir(folderPath)); + await CorePromiseUtils.ignoreErrors(CoreFile.removeDir(folderPath)); } /** diff --git a/src/addons/mod/glossary/services/glossary-sync.ts b/src/addons/mod/glossary/services/glossary-sync.ts index 61a6b1f9f2a..faadf0159af 100644 --- a/src/addons/mod/glossary/services/glossary-sync.ts +++ b/src/addons/mod/glossary/services/glossary-sync.ts @@ -22,7 +22,7 @@ import { CoreRatingSync } from '@features/rating/services/rating-sync'; import { CoreNetwork } from '@services/network'; import { CoreSites } from '@services/sites'; import { CoreSync, CoreSyncResult } from '@services/sync'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreWSError } from '@classes/errors/wserror'; import { makeSingleton, Translate } from '@singletons'; import { CoreEvents } from '@singletons/events'; import { AddonModGlossary } from './glossary'; @@ -30,9 +30,8 @@ import { AddonModGlossaryHelper } from './glossary-helper'; import { AddonModGlossaryOffline, AddonModGlossaryOfflineEntry } from './glossary-offline'; import { CoreFileUploader } from '@features/fileuploader/services/fileuploader'; import { CoreFileEntry } from '@services/file-helper'; -import { ADDON_MOD_GLOSSARY_COMPONENT } from '../constants'; - -export const GLOSSARY_AUTO_SYNCED = 'addon_mod_glossary_auto_synced'; +import { ADDON_MOD_GLOSSARY_COMPONENT, GLOSSARY_AUTO_SYNCED } from '../constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Service to sync glossaries. @@ -174,17 +173,17 @@ export class AddonModGlossarySyncProvider extends CoreCourseActivitySyncBaseProv const syncId = this.getGlossarySyncId(glossaryId, userId); // Sync offline logs. - await CoreUtils.ignoreErrors(CoreCourseLogHelper.syncActivity(ADDON_MOD_GLOSSARY_COMPONENT, glossaryId, siteId)); + await CorePromiseUtils.ignoreErrors(CoreCourseLogHelper.syncActivity(ADDON_MOD_GLOSSARY_COMPONENT, glossaryId, siteId)); // Get offline responses to be sent. - const entries = await CoreUtils.ignoreErrors( + const entries = await CorePromiseUtils.ignoreErrors( AddonModGlossaryOffline.getGlossaryOfflineEntries(glossaryId, siteId, userId), [], ); if (!entries.length) { // Nothing to sync. - await CoreUtils.ignoreErrors(this.setSyncTime(syncId, siteId)); + await CorePromiseUtils.ignoreErrors(this.setSyncTime(syncId, siteId)); return result; } else if (!CoreNetwork.isOnline()) { @@ -208,7 +207,7 @@ export class AddonModGlossarySyncProvider extends CoreCourseActivitySyncBaseProv await this.deleteAddEntry(glossaryId, data.concept, data.timecreated, siteId); } catch (error) { - if (!CoreUtils.isWebServiceError(error)) { + if (!CoreWSError.isWebServiceError(error)) { // Couldn't connect to server, reject. throw error; } @@ -235,7 +234,7 @@ export class AddonModGlossarySyncProvider extends CoreCourseActivitySyncBaseProv } // Sync finished, set sync time. - await CoreUtils.ignoreErrors(this.setSyncTime(syncId, siteId)); + await CorePromiseUtils.ignoreErrors(this.setSyncTime(syncId, siteId)); return result; } @@ -256,7 +255,7 @@ export class AddonModGlossarySyncProvider extends CoreCourseActivitySyncBaseProv let updated = false; const warnings: string[] = []; - await CoreUtils.allPromises(results.map(async (result) => { + await CorePromiseUtils.allPromises(results.map(async (result) => { if (result.updated.length) { updated = true; @@ -310,7 +309,7 @@ export class AddonModGlossarySyncProvider extends CoreCourseActivitySyncBaseProv if (entry.attachments.offline) { // Has offline files. - const storedFiles = await CoreUtils.ignoreErrors( + const storedFiles = await CorePromiseUtils.ignoreErrors( AddonModGlossaryHelper.getStoredFiles(glossaryId, entry.concept, entry.timecreated, siteId), [], // Folder not found, no files to add. ); diff --git a/src/addons/mod/glossary/services/glossary.ts b/src/addons/mod/glossary/services/glossary.ts index 715ee95755a..fc204b34e97 100644 --- a/src/addons/mod/glossary/services/glossary.ts +++ b/src/addons/mod/glossary/services/glossary.ts @@ -22,7 +22,7 @@ import { CoreRatingInfo } from '@features/rating/services/rating'; import { CoreTagItem } from '@features/tag/services/tag'; import { CoreNetwork } from '@services/network'; import { CoreSites, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreObject } from '@singletons/object'; import { CoreWSExternalFile, CoreWSExternalWarning } from '@services/ws'; import { makeSingleton, Translate } from '@singletons'; import { CoreEvents } from '@singletons/events'; @@ -37,6 +37,9 @@ import { ADDON_MOD_GLOSSARY_LIMIT_CATEGORIES, ADDON_MOD_GLOSSARY_LIMIT_ENTRIES, } from '../constants'; +import { CoreCacheUpdateFrequency } from '@/core/constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; +import { CoreWSError } from '@classes/errors/wserror'; /** * Service that provides some features for glossaries. @@ -72,7 +75,7 @@ export class AddonModGlossaryProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getCourseGlossariesCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, component: ADDON_MOD_GLOSSARY_COMPONENT, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; @@ -133,7 +136,7 @@ export class AddonModGlossaryProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getEntriesByAuthorCacheKey(glossaryId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + updateFrequency: CoreCacheUpdateFrequency.SOMETIMES, component: ADDON_MOD_GLOSSARY_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. @@ -177,7 +180,7 @@ export class AddonModGlossaryProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getEntriesByCategoryCacheKey(glossaryId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + updateFrequency: CoreCacheUpdateFrequency.SOMETIMES, component: ADDON_MOD_GLOSSARY_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. @@ -247,7 +250,7 @@ export class AddonModGlossaryProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getEntriesByDateCacheKey(glossaryId, order), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + updateFrequency: CoreCacheUpdateFrequency.SOMETIMES, component: ADDON_MOD_GLOSSARY_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. @@ -305,7 +308,7 @@ export class AddonModGlossaryProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getEntriesByLetterCacheKey(glossaryId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + updateFrequency: CoreCacheUpdateFrequency.SOMETIMES, component: ADDON_MOD_GLOSSARY_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. @@ -319,7 +322,7 @@ export class AddonModGlossaryProvider { if (limit === ADDON_MOD_GLOSSARY_LIMIT_ENTRIES) { // Store entries in background, don't block the user for this. - CoreUtils.ignoreErrors(this.storeEntries(glossaryId, result.entries, from, site.getId())); + CorePromiseUtils.ignoreErrors(this.storeEntries(glossaryId, result.entries, from, site.getId())); } return result; @@ -379,7 +382,7 @@ export class AddonModGlossaryProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getEntriesBySearchCacheKey(glossaryId, query, fullSearch), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + updateFrequency: CoreCacheUpdateFrequency.SOMETIMES, component: ADDON_MOD_GLOSSARY_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. @@ -457,7 +460,7 @@ export class AddonModGlossaryProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getCategoriesCacheKey(glossaryId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + updateFrequency: CoreCacheUpdateFrequency.SOMETIMES, component: ADDON_MOD_GLOSSARY_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. @@ -514,7 +517,7 @@ export class AddonModGlossaryProvider { }; const preSets = { cacheKey: this.getEntryCacheKey(entryId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, component: ADDON_MOD_GLOSSARY_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. @@ -528,7 +531,7 @@ export class AddonModGlossaryProvider { const data = await this.getStoredDataForEntry(entryId, site.getId()); if (data.from !== undefined) { - const response = await CoreUtils.ignoreErrors( + const response = await CorePromiseUtils.ignoreErrors( this.getEntryFromList(data.glossaryId, entryId, data.from, false, options), ); @@ -702,9 +705,9 @@ export class AddonModGlossaryProvider { async invalidateContent(moduleId: number, courseId: number): Promise { const glossary = await this.getGlossary(courseId, moduleId); - await CoreUtils.ignoreErrors(this.invalidateGlossaryEntries(glossary)); + await CorePromiseUtils.ignoreErrors(this.invalidateGlossaryEntries(glossary)); - await CoreUtils.allPromises([ + await CorePromiseUtils.allPromises([ this.invalidateCourseGlossaries(courseId), this.invalidateCategories(glossary.id), ]); @@ -750,7 +753,7 @@ export class AddonModGlossaryProvider { } }); - await CoreUtils.allPromises(promises); + await CorePromiseUtils.allPromises(promises); } /** @@ -872,7 +875,7 @@ export class AddonModGlossaryProvider { return entryId; } catch (error) { - if (otherOptions.allowOffline && !CoreUtils.isWebServiceError(error)) { + if (otherOptions.allowOffline && !CoreWSError.isWebServiceError(error)) { // Couldn't connect to server, store in offline. return storeOffline(); } @@ -908,7 +911,7 @@ export class AddonModGlossaryProvider { concept: concept, definition: definition, definitionformat: 1, - options: CoreUtils.objectToArrayOfObjects(options || {}, 'name', 'value'), + options: CoreObject.toArrayOfObjects(options || {}, 'name', 'value'), }; if (attachId) { @@ -952,7 +955,7 @@ export class AddonModGlossaryProvider { concept: concept, definition: definition, definitionformat: 1, - options: CoreUtils.objectToArrayOfObjects(options || {}, 'name', 'value'), + options: CoreObject.toArrayOfObjects(options || {}, 'name', 'value'), }; if (attachId) { diff --git a/src/addons/mod/glossary/services/handlers/prefetch.ts b/src/addons/mod/glossary/services/handlers/prefetch.ts index 7e5a6b8d387..9ca88889b9f 100644 --- a/src/addons/mod/glossary/services/handlers/prefetch.ts +++ b/src/addons/mod/glossary/services/handlers/prefetch.ts @@ -20,7 +20,7 @@ import { CoreCourses } from '@features/courses/services/courses'; import { CoreUser } from '@features/user/services/user'; import { CoreFilepool } from '@services/filepool'; import { CoreSitesReadingStrategy } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreWSFile } from '@services/ws'; import { makeSingleton } from '@singletons'; import { AddonModGlossary, AddonModGlossaryEntry, AddonModGlossaryGlossary } from '../glossary'; @@ -191,7 +191,7 @@ export class AddonModGlossaryPrefetchHandlerService extends CoreCourseActivityPr promises.push(CoreCourse.getModuleBasicInfoByInstance(glossary.id, 'glossary', { siteId })); // Get course data, needed to determine upload max size if it's configured to be course limit. - promises.push(CoreUtils.ignoreErrors(CoreCourses.getCourseByField('id', courseId, siteId))); + promises.push(CorePromiseUtils.ignoreErrors(CoreCourses.getCourseByField('id', courseId, siteId))); await Promise.all(promises); } diff --git a/src/addons/mod/glossary/services/handlers/tag-area.ts b/src/addons/mod/glossary/services/handlers/tag-area.ts index 7d844e276bb..4ae01e9af86 100644 --- a/src/addons/mod/glossary/services/handlers/tag-area.ts +++ b/src/addons/mod/glossary/services/handlers/tag-area.ts @@ -13,7 +13,6 @@ // limitations under the License. import { Injectable, Type } from '@angular/core'; -import { CoreTagFeedComponent } from '@features/tag/components/feed/feed'; import { CoreTagAreaHandler } from '@features/tag/services/tag-area-delegate'; import { CoreTagFeedElement, CoreTagHelper } from '@features/tag/services/tag-helper'; import { makeSingleton } from '@singletons'; @@ -44,7 +43,9 @@ export class AddonModGlossaryTagAreaHandlerService implements CoreTagAreaHandler /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { + const { CoreTagFeedComponent } = await import('@features/tag/components/feed/feed'); + return CoreTagFeedComponent; } diff --git a/src/addons/mod/h5pactivity/components/index/index.ts b/src/addons/mod/h5pactivity/components/index/index.ts index f202bacb823..9fcc02cd2b3 100644 --- a/src/addons/mod/h5pactivity/components/index/index.ts +++ b/src/addons/mod/h5pactivity/components/index/index.ts @@ -45,7 +45,7 @@ import { } from '../../services/h5pactivity-sync'; import { CoreFileHelper } from '@services/file-helper'; import { CoreText } from '@singletons/text'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { ADDON_MOD_H5PACTIVITY_AUTO_SYNCED, ADDON_MOD_H5PACTIVITY_COMPONENT, @@ -699,7 +699,7 @@ export class AddonModH5PActivityIndexComponent extends CoreCourseModuleMainActiv return; } - await CoreUtils.ignoreErrors(CoreXAPIOffline.deleteStates(ADDON_MOD_H5PACTIVITY_TRACK_COMPONENT, { + await CorePromiseUtils.ignoreErrors(CoreXAPIOffline.deleteStates(ADDON_MOD_H5PACTIVITY_TRACK_COMPONENT, { itemId: this.h5pActivity.context, })); } 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 f10fce516a9..dd74688b596 100644 --- a/src/addons/mod/h5pactivity/pages/attempt-results/attempt-results.ts +++ b/src/addons/mod/h5pactivity/pages/attempt-results/attempt-results.ts @@ -17,7 +17,7 @@ import { Component, OnInit } from '@angular/core'; import { CoreUser, CoreUserProfile } from '@features/user/services/user'; import { CoreNavigator } from '@services/navigator'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { AddonModH5PActivity, AddonModH5PActivityData, @@ -33,7 +33,7 @@ import { ADDON_MOD_H5PACTIVITY_COMPONENT } from '../../constants'; @Component({ selector: 'page-addon-mod-h5pactivity-attempt-results', templateUrl: 'attempt-results.html', - styleUrls: ['attempt-results.scss'], + styleUrl: 'attempt-results.scss', }) export class AddonModH5PActivityAttemptResultsPage implements OnInit { @@ -54,7 +54,7 @@ export class AddonModH5PActivityAttemptResultsPage implements OnInit { return; } - await CoreUtils.ignoreErrors(AddonModH5PActivity.logViewReport( + await CorePromiseUtils.ignoreErrors(AddonModH5PActivity.logViewReport( this.h5pActivity.id, { attemptId: this.attemptId }, )); @@ -153,7 +153,7 @@ export class AddonModH5PActivityAttemptResultsPage implements OnInit { promises.push(AddonModH5PActivity.invalidateAttemptResults(this.h5pActivity.id, this.attemptId)); } - await CoreUtils.ignoreErrors(Promise.all(promises)); + await CorePromiseUtils.ignoreErrors(Promise.all(promises)); await this.fetchData(); } 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 dba3a8e9d9d..7edad69919a 100644 --- a/src/addons/mod/h5pactivity/pages/user-attempts/user-attempts.ts +++ b/src/addons/mod/h5pactivity/pages/user-attempts/user-attempts.ts @@ -18,7 +18,7 @@ import { CoreUser, CoreUserProfile } from '@features/user/services/user'; import { CoreNavigator } from '@services/navigator'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { AddonModH5PActivity, AddonModH5PActivityAttempt, @@ -34,7 +34,7 @@ import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics'; @Component({ selector: 'page-addon-mod-h5pactivity-user-attempts', templateUrl: 'user-attempts.html', - styleUrls: ['user-attempts.scss'], + styleUrl: 'user-attempts.scss', }) export class AddonModH5PActivityUserAttemptsPage implements OnInit { @@ -55,7 +55,7 @@ export class AddonModH5PActivityUserAttemptsPage implements OnInit { return; } - await CoreUtils.ignoreErrors(AddonModH5PActivity.logViewReport( + await CorePromiseUtils.ignoreErrors(AddonModH5PActivity.logViewReport( this.h5pActivity.id, { userId: this.userId }, )); @@ -167,7 +167,7 @@ export class AddonModH5PActivityUserAttemptsPage implements OnInit { promises.push(AddonModH5PActivity.invalidateUserAttempts(this.h5pActivity.id, this.userId)); } - await CoreUtils.ignoreErrors(Promise.all(promises)); + await CorePromiseUtils.ignoreErrors(Promise.all(promises)); await this.fetchData(); } 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 6e1d0bf85c4..8c7d0b88918 100644 --- a/src/addons/mod/h5pactivity/pages/users-attempts/users-attempts.ts +++ b/src/addons/mod/h5pactivity/pages/users-attempts/users-attempts.ts @@ -17,7 +17,7 @@ import { CoreUser, CoreUserProfile } from '@features/user/services/user'; import { CoreNavigator } from '@services/navigator'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { AddonModH5PActivity, AddonModH5PActivityData, @@ -33,7 +33,7 @@ import { AddonModH5PActivityGradeMethod } from '../../constants'; @Component({ selector: 'page-addon-mod-h5pactivity-users-attempts', templateUrl: 'users-attempts.html', - styleUrls: ['users-attempts.scss'], + styleUrl: 'users-attempts.scss', }) export class AddonModH5PActivityUsersAttemptsPage implements OnInit { @@ -54,7 +54,7 @@ export class AddonModH5PActivityUsersAttemptsPage implements OnInit { return; } - await CoreUtils.ignoreErrors(AddonModH5PActivity.logViewReport(this.h5pActivity.id)); + await CorePromiseUtils.ignoreErrors(AddonModH5PActivity.logViewReport(this.h5pActivity.id)); CoreAnalytics.logEvent({ type: CoreAnalyticsEventType.VIEW_ITEM_LIST, @@ -214,7 +214,7 @@ export class AddonModH5PActivityUsersAttemptsPage implements OnInit { promises.push(AddonModH5PActivity.invalidateAllUsersAttempts(this.h5pActivity.id)); } - await CoreUtils.ignoreErrors(Promise.all(promises)); + await CorePromiseUtils.ignoreErrors(Promise.all(promises)); await this.fetchData(true); } diff --git a/src/addons/mod/h5pactivity/services/h5pactivity-sync.ts b/src/addons/mod/h5pactivity/services/h5pactivity-sync.ts index 8a2c3361b08..90f43d75b86 100644 --- a/src/addons/mod/h5pactivity/services/h5pactivity-sync.ts +++ b/src/addons/mod/h5pactivity/services/h5pactivity-sync.ts @@ -22,7 +22,6 @@ import { CoreXAPIOffline } from '@features/xapi/services/offline'; import { CoreXAPI, XAPI_STATE_DELETED } from '@features/xapi/services/xapi'; import { CoreNetwork } from '@services/network'; import { CoreSites, CoreSitesReadingStrategy } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; import { makeSingleton, Translate } from '@singletons'; import { CoreEvents } from '@singletons/events'; import { @@ -41,6 +40,7 @@ import { ADDON_MOD_H5PACTIVITY_COMPONENT, ADDON_MOD_H5PACTIVITY_TRACK_COMPONENT, } from '../constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Service to sync H5P activities. @@ -195,7 +195,7 @@ export class AddonModH5PActivitySyncProvider extends CoreCourseActivitySyncBaseP h5pActivity = await AddonModH5PActivity.getH5PActivityByContextId(courseId, contextId, { siteId }); } catch (error) { if ( - CoreUtils.isWebServiceError(error) || + CoreWSError.isWebServiceError(error) || CoreErrorHelper.getErrorMessageFromError(error) === Translate.instant('core.course.modulenotfound') ) { // Activity no longer accessible. Delete the data and finish the sync. @@ -208,7 +208,7 @@ export class AddonModH5PActivitySyncProvider extends CoreCourseActivitySyncBaseP } // Sync offline logs. - await CoreUtils.ignoreErrors( + await CorePromiseUtils.ignoreErrors( CoreCourseLogHelper.syncActivity(ADDON_MOD_H5PACTIVITY_COMPONENT, h5pActivity.id, siteId), ); @@ -252,7 +252,7 @@ export class AddonModH5PActivitySyncProvider extends CoreCourseActivitySyncBaseP await CoreXAPIOffline.deleteStatements(entry.id, siteId); } catch (error) { - if (!CoreUtils.isWebServiceError(error)) { + if (!CoreWSError.isWebServiceError(error)) { throw error; } @@ -268,7 +268,7 @@ export class AddonModH5PActivitySyncProvider extends CoreCourseActivitySyncBaseP if (result.updated) { // Data has been sent to server, invalidate attempts. - await CoreUtils.ignoreErrors(AddonModH5PActivity.invalidateUserAttempts(id, undefined, siteId)); + await CorePromiseUtils.ignoreErrors(AddonModH5PActivity.invalidateUserAttempts(id, undefined, siteId)); } return result; @@ -312,7 +312,7 @@ export class AddonModH5PActivitySyncProvider extends CoreCourseActivitySyncBaseP } catch (error) { // Error getting attempts. If the WS has thrown an exception it means the user cannot retrieve the attempts for // some reason (it shouldn't happen), continue synchronizing in that case. - if (!CoreUtils.isWebServiceError(error)) { + if (!CoreWSError.isWebServiceError(error)) { throw error; } } @@ -363,7 +363,7 @@ export class AddonModH5PActivitySyncProvider extends CoreCourseActivitySyncBaseP siteId, }); } catch (error) { - if (!CoreUtils.isWebServiceError(error)) { + if (!CoreWSError.isWebServiceError(error)) { throw error; } @@ -401,3 +401,16 @@ export type AddonModH5PActivityAutoSyncData = { contextId: number; warnings: string[]; }; + +declare module '@singletons/events' { + + /** + * Augment CoreEventsData interface with events specific to this service. + * + * @see https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation + */ + export interface CoreEventsData { + [ADDON_MOD_H5PACTIVITY_AUTO_SYNCED]: AddonModH5PActivityAutoSyncData; + } + +} diff --git a/src/addons/mod/h5pactivity/services/h5pactivity.ts b/src/addons/mod/h5pactivity/services/h5pactivity.ts index 753ffd9942f..7065efe8ec0 100644 --- a/src/addons/mod/h5pactivity/services/h5pactivity.ts +++ b/src/addons/mod/h5pactivity/services/h5pactivity.ts @@ -16,7 +16,6 @@ import { Injectable } from '@angular/core'; import { CoreSites, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@services/sites'; import { CoreWSExternalWarning, CoreWSExternalFile, CoreWSFile } from '@services/ws'; -import { CoreUtils } from '@services/utils/utils'; import { CoreSite } from '@classes/sites/site'; import { CoreCourseLogHelper } from '@features/course/services/log-helper'; import { CoreH5P } from '@features/h5p/services/h5p'; @@ -25,15 +24,14 @@ import { CoreCourseCommonModWSOptions } from '@features/course/services/course'; import { makeSingleton, Translate } from '@singletons/index'; import { CoreWSError } from '@classes/errors/wserror'; import { CoreError } from '@classes/errors/error'; -import { AddonModH5PActivityAutoSyncData } from './h5pactivity-sync'; import { CoreTime } from '@singletons/time'; import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site'; import { - ADDON_MOD_H5PACTIVITY_AUTO_SYNCED, ADDON_MOD_H5PACTIVITY_COMPONENT, ADDON_MOD_H5PACTIVITY_USERS_PER_PAGE, AddonModH5PActivityGradeMethod, } from '../constants'; +import { CoreCacheUpdateFrequency } from '@/core/constants'; /** * Service that provides some features for H5P activity. @@ -162,7 +160,7 @@ export class AddonModH5PActivityProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getAccessInformationCacheKey(id), - updateFrequency: CoreSite.FREQUENCY_OFTEN, + updateFrequency: CoreCacheUpdateFrequency.OFTEN, component: ADDON_MOD_H5PACTIVITY_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. @@ -266,7 +264,7 @@ export class AddonModH5PActivityProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getUsersAttemptsCacheKey(id, options), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + updateFrequency: CoreCacheUpdateFrequency.SOMETIMES, component: ADDON_MOD_H5PACTIVITY_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. @@ -362,7 +360,7 @@ export class AddonModH5PActivityProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getAttemptResultsCacheKey(id, params.attemptids), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + updateFrequency: CoreCacheUpdateFrequency.SOMETIMES, component: ADDON_MOD_H5PACTIVITY_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. @@ -381,7 +379,7 @@ export class AddonModH5PActivityProvider { return this.formatAttemptResults(response.attempts[0]); } catch (error) { - if (CoreUtils.isWebServiceError(error)) { + if (CoreWSError.isWebServiceError(error)) { throw error; } @@ -427,7 +425,7 @@ export class AddonModH5PActivityProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getAttemptResultsCommonCacheKey(id), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + updateFrequency: CoreCacheUpdateFrequency.SOMETIMES, component: ADDON_MOD_H5PACTIVITY_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. @@ -510,7 +508,7 @@ export class AddonModH5PActivityProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getH5PActivityDataCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, component: ADDON_MOD_H5PACTIVITY_COMPONENT, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; @@ -617,7 +615,7 @@ export class AddonModH5PActivityProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getUserAttemptsCacheKey(id, params.userids), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + updateFrequency: CoreCacheUpdateFrequency.SOMETIMES, component: ADDON_MOD_H5PACTIVITY_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. @@ -635,7 +633,7 @@ export class AddonModH5PActivityProvider { return this.formatUserAttempts(response.usersattempts[0]); } catch (error) { - if (CoreUtils.isWebServiceError(error)) { + if (CoreWSError.isWebServiceError(error)) { throw error; } @@ -1136,19 +1134,6 @@ export type AddonModH5PActivityGetAllUsersAttemptsOptions = AddonModH5PActivityG dontFailOnError?: boolean; // If true the function will return the users it's able to retrieve, until a call fails. }; -declare module '@singletons/events' { - - /** - * Augment CoreEventsData interface with events specific to this service. - * - * @see https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation - */ - export interface CoreEventsData { - [ADDON_MOD_H5PACTIVITY_AUTO_SYNCED]: AddonModH5PActivityAutoSyncData; - } - -} - /** * Data to be sent using xAPI. */ diff --git a/src/addons/mod/h5pactivity/services/handlers/prefetch.ts b/src/addons/mod/h5pactivity/services/handlers/prefetch.ts index 9463f41cfe1..2985e7d0fe1 100644 --- a/src/addons/mod/h5pactivity/services/handlers/prefetch.ts +++ b/src/addons/mod/h5pactivity/services/handlers/prefetch.ts @@ -25,7 +25,7 @@ import { CoreXAPI } from '@features/xapi/services/xapi'; import { CoreFileHelper } from '@services/file-helper'; import { CoreFilepool } from '@services/filepool'; import { CoreSites, CoreSitesReadingStrategy } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreWSFile } from '@services/ws'; import { makeSingleton } from '@singletons'; import { @@ -149,7 +149,7 @@ export class AddonModH5PActivityPrefetchHandlerService extends CoreCourseActivit const fileState = await CoreFilepool.getFileStateByUrl(siteId, CoreFileHelper.getFileUrl(deployedFile)); if (fileState !== DownloadStatus.DOWNLOADED) { - await CoreUtils.ignoreErrors(CoreXAPIOffline.deleteStates(ADDON_MOD_H5PACTIVITY_TRACK_COMPONENT, { + await CorePromiseUtils.ignoreErrors(CoreXAPIOffline.deleteStates(ADDON_MOD_H5PACTIVITY_TRACK_COMPONENT, { itemId: h5pActivity.context, siteId, })); diff --git a/src/addons/mod/h5pactivity/services/handlers/report-link.ts b/src/addons/mod/h5pactivity/services/handlers/report-link.ts index 8c2ee7aed2f..1d70bfe31d0 100644 --- a/src/addons/mod/h5pactivity/services/handlers/report-link.ts +++ b/src/addons/mod/h5pactivity/services/handlers/report-link.ts @@ -20,7 +20,7 @@ import { CoreCourse } from '@features/course/services/course'; import { CoreNavigator } from '@services/navigator'; import { CoreSites, CoreSitesReadingStrategy } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { makeSingleton } from '@singletons'; import { AddonModH5PActivity } from '../h5pactivity'; import { ADDON_MOD_H5PACTIVITY_PAGE_NAME } from '../../constants'; @@ -115,7 +115,7 @@ export class AddonModH5PActivityReportLinkHandlerService extends CoreContentLink canViewAllAttempts = await AddonModH5PActivity.canGetUsersAttempts(siteId); if (canViewAllAttempts) { - const accessInfo = await CoreUtils.ignoreErrors(AddonModH5PActivity.getAccessInformation(id, { + const accessInfo = await CorePromiseUtils.ignoreErrors(AddonModH5PActivity.getAccessInformation(id, { cmId, siteId, })); diff --git a/src/addons/mod/imscp/components/index/index.ts b/src/addons/mod/imscp/components/index/index.ts index d8ccbc57857..a2d43848713 100644 --- a/src/addons/mod/imscp/components/index/index.ts +++ b/src/addons/mod/imscp/components/index/index.ts @@ -18,7 +18,7 @@ import { CoreCourseContentsPage } from '@features/course/pages/contents/contents import { CoreCourse } from '@features/course/services/course'; import { CoreNavigator } from '@services/navigator'; import { AddonModImscp, AddonModImscpTocItem } from '../../services/imscp'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { ADDON_MOD_IMSCP_COMPONENT, ADDON_MOD_IMSCP_PAGE_NAME } from '../../constants'; /** @@ -27,7 +27,7 @@ import { ADDON_MOD_IMSCP_COMPONENT, ADDON_MOD_IMSCP_PAGE_NAME } from '../../cons @Component({ selector: 'addon-mod-imscp-index', templateUrl: 'addon-mod-imscp-index.html', - styleUrls: ['index.scss'], + styleUrl: 'index.scss', }) export class AddonModImscpIndexComponent extends CoreCourseModuleMainResourceComponent implements OnInit { @@ -103,7 +103,7 @@ export class AddonModImscpIndexComponent extends CoreCourseModuleMainResourceCom * @inheritdoc */ protected async logActivity(): Promise { - await CoreUtils.ignoreErrors(AddonModImscp.logView(this.module.instance)); + await CorePromiseUtils.ignoreErrors(AddonModImscp.logView(this.module.instance)); this.analyticsLogEvent('mod_imscp_view_imscp'); } diff --git a/src/addons/mod/imscp/pages/view/view.ts b/src/addons/mod/imscp/pages/view/view.ts index 354dabe01e7..ad4e232ad21 100644 --- a/src/addons/mod/imscp/pages/view/view.ts +++ b/src/addons/mod/imscp/pages/view/view.ts @@ -24,7 +24,7 @@ import { CoreNetwork } from '@services/network'; import { CoreNavigator } from '@services/navigator'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreErrorHelper } from '@services/error-helper'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { Translate } from '@singletons'; import { AddonModImscp, AddonModImscpImscp, AddonModImscpTocItem } from '../../services/imscp'; import { CoreModals } from '@services/modals'; @@ -214,12 +214,12 @@ export class AddonModImscpViewPage implements OnInit { * @returns Promise resolved when done. */ async doRefresh(refresher?: HTMLIonRefresherElement): Promise { - await CoreUtils.ignoreErrors(Promise.all([ + await CorePromiseUtils.ignoreErrors(Promise.all([ AddonModImscp.invalidateContent(this.cmId, this.courseId), CoreCourseModulePrefetchDelegate.invalidateCourseUpdates(this.courseId), // To detect if IMSCP was updated. ])); - await CoreUtils.ignoreErrors(this.fetchContent(true)); + await CorePromiseUtils.ignoreErrors(this.fetchContent(true)); refresher?.complete(); } diff --git a/src/addons/mod/imscp/services/handlers/prefetch.ts b/src/addons/mod/imscp/services/handlers/prefetch.ts index 5aa83b35a5a..765784bad4d 100644 --- a/src/addons/mod/imscp/services/handlers/prefetch.ts +++ b/src/addons/mod/imscp/services/handlers/prefetch.ts @@ -22,7 +22,7 @@ import { import { CoreCourseModuleData } from '@features/course/services/course-helper'; import { CoreFilepool } from '@services/filepool'; import { CoreSites, CoreSitesReadingStrategy } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreWSFile } from '@services/ws'; import { makeSingleton } from '@singletons'; import { AddonModImscp } from '../imscp'; @@ -61,7 +61,7 @@ export class AddonModImscpPrefetchHandlerService extends CoreCourseResourcePrefe */ async getIntroFiles(module: CoreCourseAnyModuleData, courseId: number): Promise { // If not found, use undefined so module description is used. - const imscp = await CoreUtils.ignoreErrors(AddonModImscp.getImscp(courseId, module.id)); + const imscp = await CorePromiseUtils.ignoreErrors(AddonModImscp.getImscp(courseId, module.id)); return this.getIntroFilesFromInstance(module, imscp); } diff --git a/src/addons/mod/imscp/services/imscp.ts b/src/addons/mod/imscp/services/imscp.ts index 9e2741d88d6..fa2a3c289aa 100644 --- a/src/addons/mod/imscp/services/imscp.ts +++ b/src/addons/mod/imscp/services/imscp.ts @@ -14,7 +14,6 @@ import { Injectable } from '@angular/core'; import { CoreError } from '@classes/errors/error'; -import { CoreSite } from '@classes/sites/site'; import { CoreCourse, CoreCourseModuleContentFile } from '@features/course/services/course'; import { CoreCourseModuleData } from '@features/course/services/course-helper'; import { CoreCourseLogHelper } from '@features/course/services/log-helper'; @@ -22,12 +21,13 @@ import { CoreNetwork } from '@services/network'; import { CoreFilepool } from '@services/filepool'; import { CoreSitesCommonWSOptions, CoreSites } from '@services/sites'; import { CoreText } from '@singletons/text'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreWSExternalFile, CoreWSExternalWarning } from '@services/ws'; import { makeSingleton, Translate } from '@singletons'; import { CorePath } from '@singletons/path'; import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site'; import { ADDON_MOD_IMSCP_COMPONENT } from '../constants'; +import { CoreCacheUpdateFrequency } from '@/core/constants'; /** * Service that provides some features for IMSCP. @@ -114,7 +114,7 @@ export class AddonModImscpProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getImscpDataCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, component: ADDON_MOD_IMSCP_COMPONENT, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), }; @@ -228,7 +228,7 @@ export class AddonModImscpProvider { promises.push(CoreFilepool.invalidateFilesByComponent(siteId, ADDON_MOD_IMSCP_COMPONENT, moduleId)); promises.push(CoreCourse.invalidateModule(moduleId, siteId)); - await CoreUtils.allPromises(promises); + await CorePromiseUtils.allPromises(promises); } /** diff --git a/src/addons/mod/label/services/handlers/prefetch.ts b/src/addons/mod/label/services/handlers/prefetch.ts index bd65a972c36..1b6db26bbeb 100644 --- a/src/addons/mod/label/services/handlers/prefetch.ts +++ b/src/addons/mod/label/services/handlers/prefetch.ts @@ -16,7 +16,7 @@ import { Injectable } from '@angular/core'; import { CoreCourseResourcePrefetchHandlerBase } from '@features/course/classes/resource-prefetch-handler'; import { CoreCourse, CoreCourseAnyModuleData } from '@features/course/services/course'; import { CoreSitesReadingStrategy } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreWSFile } from '@services/ws'; import { makeSingleton } from '@singletons'; import { AddonModLabel } from '../label'; @@ -60,7 +60,7 @@ export class AddonModLabelPrefetchHandlerService extends CoreCourseResourcePrefe promises.push(AddonModLabel.invalidateLabelData(courseId)); promises.push(CoreCourse.invalidateModule(module.id)); - await CoreUtils.allPromises(promises); + await CorePromiseUtils.allPromises(promises); } /** diff --git a/src/addons/mod/label/services/label.ts b/src/addons/mod/label/services/label.ts index f4a5e826009..caf828f3c95 100644 --- a/src/addons/mod/label/services/label.ts +++ b/src/addons/mod/label/services/label.ts @@ -15,13 +15,13 @@ import { Injectable } from '@angular/core'; import { CoreError } from '@classes/errors/error'; import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site'; -import { CoreSite } from '@classes/sites/site'; import { CoreFilepool } from '@services/filepool'; import { CoreSites, CoreSitesCommonWSOptions } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreWSExternalFile, CoreWSExternalWarning } from '@services/ws'; import { makeSingleton, Translate } from '@singletons'; import { ADDON_MOD_LABEL_COMPONENT } from '../constants'; +import { CoreCacheUpdateFrequency } from '@/core/constants'; /** * Service that provides some features for labels. @@ -64,7 +64,7 @@ export class AddonModLabelProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getLabelDataCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, component: ADDON_MOD_LABEL_COMPONENT, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), }; @@ -133,7 +133,7 @@ export class AddonModLabelProvider { promises.push(this.invalidateLabelData(courseId, siteId)); promises.push(CoreFilepool.invalidateFilesByComponent(siteId, ADDON_MOD_LABEL_COMPONENT, moduleId, true)); - await CoreUtils.allPromises(promises); + await CorePromiseUtils.allPromises(promises); } } diff --git a/src/addons/mod/lesson/components/index/index.ts b/src/addons/mod/lesson/components/index/index.ts index d89a3cc3f32..350bbcd8225 100644 --- a/src/addons/mod/lesson/components/index/index.ts +++ b/src/addons/mod/lesson/components/index/index.ts @@ -25,7 +25,7 @@ import { CoreNavigator } from '@services/navigator'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreForms } from '@singletons/form'; import { CoreText } from '@singletons/text'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { AddonModLessonRetakeFinishedInSyncDBRecord } from '../../services/database/lesson'; import { AddonModLessonPrefetchHandler } from '../../services/handlers/prefetch'; @@ -375,7 +375,7 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo return; } - await CoreUtils.ignoreErrors(AddonModLesson.logViewLesson(this.lesson.id, this.password)); + await CorePromiseUtils.ignoreErrors(AddonModLesson.logViewLesson(this.lesson.id, this.password)); } /** @@ -562,10 +562,10 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo if (formattedData.students) { // Get the user data for each student returned. - await CoreUtils.allPromises(formattedData.students.map(async (student) => { + await CorePromiseUtils.allPromises(formattedData.students.map(async (student) => { student.bestgrade = CoreText.roundToDecimals(student.bestgrade, 2); - const user = await CoreUtils.ignoreErrors(CoreUser.getProfile(student.id, this.courseId, true)); + const user = await CorePromiseUtils.ignoreErrors(CoreUser.getProfile(student.id, this.courseId, true)); if (user) { student.profileimageurl = user.profileimageurl; } @@ -672,7 +672,7 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo if (!result.updated && this.dataSent && this.isPrefetched()) { // The user sent data to server, but not in the sync process. Check if we need to fetch data. - await CoreUtils.ignoreErrors(AddonModLessonSync.prefetchAfterUpdate( + await CorePromiseUtils.ignoreErrors(AddonModLessonSync.prefetchAfterUpdate( AddonModLessonPrefetchHandler.instance, this.module, this.courseId, diff --git a/src/addons/mod/lesson/pages/player/player.ts b/src/addons/mod/lesson/pages/player/player.ts index 57c984ef3c0..012540ae268 100644 --- a/src/addons/mod/lesson/pages/player/player.ts +++ b/src/addons/mod/lesson/pages/player/player.ts @@ -24,7 +24,7 @@ import { CoreSites, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@ import { CoreSync } from '@services/sync'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreUrl } from '@singletons/url'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreObject } from '@singletons/object'; import { CoreWSExternalFile } from '@services/ws'; import { ModalController, Translate } from '@singletons'; import { CoreEvents } from '@singletons/events'; @@ -55,6 +55,8 @@ import { CoreFormFields, CoreForms } from '@singletons/form'; import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics'; import { ADDON_MOD_LESSON_COMPONENT, AddonModLessonJumpTo } from '../../constants'; import { CoreModals } from '@services/modals'; +import { CorePromiseUtils } from '@singletons/promise-utils'; +import { CoreWSError } from '@classes/errors/wserror'; /** * Page that allows attempting and reviewing a lesson. @@ -62,7 +64,7 @@ import { CoreModals } from '@services/modals'; @Component({ selector: 'page-addon-mod-lesson-player', templateUrl: 'player.html', - styleUrls: ['player.scss'], + styleUrl: 'player.scss', }) export class AddonModLessonPlayerPage implements OnInit, OnDestroy, CanLeave { @@ -169,7 +171,7 @@ export class AddonModLessonPlayerPage implements OnInit, OnDestroy, CanLeave { if (this.question && !this.eolData && !this.processData && this.originalData) { // Question shown. Check if there is any change. - if (!CoreUtils.basicLeftCompare(this.questionForm.getRawValue(), this.originalData, 3)) { + if (!CoreObject.basicLeftCompare(this.questionForm.getRawValue(), this.originalData, 3)) { await CoreDomUtils.showConfirm(Translate.instant('core.confirmcanceledit')); } } @@ -213,7 +215,7 @@ export class AddonModLessonPlayerPage implements OnInit, OnDestroy, CanLeave { throw error; } - if (CoreUtils.isWebServiceError(error)) { + if (CoreWSError.isWebServiceError(error)) { // WebService returned an error, cannot perform the action. throw error; } @@ -355,7 +357,7 @@ export class AddonModLessonPlayerPage implements OnInit, OnDestroy, CanLeave { return true; } catch (error) { - if (this.review && this.retakeToReview && CoreUtils.isWebServiceError(error)) { + if (this.review && this.retakeToReview && CoreWSError.isWebServiceError(error)) { // The user cannot review the retake. Unmark the retake as being finished in sync. await AddonModLessonSync.deleteRetakeFinishedInSync(this.lesson!.id); } @@ -384,7 +386,7 @@ export class AddonModLessonPlayerPage implements OnInit, OnDestroy, CanLeave { if (this.offline && CoreNetwork.isOnline()) { // Offline mode but the app is online. Try to sync the data. - const result = await CoreUtils.ignoreErrors( + const result = await CorePromiseUtils.ignoreErrors( AddonModLessonSync.syncLesson(lesson.id, true, true), ); diff --git a/src/addons/mod/lesson/pages/user-retake/user-retake.ts b/src/addons/mod/lesson/pages/user-retake/user-retake.ts index 3ecb605a4c0..8a751006222 100644 --- a/src/addons/mod/lesson/pages/user-retake/user-retake.ts +++ b/src/addons/mod/lesson/pages/user-retake/user-retake.ts @@ -20,7 +20,7 @@ import { CoreNavigator } from '@services/navigator'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreText } from '@singletons/text'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreErrorHelper } from '@services/error-helper'; import { Translate } from '@singletons'; import { AddonModLesson, @@ -35,6 +35,7 @@ import { AddonModLessonAnswerData, AddonModLessonHelper } from '../../services/l import { CoreTime } from '@singletons/time'; import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics'; import { ADDON_MOD_LESSON_COMPONENT } from '../../constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Page that displays a retake made by a certain user. @@ -42,7 +43,7 @@ import { ADDON_MOD_LESSON_COMPONENT } from '../../constants'; @Component({ selector: 'page-addon-mod-lesson-user-retake', templateUrl: 'user-retake.html', - styleUrls: ['user-retake.scss'], + styleUrl: 'user-retake.scss', }) export class AddonModLessonUserRetakePage implements OnInit { @@ -102,7 +103,7 @@ export class AddonModLessonUserRetakePage implements OnInit { this.performLogView(); } catch (error) { this.selectedRetake = this.previousSelectedRetake ?? this.selectedRetake; - CoreDomUtils.showErrorModal(CoreUtils.addDataNotDownloadedError(error, 'Error getting attempt.')); + CoreDomUtils.showErrorModal(CoreErrorHelper.addDataNotDownloadedError(error, 'Error getting attempt.')); } finally { this.loaded = true; } @@ -161,7 +162,7 @@ export class AddonModLessonUserRetakePage implements OnInit { } // Get the profile image of the user. - const user = await CoreUtils.ignoreErrors(CoreUser.getProfile(student.id, this.courseId, true)); + const user = await CorePromiseUtils.ignoreErrors(CoreUser.getProfile(student.id, this.courseId, true)); this.student = student; this.student.profileimageurl = user?.profileimageurl; @@ -188,7 +189,7 @@ export class AddonModLessonUserRetakePage implements OnInit { promises.push(AddonModLesson.invalidateUserRetakesForUser(this.lesson.id, this.userId)); } - await CoreUtils.ignoreErrors(Promise.all(promises)); + await CorePromiseUtils.ignoreErrors(Promise.all(promises)); await this.fetchData(); } diff --git a/src/addons/mod/lesson/services/handlers/index-link.ts b/src/addons/mod/lesson/services/handlers/index-link.ts index 39fd3d9c33d..adb07e8761c 100644 --- a/src/addons/mod/lesson/services/handlers/index-link.ts +++ b/src/addons/mod/lesson/services/handlers/index-link.ts @@ -20,7 +20,7 @@ import { CoreCourse } from '@features/course/services/course'; import { CoreCourseHelper } from '@features/course/services/course-helper'; import { CoreSitesReadingStrategy } from '@services/sites'; import { CoreLoadings } from '@services/loadings'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { makeSingleton } from '@singletons'; import { AddonModLesson } from '../lesson'; @@ -95,7 +95,7 @@ export class AddonModLessonIndexLinkHandlerService extends CoreContentLinksModul ); // Store the password so it's automatically used. - await CoreUtils.ignoreErrors(AddonModLesson.storePassword(module.instance, password, siteId)); + await CorePromiseUtils.ignoreErrors(AddonModLesson.storePassword(module.instance, password, siteId)); await CoreCourseHelper.navigateToModule(moduleId, { courseId: module.course, diff --git a/src/addons/mod/lesson/services/handlers/prefetch.ts b/src/addons/mod/lesson/services/handlers/prefetch.ts index 6df58a118d5..853d3550c58 100644 --- a/src/addons/mod/lesson/services/handlers/prefetch.ts +++ b/src/addons/mod/lesson/services/handlers/prefetch.ts @@ -22,7 +22,7 @@ import { CoreGroups } from '@services/groups'; import { CoreFileSizeSum, CorePluginFileDelegate } from '@services/plugin-file-delegate'; import { CoreSites, CoreSitesReadingStrategy } from '@services/sites'; import { CoreModals } from '@services/modals'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreWSFile } from '@services/ws'; import { makeSingleton, Translate } from '@singletons'; import { @@ -119,7 +119,7 @@ export class AddonModLessonPrefetchHandlerService extends CoreCourseActivityPref } // The lesson requires a password. Check if there is one in DB. - let password = await CoreUtils.ignoreErrors(AddonModLesson.getStoredPassword(lessonId)); + let password = await CorePromiseUtils.ignoreErrors(AddonModLesson.getStoredPassword(lessonId)); if (password) { try { @@ -294,7 +294,7 @@ export class AddonModLessonPrefetchHandlerService extends CoreCourseActivityPref await AddonModLesson.launchRetake(lessonId, password, undefined, false, siteId); const results = await Promise.all([ - CoreUtils.ignoreErrors(CoreFilepool.updatePackageDownloadTime(siteId, this.component, module.id)), + CorePromiseUtils.ignoreErrors(CoreFilepool.updatePackageDownloadTime(siteId, this.component, module.id)), AddonModLesson.getAccessInformation(lessonId, modOptions), ]); @@ -324,7 +324,7 @@ export class AddonModLessonPrefetchHandlerService extends CoreCourseActivityPref await Promise.all([ this.prefetchPagesData(lesson, passwordOptions), // Prefetch user timers to be able to calculate timemodified in offline. - CoreUtils.ignoreErrors(AddonModLesson.getTimers(lesson.id, modOptions)), + CorePromiseUtils.ignoreErrors(AddonModLesson.getTimers(lesson.id, modOptions)), // Prefetch viewed pages in last retake to calculate progress. AddonModLesson.getContentPagesViewedOnline(lesson.id, retake, modOptions), // Prefetch question attempts in last retake for offline calculations. diff --git a/src/addons/mod/lesson/services/handlers/push-click.ts b/src/addons/mod/lesson/services/handlers/push-click.ts index 6a63059be71..f0a3d19b306 100644 --- a/src/addons/mod/lesson/services/handlers/push-click.ts +++ b/src/addons/mod/lesson/services/handlers/push-click.ts @@ -19,7 +19,7 @@ import { CoreGrades } from '@features/grades/services/grades'; import { CoreGradesHelper } from '@features/grades/services/grades-helper'; import { CorePushNotificationsClickHandler } from '@features/pushnotifications/services/push-delegate'; import { CorePushNotificationsNotificationBasicData } from '@features/pushnotifications/services/pushnotifications'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { makeSingleton } from '@singletons'; /** diff --git a/src/addons/mod/lesson/services/lesson-helper.ts b/src/addons/mod/lesson/services/lesson-helper.ts index 1bd5c7397e9..1f063887a30 100644 --- a/src/addons/mod/lesson/services/lesson-helper.ts +++ b/src/addons/mod/lesson/services/lesson-helper.ts @@ -26,7 +26,7 @@ import { AddonModLessonGetPageDataWSResponse, } from './lesson'; import { CoreTime } from '@singletons/time'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { AddonModLessonPageSubtype } from '../constants'; import { convertTextToHTMLElement } from '@/core/utils/create-html-element'; diff --git a/src/addons/mod/lesson/services/lesson-offline.ts b/src/addons/mod/lesson/services/lesson-offline.ts index daf5b656c40..eb1659fd0c7 100644 --- a/src/addons/mod/lesson/services/lesson-offline.ts +++ b/src/addons/mod/lesson/services/lesson-offline.ts @@ -17,7 +17,7 @@ import { CoreSites } from '@services/sites'; import { CoreFormFields } from '@singletons/form'; import { CoreText } from '@singletons/text'; import { CoreTimeUtils } from '@services/utils/time'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreObject } from '@singletons/object'; import { makeSingleton } from '@singletons'; import { AddonModLessonPageAttemptDBRecord, @@ -28,6 +28,7 @@ import { import { AddonModLessonPageWSData } from './lesson'; import { AddonModLessonPageType } from '../constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Service to handle offline lesson. @@ -144,14 +145,14 @@ export class AddonModLessonOfflineProvider { const lessons: Record = {}; const [pageAttempts, retakes] = await Promise.all([ - CoreUtils.ignoreErrors(this.getAllAttempts(siteId)), - CoreUtils.ignoreErrors(this.getAllRetakes(siteId)), + CorePromiseUtils.ignoreErrors(this.getAllAttempts(siteId)), + CorePromiseUtils.ignoreErrors(this.getAllRetakes(siteId)), ]); this.getLessonsFromEntries(lessons, pageAttempts || []); this.getLessonsFromEntries(lessons, retakes || []); - return CoreUtils.objectToArray(lessons); + return CoreObject.toArray(lessons); } /** @@ -420,8 +421,8 @@ export class AddonModLessonOfflineProvider { */ async hasOfflineData(lessonId: number, siteId?: string): Promise { const [retake, attempts] = await Promise.all([ - CoreUtils.ignoreErrors(this.getRetake(lessonId, siteId)), - CoreUtils.ignoreErrors(this.getLessonAttempts(lessonId, siteId)), + CorePromiseUtils.ignoreErrors(this.getRetake(lessonId, siteId)), + CorePromiseUtils.ignoreErrors(this.getLessonAttempts(lessonId, siteId)), ]); return !!retake || !!attempts?.length; diff --git a/src/addons/mod/lesson/services/lesson-sync.ts b/src/addons/mod/lesson/services/lesson-sync.ts index 04db999a113..bdcfaf2d270 100644 --- a/src/addons/mod/lesson/services/lesson-sync.ts +++ b/src/addons/mod/lesson/services/lesson-sync.ts @@ -24,7 +24,7 @@ import { CoreSites, CoreSitesReadingStrategy } from '@services/sites'; import { CoreSync, CoreSyncResult } from '@services/sync'; import { CoreTimeUtils } from '@services/utils/time'; import { CoreUrl } from '@singletons/url'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreWSError } from '@classes/errors/wserror'; import { makeSingleton, Translate } from '@singletons'; import { CoreEvents } from '@singletons/events'; import { AddonModLessonRetakeFinishedInSyncDBRecord, RETAKES_FINISHED_SYNC_TABLE_NAME } from './database/lesson'; @@ -32,6 +32,7 @@ import { AddonModLessonGetPasswordResult, AddonModLessonPrefetchHandler } from ' import { AddonModLesson, AddonModLessonLessonWSData } from './lesson'; import { AddonModLessonOffline, AddonModLessonPageAttemptRecord } from './lesson-offline'; import { ADDON_MOD_LESSON_AUTO_SYNCED, ADDON_MOD_LESSON_COMPONENT } from '../constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Service to sync lesson. @@ -56,7 +57,7 @@ export class AddonModLessonSyncProvider extends CoreCourseActivitySyncBaseProvid const site = await CoreSites.getSite(siteId); // Ignore errors, maybe there is none. - await CoreUtils.ignoreErrors(site.getDb().deleteRecords(RETAKES_FINISHED_SYNC_TABLE_NAME, { lessonid: lessonId })); + await CorePromiseUtils.ignoreErrors(site.getDb().deleteRecords(RETAKES_FINISHED_SYNC_TABLE_NAME, { lessonid: lessonId })); } /** @@ -72,7 +73,7 @@ export class AddonModLessonSyncProvider extends CoreCourseActivitySyncBaseProvid ): Promise { const site = await CoreSites.getSite(siteId); - return CoreUtils.ignoreErrors(site.getDb().getRecord(RETAKES_FINISHED_SYNC_TABLE_NAME, { lessonid: lessonId })); + return CorePromiseUtils.ignoreErrors(site.getDb().getRecord(RETAKES_FINISHED_SYNC_TABLE_NAME, { lessonid: lessonId })); } /** @@ -86,8 +87,8 @@ export class AddonModLessonSyncProvider extends CoreCourseActivitySyncBaseProvid async hasDataToSync(lessonId: number, retake: number, siteId?: string): Promise { const [hasAttempts, hasFinished] = await Promise.all([ - CoreUtils.ignoreErrors(AddonModLessonOffline.hasRetakeAttempts(lessonId, retake, siteId)), - CoreUtils.ignoreErrors(AddonModLessonOffline.hasFinishedRetake(lessonId, siteId)), + CorePromiseUtils.ignoreErrors(AddonModLessonOffline.hasRetakeAttempts(lessonId, retake, siteId)), + CorePromiseUtils.ignoreErrors(AddonModLessonOffline.hasFinishedRetake(lessonId, siteId)), ]); return !!(hasAttempts || hasFinished); @@ -224,7 +225,7 @@ export class AddonModLessonSyncProvider extends CoreCourseActivitySyncBaseProvid siteId?: string, ): Promise { // Sync offline logs. - await CoreUtils.ignoreErrors( + await CorePromiseUtils.ignoreErrors( CoreCourseLogHelper.syncActivity(ADDON_MOD_LESSON_COMPONENT, lessonId, siteId), ); @@ -250,7 +251,7 @@ export class AddonModLessonSyncProvider extends CoreCourseActivitySyncBaseProvid } // Sync finished, set sync time. - await CoreUtils.ignoreErrors(this.setSyncTime(lessonId, siteId)); + await CorePromiseUtils.ignoreErrors(this.setSyncTime(lessonId, siteId)); // All done, return the result. return result; @@ -302,7 +303,7 @@ export class AddonModLessonSyncProvider extends CoreCourseActivitySyncBaseProvid } // Attempt doesn't belong to current retake, delete. - promises.push(CoreUtils.ignoreErrors(AddonModLessonOffline.deleteAttempt( + promises.push(CorePromiseUtils.ignoreErrors(AddonModLessonOffline.deleteAttempt( lesson.id, attempt.retake, attempt.pageid, @@ -337,7 +338,7 @@ export class AddonModLessonSyncProvider extends CoreCourseActivitySyncBaseProvid blocking: true, })); - await CoreUtils.executeOrderedPromises(promisesData); + await CorePromiseUtils.executeOrderedPromises(promisesData); return passwordData; } @@ -374,7 +375,7 @@ export class AddonModLessonSyncProvider extends CoreCourseActivitySyncBaseProvid await AddonModLessonOffline.deleteAttempt(lesson.id, retake, pageId, timemodified, siteId); } catch (error) { - if (!error || !CoreUtils.isWebServiceError(error)) { + if (!CoreWSError.isWebServiceError(error)) { // Couldn't connect to server. throw error; } @@ -408,7 +409,7 @@ export class AddonModLessonSyncProvider extends CoreCourseActivitySyncBaseProvid siteId?: string, ): Promise { // Attempts sent or there was none. If there is a finished retake, send it. - const retake = await CoreUtils.ignoreErrors(AddonModLessonOffline.getRetake(lessonId, siteId)); + const retake = await CorePromiseUtils.ignoreErrors(AddonModLessonOffline.getRetake(lessonId, siteId)); if (!retake) { // No retake to sync. @@ -472,7 +473,7 @@ export class AddonModLessonSyncProvider extends CoreCourseActivitySyncBaseProvid await AddonModLessonOffline.deleteRetake(lessonId, siteId); } catch (error) { - if (!error || !CoreUtils.isWebServiceError(error)) { + if (!CoreWSError.isWebServiceError(error)) { // Couldn't connect to server. throw error; } @@ -499,9 +500,22 @@ export type AddonModLessonSyncResult = CoreSyncResult & { }; /** - * Data passed to AUTO_SYNCED event. + * Data passed to ADDON_MOD_LESSON_AUTO_SYNCED event. */ export type AddonModLessonAutoSyncData = { lessonId: number; warnings: string[]; }; + +declare module '@singletons/events' { + + /** + * Augment CoreEventsData interface with events specific to this service. + * + * @see https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation + */ + export interface CoreEventsData { + [ADDON_MOD_LESSON_AUTO_SYNCED]: AddonModLessonAutoSyncData; + } + +} diff --git a/src/addons/mod/lesson/services/lesson.ts b/src/addons/mod/lesson/services/lesson.ts index b9dcd410517..b877c636f3b 100644 --- a/src/addons/mod/lesson/services/lesson.ts +++ b/src/addons/mod/lesson/services/lesson.ts @@ -14,22 +14,19 @@ import { Injectable } from '@angular/core'; import { CoreError } from '@classes/errors/error'; -import { CoreSite } from '@classes/sites/site'; import { CoreCourseCommonModWSOptions } from '@features/course/services/course'; import { CoreCourseLogHelper } from '@features/course/services/log-helper'; import { CoreSites, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@services/sites'; import { convertTextToHTMLElement } from '@/core/utils/create-html-element'; import { CoreText } from '@singletons/text'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { CoreWSExternalFile, CoreWSExternalWarning } from '@services/ws'; import { makeSingleton, Translate } from '@singletons'; import { CoreEvents } from '@singletons/events'; import { AddonModLessonPasswordDBRecord, PASSWORD_TABLE_NAME } from './database/lesson'; import { AddonModLessonOffline, AddonModLessonPageAttemptRecord } from './lesson-offline'; -import { AddonModLessonAutoSyncData } from './lesson-sync'; import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site'; import { - ADDON_MOD_LESSON_AUTO_SYNCED, ADDON_MOD_LESSON_COMPONENT, ADDON_MOD_LESSON_DATA_SENT_EVENT, ADDON_MOD_LESSON_OTHER_ANSWERS, @@ -38,6 +35,10 @@ import { AddonModLessonPageSubtype, } from '../constants'; import { CoreGradeType } from '@features/grades/constants'; +import { CoreCacheUpdateFrequency } from '@/core/constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; +import { CoreObject } from '@singletons/object'; +import { CoreArray } from '@singletons/array'; declare module '@singletons/events' { @@ -48,7 +49,6 @@ declare module '@singletons/events' { */ export interface CoreEventsData { [ADDON_MOD_LESSON_DATA_SENT_EVENT]: AddonModLessonDataSentData; - [ADDON_MOD_LESSON_AUTO_SYNCED]: AddonModLessonAutoSyncData; } } @@ -288,7 +288,7 @@ export class AddonModLessonProvider { const validPages = {}; let pageId = accessInfo.firstpageid; - viewedPagesIds = CoreUtils.mergeArraysWithoutDuplicates(viewedPagesIds, viewedContentPagesIds); + viewedPagesIds = CoreArray.mergeWithoutDuplicates(viewedPagesIds, viewedContentPagesIds); // Filter out the following pages: // - End of Cluster @@ -400,11 +400,11 @@ export class AddonModLessonProvider { // The name was changed to "answer_editor" in 3.7. Before it was just "answer". Support both cases. if (data['answer_editor[text]'] !== undefined) { studentAnswer = data['answer_editor[text]']; - } else if (typeof data.answer_editor == 'object') { + } else if (typeof data.answer_editor === 'object') { studentAnswer = (<{text: string}> data.answer_editor).text; } else if (data['answer[text]'] !== undefined) { studentAnswer = data['answer[text]']; - } else if (typeof data.answer == 'object') { + } else if (typeof data.answer === 'object') { studentAnswer = (<{text: string}> data.answer).text; } else { studentAnswer = data.answer; @@ -1016,7 +1016,7 @@ export class AddonModLessonProvider { siteId: options.siteId, }; - const gradeInfo = await CoreUtils.ignoreErrors(this.lessonGrade(lesson, retake, newOptions)); + const gradeInfo = await CorePromiseUtils.ignoreErrors(this.lessonGrade(lesson, retake, newOptions)); // Retake marked, now return the response. return this.processEolPage(lesson, courseId, options, gradeInfo); @@ -1160,7 +1160,7 @@ export class AddonModLessonProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getAccessInformationCacheKey(lessonId), - updateFrequency: CoreSite.FREQUENCY_OFTEN, + updateFrequency: CoreCacheUpdateFrequency.OFTEN, component: ADDON_MOD_LESSON_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. @@ -1196,7 +1196,7 @@ export class AddonModLessonProvider { const [online, offline] = await Promise.all([ this.getContentPagesViewedOnline(lessonId, retake, options), - CoreUtils.ignoreErrors( + CorePromiseUtils.ignoreErrors( AddonModLessonOffline.getRetakeAttemptsForType(lessonId, retake, type, options.siteId), ), ]); @@ -1409,7 +1409,7 @@ export class AddonModLessonProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getLessonDataCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, component: ADDON_MOD_LESSON_COMPONENT, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; @@ -1488,7 +1488,7 @@ export class AddonModLessonProvider { if (validatePassword) { // Invalidate the data and reject. - await CoreUtils.ignoreErrors(this.invalidateLessonWithPassword(lessonId, site.id)); + await CorePromiseUtils.ignoreErrors(this.invalidateLessonWithPassword(lessonId, site.id)); throw new CoreError(Translate.instant('addon.mod_lesson.loginfail')); } @@ -1710,7 +1710,7 @@ export class AddonModLessonProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getPagesCacheKey(lessonId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + updateFrequency: CoreCacheUpdateFrequency.SOMETIMES, component: ADDON_MOD_LESSON_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. @@ -1888,7 +1888,7 @@ export class AddonModLessonProvider { // Tell student how many questions they have seen, how many are required and their grade. const retake = accessInfo.attemptscount; - const gradeInfo = await CoreUtils.ignoreErrors(this.lessonGrade(lesson, retake, options)); + const gradeInfo = await CorePromiseUtils.ignoreErrors(this.lessonGrade(lesson, retake, options)); if (gradeInfo?.attempts) { if (gradeInfo.nquestions < lesson.minquestions) { this.addMessage(messages, 'addon.mod_lesson.numberofpagesviewednotice', { @@ -1946,7 +1946,7 @@ export class AddonModLessonProvider { const [online, offline] = await Promise.all([ this.getQuestionsAttemptsOnline(lessonId, retake, options), - CoreUtils.ignoreErrors(AddonModLessonOffline.getQuestionsAttempts( + CorePromiseUtils.ignoreErrors(AddonModLessonOffline.getQuestionsAttempts( lessonId, retake, options.correct, @@ -2059,7 +2059,7 @@ export class AddonModLessonProvider { }; const preSets = { cacheKey: this.getRetakesOverviewCacheKey(lessonId, groupId), - updateFrequency: CoreSite.FREQUENCY_OFTEN, + updateFrequency: CoreCacheUpdateFrequency.OFTEN, component: ADDON_MOD_LESSON_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. @@ -2273,7 +2273,7 @@ export class AddonModLessonProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getUserRetakeCacheKey(lessonId, userId, retake), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + updateFrequency: CoreCacheUpdateFrequency.SOMETIMES, component: ADDON_MOD_LESSON_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. @@ -3089,7 +3089,7 @@ export class AddonModLessonProvider { const params: AddonModLessonProcessPageWSParams = { lessonid: lessonId, pageid: pageId, - data: CoreUtils.objectToArrayOfObjects(data, 'name', 'value', true), + data: CoreObject.toArrayOfObjects(data, 'name', 'value', true), review: !!options.review, }; diff --git a/src/addons/mod/lti/services/lti.ts b/src/addons/mod/lti/services/lti.ts index 2e9df906449..84d8b62fdfa 100644 --- a/src/addons/mod/lti/services/lti.ts +++ b/src/addons/mod/lti/services/lti.ts @@ -23,10 +23,11 @@ import { CorePlatform } from '@services/platform'; import { CoreSites, CoreSitesCommonWSOptions } from '@services/sites'; import { CoreText } from '@singletons/text'; import { CoreUrl } from '@singletons/url'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreOpener } from '@singletons/opener'; import { CoreWSExternalFile, CoreWSExternalWarning } from '@services/ws'; import { makeSingleton, Translate } from '@singletons'; import { ADDON_MOD_LTI_COMPONENT } from '../constants'; +import { CoreCacheUpdateFrequency } from '@/core/constants'; /** * Service that provides some features for LTI. @@ -96,7 +97,7 @@ export class AddonModLtiProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getLtiCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, component: ADDON_MOD_LTI_COMPONENT, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; @@ -252,10 +253,10 @@ export class AddonModLtiProvider { const launcherUrl = await this.generateLauncher(url, params); if (CorePlatform.isMobile()) { - CoreUtils.openInApp(launcherUrl); + CoreOpener.openInApp(launcherUrl); } else { // In desktop open in browser, we found some cases where inapp caused JS issues. - CoreUtils.openInBrowser(launcherUrl); + CoreOpener.openInBrowser(launcherUrl); } } diff --git a/src/addons/mod/page/components/index/index.ts b/src/addons/mod/page/components/index/index.ts index ad368ca1f82..2fb49068a56 100644 --- a/src/addons/mod/page/components/index/index.ts +++ b/src/addons/mod/page/components/index/index.ts @@ -16,10 +16,11 @@ import { Component, OnInit, Optional } from '@angular/core'; import { CoreCourseModuleMainResourceComponent } from '@features/course/classes/main-resource-component'; import { CoreCourseContentsPage } from '@features/course/pages/contents/contents'; import { CoreText } from '@singletons/text'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { AddonModPagePage, AddonModPage } from '../../services/page'; import { AddonModPageHelper } from '../../services/page-helper'; import { ADDON_MOD_PAGE_COMPONENT } from '../../constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Component that displays a page. @@ -107,7 +108,7 @@ export class AddonModPageIndexComponent extends CoreCourseModuleMainResourceComp * @inheritdoc */ protected async logActivity(): Promise { - await CoreUtils.ignoreErrors(AddonModPage.logView(this.module.instance)); + await CorePromiseUtils.ignoreErrors(AddonModPage.logView(this.module.instance)); this.analyticsLogEvent('mod_page_view_page'); } diff --git a/src/addons/mod/page/services/handlers/prefetch.ts b/src/addons/mod/page/services/handlers/prefetch.ts index f00fceb8039..15f06c29084 100644 --- a/src/addons/mod/page/services/handlers/prefetch.ts +++ b/src/addons/mod/page/services/handlers/prefetch.ts @@ -16,7 +16,7 @@ import { Injectable } from '@angular/core'; import { CoreCourseResourcePrefetchHandlerBase } from '@features/course/classes/resource-prefetch-handler'; import { CoreCourse, CoreCourseAnyModuleData } from '@features/course/services/course'; import { CoreCourseModuleData } from '@features/course/services/course-helper'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { makeSingleton } from '@singletons'; import { AddonModPage } from '../page'; import { ADDON_MOD_PAGE_COMPONENT } from '../../constants'; @@ -73,7 +73,7 @@ export class AddonModPagePrefetchHandlerService extends CoreCourseResourcePrefet promises.push(AddonModPage.invalidatePageData(courseId)); promises.push(CoreCourse.invalidateModule(module.id)); - await CoreUtils.allPromises(promises); + await CorePromiseUtils.allPromises(promises); } /** diff --git a/src/addons/mod/page/services/page.ts b/src/addons/mod/page/services/page.ts index cea794dd4a0..87cc04221c2 100644 --- a/src/addons/mod/page/services/page.ts +++ b/src/addons/mod/page/services/page.ts @@ -14,16 +14,16 @@ import { Injectable } from '@angular/core'; import { CoreSitesCommonWSOptions, CoreSites } from '@services/sites'; -import { CoreSite } from '@classes/sites/site'; import { CoreWSExternalWarning, CoreWSExternalFile } from '@services/ws'; import { makeSingleton, Translate } from '@singletons'; import { CoreFilepool } from '@services/filepool'; import { CoreCourse } from '@features/course/services/course'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreCourseLogHelper } from '@features/course/services/log-helper'; import { CoreError } from '@classes/errors/error'; import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site'; import { ADDON_MOD_PAGE_COMPONENT } from '../constants'; +import { CoreCacheUpdateFrequency } from '@/core/constants'; /** * Service that provides some features for page. @@ -67,7 +67,7 @@ export class AddonModPageProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getPageCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, component: ADDON_MOD_PAGE_COMPONENT, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), }; @@ -109,7 +109,7 @@ export class AddonModPageProvider { promises.push(CoreFilepool.invalidateFilesByComponent(siteId, ADDON_MOD_PAGE_COMPONENT, moduleId)); promises.push(CoreCourse.invalidateModule(moduleId, siteId)); - return CoreUtils.allPromises(promises); + return CorePromiseUtils.allPromises(promises); } /** diff --git a/src/addons/mod/quiz/accessrules/password/services/handlers/password.ts b/src/addons/mod/quiz/accessrules/password/services/handlers/password.ts index b2328c887c2..7e0fec67f2e 100644 --- a/src/addons/mod/quiz/accessrules/password/services/handlers/password.ts +++ b/src/addons/mod/quiz/accessrules/password/services/handlers/password.ts @@ -20,7 +20,7 @@ import { AddonModQuizAttemptWSData, AddonModQuizQuizWSData } from '@addons/mod/q import { CoreSites } from '@services/sites'; import { AddonModQuizAccessPasswordDBRecord, PASSWORD_TABLE_NAME } from '../database/password'; import { AddonModQuizAccessPasswordComponent } from '../../component/password'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Handler to support password access rule. @@ -111,7 +111,7 @@ export class AddonModQuizAccessPasswordHandlerService implements AddonModQuizAcc siteId?: string, ): Promise { // If there's a password stored don't require the preflight since we'll use the stored one. - const entry = await CoreUtils.ignoreErrors(this.getPasswordEntry(quiz.id, siteId)); + const entry = await CorePromiseUtils.ignoreErrors(this.getPasswordEntry(quiz.id, siteId)); return !entry; } diff --git a/src/addons/mod/quiz/components/attempt-state/attempt-state.ts b/src/addons/mod/quiz/components/attempt-state/attempt-state.ts index 11bae29dfcd..1220361a0ef 100644 --- a/src/addons/mod/quiz/components/attempt-state/attempt-state.ts +++ b/src/addons/mod/quiz/components/attempt-state/attempt-state.ts @@ -22,7 +22,7 @@ import { toBoolean } from '@/core/transforms/boolean'; @Component({ selector: 'addon-mod-quiz-attempt-state', templateUrl: 'attempt-state.html', - styleUrls: ['attempt-state.scss'], + styleUrl: 'attempt-state.scss', }) export class AddonModQuizAttemptStateComponent implements OnChanges { diff --git a/src/addons/mod/quiz/components/index/index.ts b/src/addons/mod/quiz/components/index/index.ts index 3d6208f2619..453d459bac3 100644 --- a/src/addons/mod/quiz/components/index/index.ts +++ b/src/addons/mod/quiz/components/index/index.ts @@ -23,7 +23,7 @@ import { IonContent } from '@ionic/angular'; import { CoreNavigator } from '@services/navigator'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreText } from '@singletons/text'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { Translate } from '@singletons'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { AddonModQuizPrefetchHandler } from '../../services/handlers/prefetch'; @@ -58,7 +58,7 @@ import { QuestionDisplayOptionsMarks } from '@features/question/constants'; @Component({ selector: 'addon-mod-quiz-index', templateUrl: 'addon-mod-quiz-index.html', - styleUrls: ['index.scss'], + styleUrl: 'index.scss', }) export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComponent implements OnInit, OnDestroy { @@ -200,7 +200,7 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp if (AddonModQuiz.isQuizOffline(quiz)) { if (sync) { // Try to sync the quiz. - await CoreUtils.ignoreErrors(this.syncActivity(showErrors)); + await CorePromiseUtils.ignoreErrors(this.syncActivity(showErrors)); } } else { this.showStatusSpinner = false; @@ -375,7 +375,7 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp return; // Shouldn't happen. } - await CoreUtils.ignoreErrors(AddonModQuiz.logViewQuiz(this.quiz.id)); + await CorePromiseUtils.ignoreErrors(AddonModQuiz.logViewQuiz(this.quiz.id)); this.analyticsLogEvent('mod_quiz_view_quiz'); } @@ -440,7 +440,7 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp this.showLoading = true; this.content?.scrollToTop(); - await CoreUtils.ignoreErrors(this.refreshContent(true)); + await CorePromiseUtils.ignoreErrors(this.refreshContent(true)); this.showLoading = false; this.autoReview = undefined; diff --git a/src/addons/mod/quiz/pages/player/player.ts b/src/addons/mod/quiz/pages/player/player.ts index b3b6db14730..b15811d5680 100644 --- a/src/addons/mod/quiz/pages/player/player.ts +++ b/src/addons/mod/quiz/pages/player/player.ts @@ -28,7 +28,7 @@ import { CoreNavigator } from '@services/navigator'; import { CoreSites, CoreSitesReadingStrategy } from '@services/sites'; import { CoreSync } from '@services/sync'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { ModalController, Translate } from '@singletons'; import { CoreEvents } from '@singletons/events'; import { AddonModQuizAutoSave } from '../../classes/auto-save'; @@ -617,7 +617,7 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave { return; } - await CoreUtils.ignoreErrors(AddonModQuiz.logViewAttempt(this.attempt.id, page, this.preflightData, this.offline)); + await CorePromiseUtils.ignoreErrors(AddonModQuiz.logViewAttempt(this.attempt.id, page, this.preflightData, this.offline)); CoreAnalytics.logEvent({ type: CoreAnalyticsEventType.VIEW_ITEM, @@ -636,7 +636,7 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave { return; } - await CoreUtils.ignoreErrors( + await CorePromiseUtils.ignoreErrors( AddonModQuiz.logViewAttemptSummary(this.attempt.id, this.preflightData, this.quiz.id), ); @@ -735,7 +735,7 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave { // Some data has changed, reload the navigation. const modal = await CoreLoadings.show(); - await CoreUtils.ignoreErrors(this.loadNavigation()); + await CorePromiseUtils.ignoreErrors(this.loadNavigation()); modal.dismiss(); this.reloadNavigation = false; diff --git a/src/addons/mod/quiz/pages/review/review.ts b/src/addons/mod/quiz/pages/review/review.ts index 653475dce77..da4db89aea9 100644 --- a/src/addons/mod/quiz/pages/review/review.ts +++ b/src/addons/mod/quiz/pages/review/review.ts @@ -18,7 +18,7 @@ import { CoreQuestionHelper } from '@features/question/services/question-helper' import { IonContent } from '@ionic/angular'; import { CoreNavigator } from '@services/navigator'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreDom } from '@singletons/dom'; import { CoreTime } from '@singletons/time'; import { @@ -231,7 +231,7 @@ export class AddonModQuizReviewPage implements OnInit { promises.push(AddonModQuiz.invalidateCombinedReviewOptionsForUser(this.quiz.id)); } - await CoreUtils.ignoreErrors(Promise.all(promises)); + await CorePromiseUtils.ignoreErrors(Promise.all(promises)); try { await this.fetchData(); @@ -299,7 +299,7 @@ export class AddonModQuizReviewPage implements OnInit { } if (logInLMS) { - await CoreUtils.ignoreErrors(AddonModQuiz.logViewAttemptReview(this.attemptId, this.quiz.id)); + await CorePromiseUtils.ignoreErrors(AddonModQuiz.logViewAttemptReview(this.attemptId, this.quiz.id)); } let url = `/mod/quiz/review.php?attempt=${this.attemptId}&cmid=${this.cmId}`; diff --git a/src/addons/mod/quiz/services/access-rules-delegate.ts b/src/addons/mod/quiz/services/access-rules-delegate.ts index dc666d800c3..40b5f0c0155 100644 --- a/src/addons/mod/quiz/services/access-rules-delegate.ts +++ b/src/addons/mod/quiz/services/access-rules-delegate.ts @@ -15,7 +15,7 @@ import { Injectable, Type } from '@angular/core'; import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { makeSingleton } from '@singletons'; import { AddonModQuizAttemptWSData, AddonModQuizQuizWSData } from './quiz'; import { CoreSites } from '@services/sites'; @@ -171,9 +171,9 @@ export class AddonModQuizAccessRuleDelegateService extends CoreDelegate { rules = rules || []; - await CoreUtils.ignoreErrors(CoreUtils.allPromises(rules.map(async (rule) => { + await CorePromiseUtils.allPromisesIgnoringErrors(rules.map(async (rule) => { await this.executeFunctionOnEnabled(rule, 'getFixedPreflightData', [quiz, preflightData, attempt, prefetch, siteId]); - }))); + })); } /** @@ -216,11 +216,11 @@ export class AddonModQuizAccessRuleDelegateService extends CoreDelegate { + await CorePromiseUtils.allPromisesIgnoringErrors(rules.map(async (rule) => { const ruleRequired = await this.isPreflightCheckRequiredForRule(rule, quiz, attempt, prefetch, siteId); isRequired = isRequired || ruleRequired; - }))); + })); return isRequired; } @@ -268,13 +268,13 @@ export class AddonModQuizAccessRuleDelegateService extends CoreDelegate { rules = rules || []; - await CoreUtils.ignoreErrors(CoreUtils.allPromises(rules.map(async (rule) => { + await CorePromiseUtils.allPromisesIgnoringErrors(rules.map(async (rule) => { await this.executeFunctionOnEnabled( rule, 'notifyPreflightCheckPassed', [quiz, attempt, preflightData, prefetch, siteId], ); - }))); + })); } /** @@ -298,13 +298,13 @@ export class AddonModQuizAccessRuleDelegateService extends CoreDelegate { rules = rules || []; - await CoreUtils.ignoreErrors(CoreUtils.allPromises(rules.map(async (rule) => { + await CorePromiseUtils.allPromisesIgnoringErrors(rules.map(async (rule) => { await this.executeFunctionOnEnabled( rule, 'notifyPreflightCheckFailed', [quiz, attempt, preflightData, prefetch, siteId], ); - }))); + })); } /** diff --git a/src/addons/mod/quiz/services/handlers/prefetch.ts b/src/addons/mod/quiz/services/handlers/prefetch.ts index 14fd3ad8eb9..bb51503b24e 100644 --- a/src/addons/mod/quiz/services/handlers/prefetch.ts +++ b/src/addons/mod/quiz/services/handlers/prefetch.ts @@ -24,7 +24,7 @@ import { CoreQuestionHelper } from '@features/question/services/question-helper' import { CoreFilepool } from '@services/filepool'; import { CoreSites, CoreSitesReadingStrategy } from '@services/sites'; import { CoreText } from '@singletons/text'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreWSFile } from '@services/ws'; import { makeSingleton } from '@singletons'; import { AddonModQuizAccessRuleDelegate } from '../access-rules-delegate'; @@ -358,7 +358,7 @@ export class AddonModQuizPrefetchHandlerService extends CoreCourseActivityPrefet })); // Update the download time to prevent detecting the new attempt as an update. - promises.push(CoreUtils.ignoreErrors( + promises.push(CorePromiseUtils.ignoreErrors( CoreFilepool.updatePackageDownloadTime(siteId, ADDON_MOD_QUIZ_COMPONENT, module.id), )); } else { @@ -374,7 +374,7 @@ export class AddonModQuizPrefetchHandlerService extends CoreCourseActivityPrefet promises.push(AddonModQuiz.getAttemptAccessInformation(quiz.id, 0, modOptions)); // Last attempt. // Get course data, needed to determine upload max size if it's configured to be course limit. - promises.push(CoreUtils.ignoreErrors(CoreCourses.getCourseByField('id', courseId, siteId))); + promises.push(CorePromiseUtils.ignoreErrors(CoreCourses.getCourseByField('id', courseId, siteId))); await Promise.all(promises); @@ -493,7 +493,7 @@ export class AddonModQuizPrefetchHandlerService extends CoreCourseActivityPrefet // Get the review for each page. pages.forEach((page) => { - promises.push(CoreUtils.ignoreErrors(AddonModQuiz.getAttemptReview(attempt.id, { + promises.push(CorePromiseUtils.ignoreErrors(AddonModQuiz.getAttemptReview(attempt.id, { page, ...modOptions, // Include all options. }))); @@ -519,7 +519,7 @@ export class AddonModQuizPrefetchHandlerService extends CoreCourseActivityPrefet modOptions: CoreCourseCommonModWSOptions, ): Promise { // Get the review for all questions in same page. - const data = await CoreUtils.ignoreErrors(AddonModQuiz.getAttemptReview(attempt.id, { + const data = await CorePromiseUtils.ignoreErrors(AddonModQuiz.getAttemptReview(attempt.id, { page: -1, ...modOptions, // Include all options. })); diff --git a/src/addons/mod/quiz/services/handlers/push-click.ts b/src/addons/mod/quiz/services/handlers/push-click.ts index 22e7e10e473..0b627910673 100644 --- a/src/addons/mod/quiz/services/handlers/push-click.ts +++ b/src/addons/mod/quiz/services/handlers/push-click.ts @@ -18,12 +18,13 @@ import { CoreCourseHelper } from '@features/course/services/course-helper'; import { CorePushNotificationsClickHandler } from '@features/pushnotifications/services/push-delegate'; import { CorePushNotificationsNotificationBasicData } from '@features/pushnotifications/services/pushnotifications'; import { CoreUrl } from '@singletons/url'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { makeSingleton } from '@singletons'; import { AddonModQuiz } from '../quiz'; import { AddonModQuizHelper } from '../quiz-helper'; import { isSafeNumber } from '@/core/utils/types'; import { ADDON_MOD_QUIZ_FEATURE_NAME } from '../../constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Handler for quiz push notifications clicks. @@ -81,7 +82,7 @@ export class AddonModQuizPushClickHandlerService implements CorePushNotification return; } - await CoreUtils.ignoreErrors(AddonModQuiz.invalidateContent(moduleId, courseId, notification.site)); + await CorePromiseUtils.ignoreErrors(AddonModQuiz.invalidateContent(moduleId, courseId, notification.site)); return CoreCourseHelper.navigateToModule(moduleId, { courseId, diff --git a/src/addons/mod/quiz/services/quiz-helper.ts b/src/addons/mod/quiz/services/quiz-helper.ts index d1461a8868a..db769949792 100644 --- a/src/addons/mod/quiz/services/quiz-helper.ts +++ b/src/addons/mod/quiz/services/quiz-helper.ts @@ -20,7 +20,7 @@ import { CoreCourse } from '@features/course/services/course'; import { CoreNavigator } from '@services/navigator'; import { CoreSites, CoreSitesReadingStrategy } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreWSError } from '@classes/errors/wserror'; import { makeSingleton, Translate } from '@singletons'; import { AddonModQuizAccessRuleDelegate } from './access-rules-delegate'; import { @@ -43,6 +43,7 @@ import { CoreTimeUtils } from '@services/utils/time'; import { CoreModals } from '@services/modals'; import { CoreLoadings } from '@services/loadings'; import { convertTextToHTMLElement } from '@/core/utils/create-html-element'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Helper service that provides some features for quiz. @@ -474,7 +475,7 @@ export class AddonModQuizHelperProvider { if (options.offline) { // Get current page stored in local. - const storedAttempt = await CoreUtils.ignoreErrors( + const storedAttempt = await CorePromiseUtils.ignoreErrors( AddonModQuizOffline.getAttemptById(attempt.id), ); @@ -502,7 +503,7 @@ export class AddonModQuizHelperProvider { return attempt; } catch (error) { - if (CoreUtils.isWebServiceError(error)) { + if (CoreWSError.isWebServiceError(error)) { // The WebService returned an error, assume the preflight failed. AddonModQuizAccessRuleDelegate.notifyPreflightCheckFailed( rules, diff --git a/src/addons/mod/quiz/services/quiz-offline.ts b/src/addons/mod/quiz/services/quiz-offline.ts index 94354859b43..ff98e646a65 100644 --- a/src/addons/mod/quiz/services/quiz-offline.ts +++ b/src/addons/mod/quiz/services/quiz-offline.ts @@ -19,7 +19,7 @@ import { CoreQuestionAnswerDBRecord } from '@features/question/services/database import { CoreQuestion, CoreQuestionQuestionParsed, CoreQuestionsAnswers } from '@features/question/services/question'; import { CoreSites } from '@services/sites'; import { CoreTimeUtils } from '@services/utils/time'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { makeSingleton, Translate } from '@singletons'; import { CoreLogger } from '@singletons/logger'; import { AddonModQuizAttemptDBRecord, ATTEMPTS_TABLE_NAME } from './database/quiz'; @@ -149,7 +149,7 @@ export class AddonModQuizOfflineProvider { ): Promise { await Promise.all(questions.map(async (question) => { - const dbQuestion = await CoreUtils.ignoreErrors( + const dbQuestion = await CorePromiseUtils.ignoreErrors( CoreQuestion.getQuestion(ADDON_MOD_QUIZ_COMPONENT, attemptId, question.slot, siteId), ); @@ -192,7 +192,7 @@ export class AddonModQuizOfflineProvider { const db = await CoreSites.getSiteDb(siteId); // Check if an attempt already exists. Return a new one if it doesn't. - let entry = await CoreUtils.ignoreErrors(this.getAttemptById(attempt.id, siteId)); + let entry = await CorePromiseUtils.ignoreErrors(this.getAttemptById(attempt.id, siteId)); if (entry) { entry.timemodified = now; @@ -329,7 +329,7 @@ export class AddonModQuizOfflineProvider { try { // Answers have been saved, now we can save the questions with the states. - await CoreUtils.allPromises(Object.keys(newStates).map(async (slot) => { + await CorePromiseUtils.allPromises(Object.keys(newStates).map(async (slot) => { const question = questionsWithAnswers[Number(slot)]; await CoreQuestion.saveQuestion( diff --git a/src/addons/mod/quiz/services/quiz-sync.ts b/src/addons/mod/quiz/services/quiz-sync.ts index dd36abfb28b..400dedbaa28 100644 --- a/src/addons/mod/quiz/services/quiz-sync.ts +++ b/src/addons/mod/quiz/services/quiz-sync.ts @@ -24,7 +24,7 @@ import { CoreQuestionDelegate } from '@features/question/services/question-deleg import { CoreNetwork } from '@services/network'; import { CoreSites, CoreSitesReadingStrategy } from '@services/sites'; import { CoreSync, CoreSyncResult } from '@services/sync'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { makeSingleton, Translate } from '@singletons'; import { CoreEvents } from '@singletons/events'; import { AddonModQuizAttemptDBRecord } from './database/quiz'; @@ -65,7 +65,7 @@ export class AddonModQuizSyncProvider extends CoreCourseActivitySyncBaseProvider options = options || {}; // Invalidate the data for the quiz and attempt. - await CoreUtils.ignoreErrors( + await CorePromiseUtils.ignoreErrors( AddonModQuiz.invalidateAllQuizData(quiz.id, courseId, options.attemptId, siteId), ); @@ -99,7 +99,7 @@ export class AddonModQuizSyncProvider extends CoreCourseActivitySyncBaseProvider } } - await CoreUtils.ignoreErrors(this.setSyncTime(quiz.id, siteId)); + await CorePromiseUtils.ignoreErrors(this.setSyncTime(quiz.id, siteId)); // Check if online attempt was finished because of the sync. let attemptFinished = false; @@ -298,7 +298,7 @@ export class AddonModQuizSyncProvider extends CoreCourseActivitySyncBaseProvider this.logger.debug('Try to sync quiz ' + quiz.id + ' in site ' + siteId); // Sync offline logs. - await CoreUtils.ignoreErrors( + await CorePromiseUtils.ignoreErrors( CoreCourseLogHelper.syncActivity(ADDON_MOD_QUIZ_COMPONENT, quiz.id, siteId), ); @@ -403,7 +403,7 @@ export class AddonModQuizSyncProvider extends CoreCourseActivitySyncBaseProvider if (!finish) { // Answers sent, now set the current page. - await CoreUtils.ignoreErrors(AddonModQuiz.logViewAttempt( + await CorePromiseUtils.ignoreErrors(AddonModQuiz.logViewAttempt( onlineAttempt.id, offlineAttempt.currentpage, preflightData, @@ -496,10 +496,23 @@ type FinishSyncOptions = { }; /** - * Data passed to AUTO_SYNCED event. + * Data passed to ADDON_MOD_QUIZ_AUTO_SYNCED event. */ export type AddonModQuizAutoSyncData = { quizId: number; attemptFinished: boolean; warnings: string[]; }; + +declare module '@singletons/events' { + + /** + * Augment CoreEventsData interface with events specific to this service. + * + * @see https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation + */ + export interface CoreEventsData { + [ADDON_MOD_QUIZ_AUTO_SYNCED]: AddonModQuizAutoSyncData; + } + +} diff --git a/src/addons/mod/quiz/services/quiz.ts b/src/addons/mod/quiz/services/quiz.ts index 7384b276ec0..938081dbb52 100644 --- a/src/addons/mod/quiz/services/quiz.ts +++ b/src/addons/mod/quiz/services/quiz.ts @@ -17,7 +17,6 @@ import { Injectable } from '@angular/core'; import { CoreError } from '@classes/errors/error'; import { CoreWSError } from '@classes/errors/wserror'; -import { CoreSite } from '@classes/sites/site'; import { CoreCourseCommonModWSOptions } from '@features/course/services/course'; import { CoreCourseLogHelper } from '@features/course/services/log-helper'; import { CoreGradesFormattedItem, CoreGradesHelper } from '@features/grades/services/grades-helper'; @@ -31,13 +30,12 @@ import { CoreQuestionDelegate } from '@features/question/services/question-deleg import { CoreSites, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@services/sites'; import { convertTextToHTMLElement } from '@/core/utils/create-html-element'; import { CoreTimeUtils } from '@services/utils/time'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { CoreStatusWithWarningsWSResponse, CoreWSExternalFile, CoreWSExternalWarning } from '@services/ws'; import { makeSingleton, Translate } from '@singletons'; import { CoreLogger } from '@singletons/logger'; import { AddonModQuizAccessRuleDelegate } from './access-rules-delegate'; import { AddonModQuizOffline, AddonModQuizQuestionsWithAnswers } from './quiz-offline'; -import { AddonModQuizAutoSyncData } from './quiz-sync'; import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site'; import { QUESTION_INVALID_STATE_CLASSES, @@ -52,9 +50,11 @@ import { AddonModQuizGradeMethods, AddonModQuizDisplayOptionsAttemptStates, ADDON_MOD_QUIZ_IMMEDIATELY_AFTER_PERIOD, - ADDON_MOD_QUIZ_AUTO_SYNCED, } from '../constants'; import { CoreIonicColorNames } from '@singletons/colors'; +import { CoreCacheUpdateFrequency } from '@/core/constants'; +import { CoreObject } from '@singletons/object'; +import { CoreArray } from '@singletons/array'; declare module '@singletons/events' { @@ -65,7 +65,6 @@ declare module '@singletons/events' { */ export interface CoreEventsData { [ADDON_MOD_QUIZ_ATTEMPT_FINISHED_EVENT]: AddonModQuizAttemptFinishedData; - [ADDON_MOD_QUIZ_AUTO_SYNCED]: AddonModQuizAutoSyncData; } } @@ -230,7 +229,7 @@ export class AddonModQuizProvider { const params: AddonModQuizGetAttemptDataWSParams = { attemptid: attemptId, page: page, - preflightdata: CoreUtils.objectToArrayOfObjects( + preflightdata: CoreObject.toArrayOfObjects( preflightData, 'name', 'value', @@ -553,7 +552,7 @@ export class AddonModQuizProvider { const params: AddonModQuizGetAttemptSummaryWSParams = { attemptid: attemptId, - preflightdata: CoreUtils.objectToArrayOfObjects( + preflightdata: CoreObject.toArrayOfObjects( preflightData, 'name', 'value', @@ -632,8 +631,8 @@ export class AddonModQuizProvider { // Convert the arrays to objects with name -> value. return { - someoptions: > CoreUtils.objectToKeyValueMap(response.someoptions, 'name', 'value'), - alloptions: > CoreUtils.objectToKeyValueMap(response.alloptions, 'name', 'value'), + someoptions: > CoreObject.toKeyValueMap(response.someoptions, 'name', 'value'), + alloptions: > CoreObject.toKeyValueMap(response.alloptions, 'name', 'value'), warnings: response.warnings, }; } @@ -680,7 +679,7 @@ export class AddonModQuizProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getFeedbackForGradeCacheKey(quizId, grade), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, component: ADDON_MOD_QUIZ_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. @@ -820,7 +819,7 @@ export class AddonModQuizProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getQuizDataCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, component: ADDON_MOD_QUIZ_COMPONENT, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; @@ -955,7 +954,7 @@ export class AddonModQuizProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getQuizRequiredQtypesCacheKey(quizId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + updateFrequency: CoreCacheUpdateFrequency.SOMETIMES, component: ADDON_MOD_QUIZ_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. @@ -1119,7 +1118,7 @@ export class AddonModQuizProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getUserAttemptsCacheKey(quizId, userId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + updateFrequency: CoreCacheUpdateFrequency.SOMETIMES, component: ADDON_MOD_QUIZ_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. @@ -1643,7 +1642,7 @@ export class AddonModQuizProvider { const params: AddonModQuizViewAttemptWSParams = { attemptid: attemptId, page: page, - preflightdata: CoreUtils.objectToArrayOfObjects( + preflightdata: CoreObject.toArrayOfObjects( preflightData, 'name', 'value', @@ -1698,7 +1697,7 @@ export class AddonModQuizProvider { ): Promise { const params: AddonModQuizViewAttemptSummaryWSParams = { attemptid: attemptId, - preflightdata: CoreUtils.objectToArrayOfObjects( + preflightdata: CoreObject.toArrayOfObjects( preflightData, 'name', 'value', @@ -1788,10 +1787,10 @@ export class AddonModQuizProvider { const params: AddonModQuizProcessAttemptWSParams = { attemptid: attemptId, - data: CoreUtils.objectToArrayOfObjects(data, 'name', 'value'), + data: CoreObject.toArrayOfObjects(data, 'name', 'value'), finishattempt: !!finish, timeup: !!timeUp, - preflightdata: CoreUtils.objectToArrayOfObjects( + preflightdata: CoreObject.toArrayOfObjects( preflightData, 'name', 'value', @@ -1837,7 +1836,7 @@ export class AddonModQuizProvider { }); // Convert the question array to an object. - const questions = CoreUtils.arrayToObject(questionsArray, 'slot'); + const questions = CoreArray.toObject(questionsArray, 'slot'); return AddonModQuizOffline.processAttempt(quiz, attempt, questions, data, finish, siteId); } @@ -1942,8 +1941,8 @@ export class AddonModQuizProvider { const params: AddonModQuizSaveAttemptWSParams = { attemptid: attemptId, - data: CoreUtils.objectToArrayOfObjects(data, 'name', 'value'), - preflightdata: CoreUtils.objectToArrayOfObjects( + data: CoreObject.toArrayOfObjects(data, 'name', 'value'), + preflightdata: CoreObject.toArrayOfObjects( preflightData, 'name', 'value', @@ -1998,7 +1997,7 @@ export class AddonModQuizProvider { const params: AddonModQuizStartAttemptWSParams = { quizid: quizId, - preflightdata: CoreUtils.objectToArrayOfObjects( + preflightdata: CoreObject.toArrayOfObjects( preflightData, 'name', 'value', diff --git a/src/addons/mod/resource/components/index/index.ts b/src/addons/mod/resource/components/index/index.ts index 27f536692ad..58a961e5693 100644 --- a/src/addons/mod/resource/components/index/index.ts +++ b/src/addons/mod/resource/components/index/index.ts @@ -25,7 +25,6 @@ import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreMimetypeUtils } from '@services/utils/mimetype'; import { CoreText } from '@singletons/text'; -import { CoreUtils, OpenFileAction } from '@services/utils/utils'; import { NgZone, Translate } from '@singletons'; import { Subscription } from 'rxjs'; import { @@ -35,6 +34,8 @@ import { import { AddonModResourceHelper } from '../../services/resource-helper'; import { CorePlatform } from '@services/platform'; import { ADDON_MOD_RESOURCE_COMPONENT } from '../../constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; +import { OpenFileAction } from '@singletons/opener'; /** * Component that displays a resource. @@ -42,7 +43,7 @@ import { ADDON_MOD_RESOURCE_COMPONENT } from '../../constants'; @Component({ selector: 'addon-mod-resource-index', templateUrl: 'addon-mod-resource-index.html', - styleUrls: ['index.scss'], + styleUrl: 'index.scss', }) export class AddonModResourceIndexComponent extends CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy { @@ -175,7 +176,7 @@ export class AddonModResourceIndexComponent extends CoreCourseModuleMainResource this.readableSize = CoreText.bytesToSize(this.module.contentsinfo.filessize, 1); this.timemodified = this.module.contentsinfo.lastmodified * 1000; } else { - mimetype = await CoreUtils.getMimeTypeFromUrl(CoreFileHelper.getFileUrl(contents[0])); + mimetype = await CoreMimetypeUtils.getMimeTypeFromUrl(CoreFileHelper.getFileUrl(contents[0])); this.readableSize = CoreText.bytesToSize(contents[0].filesize, 1); this.timemodified = contents[0].timemodified * 1000; } @@ -191,7 +192,7 @@ export class AddonModResourceIndexComponent extends CoreCourseModuleMainResource * @inheritdoc */ protected async logActivity(): Promise { - await CoreUtils.ignoreErrors(AddonModResource.logView(this.module.instance)); + await CorePromiseUtils.ignoreErrors(AddonModResource.logView(this.module.instance)); this.analyticsLogEvent('mod_resource_view_resource'); } diff --git a/src/addons/mod/resource/services/handlers/module.ts b/src/addons/mod/resource/services/handlers/module.ts index f942936091b..8c97dd531b1 100644 --- a/src/addons/mod/resource/services/handlers/module.ts +++ b/src/addons/mod/resource/services/handlers/module.ts @@ -24,7 +24,7 @@ import { CoreMimetypeUtils } from '@services/utils/mimetype'; import { makeSingleton, Translate } from '@singletons'; import { AddonModResource } from '../resource'; import { AddonModResourceHelper } from '../resource-helper'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { ADDON_MOD_RESOURCE_PAGE_NAME } from '../../constants'; /** @@ -92,8 +92,8 @@ export class AddonModResourceModuleHandlerService extends CoreModuleHandlerBase }; const [hideButton, extraBadge] = await Promise.all([ - CoreUtils.ignoreErrors(this.hideOpenButton(module)), - CoreUtils.ignoreErrors(AddonModResourceHelper.getAfterLinkDetails(module, courseId)), + CorePromiseUtils.ignoreErrors(this.hideOpenButton(module)), + CorePromiseUtils.ignoreErrors(AddonModResourceHelper.getAfterLinkDetails(module, courseId)), ]); // Check if the button needs to be shown or not. diff --git a/src/addons/mod/resource/services/resource-helper.ts b/src/addons/mod/resource/services/resource-helper.ts index 0f9fc20b676..84ce659f7e7 100644 --- a/src/addons/mod/resource/services/resource-helper.ts +++ b/src/addons/mod/resource/services/resource-helper.ts @@ -24,7 +24,6 @@ import { CoreFilepool } from '@services/filepool'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreMimetypeUtils } from '@services/utils/mimetype'; -import { CoreUtilsOpenFileOptions } from '@services/utils/utils'; import { makeSingleton, Translate } from '@singletons'; import { CorePath } from '@singletons/path'; import { AddonModResource, AddonModResourceCustomData } from './resource'; @@ -33,6 +32,7 @@ import { CoreText } from '@singletons/text'; import { CoreTimeUtils } from '@services/utils/time'; import { ADDON_MOD_RESOURCE_COMPONENT } from '../constants'; import { CoreLoadings } from '@services/loadings'; +import { CoreOpenerOpenFileOptions } from '@singletons/opener'; /** * Service that provides helper functions for resources. @@ -190,7 +190,7 @@ export class AddonModResourceHelperProvider { * @param options Options to open the file. * @returns Resolved when done. */ - async openModuleFile(module: CoreCourseModuleData, courseId: number, options: CoreUtilsOpenFileOptions = {}): Promise { + async openModuleFile(module: CoreCourseModuleData, courseId: number, options: CoreOpenerOpenFileOptions = {}): Promise { const modal = await CoreLoadings.show(); try { diff --git a/src/addons/mod/resource/services/resource.ts b/src/addons/mod/resource/services/resource.ts index 3dfaa62dc0a..ef5702d95a0 100644 --- a/src/addons/mod/resource/services/resource.ts +++ b/src/addons/mod/resource/services/resource.ts @@ -15,15 +15,15 @@ import { Injectable } from '@angular/core'; import { CoreError } from '@classes/errors/error'; import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site'; -import { CoreSite } from '@classes/sites/site'; import { CoreCourse } from '@features/course/services/course'; import { CoreCourseLogHelper } from '@features/course/services/log-helper'; import { CoreFilepool } from '@services/filepool'; import { CoreSites, CoreSitesCommonWSOptions } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreWSExternalFile, CoreWSExternalWarning } from '@services/ws'; import { makeSingleton, Translate } from '@singletons'; import { ADDON_MOD_RESOURCE_COMPONENT } from '../constants'; +import { CoreCacheUpdateFrequency } from '@/core/constants'; /** * Service that provides some features for resources. @@ -66,7 +66,7 @@ export class AddonModResourceProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getResourceCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, component: ADDON_MOD_RESOURCE_COMPONENT, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), }; @@ -114,7 +114,7 @@ export class AddonModResourceProvider { promises.push(CoreFilepool.invalidateFilesByComponent(siteId, ADDON_MOD_RESOURCE_COMPONENT, moduleId)); promises.push(CoreCourse.invalidateModule(moduleId, siteId, 'resource')); - await CoreUtils.allPromises(promises); + await CorePromiseUtils.allPromises(promises); } /** diff --git a/src/addons/mod/scorm/classes/data-model-12.ts b/src/addons/mod/scorm/classes/data-model-12.ts index 9e1bfa44328..ff82db0406a 100644 --- a/src/addons/mod/scorm/classes/data-model-12.ts +++ b/src/addons/mod/scorm/classes/data-model-12.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { CoreEvents } from '@singletons/events'; import { AddonModScorm, diff --git a/src/addons/mod/scorm/components/index/index.ts b/src/addons/mod/scorm/components/index/index.ts index eb9febdb4e6..93d8d1da35c 100644 --- a/src/addons/mod/scorm/components/index/index.ts +++ b/src/addons/mod/scorm/components/index/index.ts @@ -22,7 +22,7 @@ import { IonContent } from '@ionic/angular'; import { CoreNavigator } from '@services/navigator'; import { CoreSync } from '@services/sync'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreObject } from '@singletons/object'; import { Translate } from '@singletons'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { AddonModScormPrefetchHandler } from '../../services/handlers/prefetch'; @@ -50,6 +50,7 @@ import { ADDON_MOD_SCORM_PAGE_NAME, } from '../../constants'; import { CoreWait } from '@singletons/wait'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Component that displays a SCORM entry page. @@ -57,7 +58,7 @@ import { CoreWait } from '@singletons/wait'; @Component({ selector: 'addon-mod-scorm-index', templateUrl: 'addon-mod-scorm-index.html', - styleUrls: ['index.scss'], + styleUrl: 'index.scss', }) export class AddonModScormIndexComponent extends CoreCourseModuleMainActivityComponent implements OnInit { @@ -188,7 +189,7 @@ export class AddonModScormIndexComponent extends CoreCourseModuleMainActivityCom if (sync) { // Try to synchronize the SCORM. - await CoreUtils.ignoreErrors(this.syncActivity(showErrors)); + await CorePromiseUtils.ignoreErrors(this.syncActivity(showErrors)); } const [syncTime, accessInfo] = await Promise.all([ @@ -339,8 +340,8 @@ export class AddonModScormIndexComponent extends CoreCourseModuleMainActivityCom this.grade = AddonModScorm.calculateScormGrade(scorm, onlineAttempts); // Add the attempts to the SCORM in array format in ASC order, and format the grades. - this.onlineAttempts = CoreUtils.objectToArray(onlineAttempts); - this.offlineAttempts = CoreUtils.objectToArray(offlineAttempts); + this.onlineAttempts = CoreObject.toArray(onlineAttempts); + this.offlineAttempts = CoreObject.toArray(offlineAttempts); this.onlineAttempts.sort((a, b) => a.num - b.num); this.offlineAttempts.sort((a, b) => a.num - b.num); @@ -363,7 +364,7 @@ export class AddonModScormIndexComponent extends CoreCourseModuleMainActivityCom return; // Shouldn't happen. } - await CoreUtils.ignoreErrors(AddonModScorm.logView(this.scorm.id)); + await CorePromiseUtils.ignoreErrors(AddonModScorm.logView(this.scorm.id)); this.analyticsLogEvent('mod_scorm_view_scorm'); } @@ -516,7 +517,7 @@ export class AddonModScormIndexComponent extends CoreCourseModuleMainActivityCom await AddonModScormHelper.confirmDownload(scorm, isOutdated); // Invalidate WS data if SCORM is outdated. if (isOutdated) { - await CoreUtils.ignoreErrors(AddonModScorm.invalidateAllScormData(scorm.id)); + await CorePromiseUtils.ignoreErrors(AddonModScorm.invalidateAllScormData(scorm.id)); } try { @@ -626,7 +627,7 @@ export class AddonModScormIndexComponent extends CoreCourseModuleMainActivityCom if (!result.updated && this.dataSent) { // The user sent data to server, but not in the sync process. Check if we need to fetch data. - await CoreUtils.ignoreErrors( + await CorePromiseUtils.ignoreErrors( AddonModScormSync.prefetchAfterUpdate(AddonModScormPrefetchHandler.instance, this.module, this.courseId), ); } diff --git a/src/addons/mod/scorm/pages/player/player.ts b/src/addons/mod/scorm/pages/player/player.ts index e3d29384db8..7d6cd920a76 100644 --- a/src/addons/mod/scorm/pages/player/player.ts +++ b/src/addons/mod/scorm/pages/player/player.ts @@ -20,7 +20,7 @@ import { CoreSites, CoreSitesReadingStrategy } from '@services/sites'; import { CoreSync } from '@services/sync'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreTimeUtils } from '@services/utils/time'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { AddonModScormDataModel12 } from '../../classes/data-model-12'; import { @@ -540,7 +540,7 @@ export class AddonModScormPlayerPage implements OnInit, OnDestroy { */ protected async refreshToc(): Promise { try { - await CoreUtils.ignoreErrors(AddonModScorm.invalidateAllScormData(this.scorm.id)); + await CorePromiseUtils.ignoreErrors(AddonModScorm.invalidateAllScormData(this.scorm.id)); await this.fetchToc(); } catch (error) { @@ -571,7 +571,7 @@ export class AddonModScormPlayerPage implements OnInit, OnDestroy { } // New online attempt created, update cached data about online attempts. - await CoreUtils.ignoreErrors(AddonModScorm.getAttemptCount(this.scorm.id, { + await CorePromiseUtils.ignoreErrors(AddonModScorm.getAttemptCount(this.scorm.id, { cmId: this.cmId, readingStrategy: CoreSitesReadingStrategy.ONLY_NETWORK, })); @@ -581,7 +581,7 @@ export class AddonModScormPlayerPage implements OnInit, OnDestroy { * Log event. */ protected async logEvent(scoId: number): Promise { - await CoreUtils.ignoreErrors(AddonModScorm.logLaunchSco(this.scorm.id, scoId)); + await CorePromiseUtils.ignoreErrors(AddonModScorm.logLaunchSco(this.scorm.id, scoId)); let url = '/mod/scorm/player.php'; if (this.scorm.popup) { diff --git a/src/addons/mod/scorm/services/handlers/prefetch.ts b/src/addons/mod/scorm/services/handlers/prefetch.ts index eb5b03f81a2..bd31f7e15a1 100644 --- a/src/addons/mod/scorm/services/handlers/prefetch.ts +++ b/src/addons/mod/scorm/services/handlers/prefetch.ts @@ -21,7 +21,7 @@ import { CoreFilepool } from '@services/filepool'; import { CorePlatform } from '@services/platform'; import { CoreFileSizeSum } from '@services/plugin-file-delegate'; import { CoreSites, CoreSitesReadingStrategy } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreWSFile } from '@services/ws'; import { makeSingleton, Translate } from '@singletons'; import { AddonModScorm, AddonModScormScorm } from '../scorm'; @@ -84,9 +84,11 @@ export class AddonModScormPrefetchHandlerService extends CoreCourseActivityPrefe this.downloadOrPrefetchMainFileIfNeeded(scorm, prefetch, onProgress, siteId), // Download WS data. If it fails we don't want to fail the whole download, so we'll ignore the error for now. // @todo Implement a warning system so the user knows which SCORMs have failed. - CoreUtils.ignoreErrors(this.fetchWSData(scorm, siteId)), + CorePromiseUtils.ignoreErrors(this.fetchWSData(scorm, siteId)), // Download intro files, ignoring errors. - CoreUtils.ignoreErrors(CoreFilepool.downloadOrPrefetchFiles(siteId, files, prefetch, false, this.component, module.id)), + CorePromiseUtils.ignoreErrors( + CoreFilepool.downloadOrPrefetchFiles(siteId, files, prefetch, false, this.component, module.id), + ), ]); // Success, return the hash. @@ -154,7 +156,7 @@ export class AddonModScormPrefetchHandlerService extends CoreCourseActivityPrefe (event: ProgressEvent) => this.downloadProgress(false, onProgress, event), ); - await CoreUtils.ignoreErrors(CoreFilepool.removeFileByUrl(siteId, packageUrl)); + await CorePromiseUtils.ignoreErrors(CoreFilepool.removeFileByUrl(siteId, packageUrl)); } /** @@ -238,7 +240,7 @@ export class AddonModScormPrefetchHandlerService extends CoreCourseActivityPrefe */ async fetchAttempts(scorm: AddonModScormScorm, modOptions: CoreCourseCommonModWSOptions): Promise { // If it fails, assume we have no attempts. - const numAttempts = await CoreUtils.ignoreErrors(AddonModScorm.getAttemptCountOnline(scorm.id, modOptions), 0); + const numAttempts = await CorePromiseUtils.ignoreErrors(AddonModScorm.getAttemptCountOnline(scorm.id, modOptions), 0); if (numAttempts <= 0) { // No attempts. We'll still try to get user data to be able to identify SCOs not visible and so. diff --git a/src/addons/mod/scorm/services/scorm-helper.ts b/src/addons/mod/scorm/services/scorm-helper.ts index 2986d3b9944..adadfc273ba 100644 --- a/src/addons/mod/scorm/services/scorm-helper.ts +++ b/src/addons/mod/scorm/services/scorm-helper.ts @@ -17,7 +17,7 @@ import { CoreError } from '@classes/errors/error'; import { CoreCourseCommonModWSOptions } from '@features/course/services/course'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { makeSingleton, Translate } from '@singletons'; import { AddonModScorm, @@ -33,6 +33,7 @@ import { } from './scorm'; import { AddonModScormOffline } from './scorm-offline'; import { AddonModScormMode } from '../constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; // List of elements we want to ignore when copying attempts (they're calculated). const elementsToIgnore = [ @@ -86,7 +87,7 @@ export class AddonModScormHelperProvider { siteId = siteId || CoreSites.getCurrentSiteId(); // Get data from the online attempt. - const onlineData = await CoreUtils.ignoreErrors( + const onlineData = await CorePromiseUtils.ignoreErrors( AddonModScorm.getScormUserData(scorm.id, attempt, { cmId: scorm.coursemodule, siteId }), ); @@ -97,7 +98,7 @@ export class AddonModScormHelperProvider { // The SCORM API might have written some data to the offline attempt already. // We don't want to override it with cached online data. - const offlineData = await CoreUtils.ignoreErrors( + const offlineData = await CorePromiseUtils.ignoreErrors( AddonModScormOffline.getScormUserData(scorm.id, attempt, undefined, siteId), ); @@ -143,7 +144,7 @@ export class AddonModScormHelperProvider { siteId = siteId || CoreSites.getCurrentSiteId(); // Try to get data from online attempts. - const userData = await CoreUtils.ignoreErrors( + const userData = await CorePromiseUtils.ignoreErrors( this.searchOnlineAttemptUserData(scorm.id, lastOnline, { cmId: scorm.coursemodule, siteId }), ); diff --git a/src/addons/mod/scorm/services/scorm-offline.ts b/src/addons/mod/scorm/services/scorm-offline.ts index 9ba1aa1f5e4..7105aa9470b 100644 --- a/src/addons/mod/scorm/services/scorm-offline.ts +++ b/src/addons/mod/scorm/services/scorm-offline.ts @@ -18,7 +18,7 @@ import { CoreSites } from '@services/sites'; import { CoreSync } from '@services/sync'; import { CoreText } from '@singletons/text'; import { CoreTimeUtils } from '@services/utils/time'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { makeSingleton } from '@singletons'; import { CoreLogger } from '@singletons/logger'; import { @@ -45,6 +45,7 @@ import { asyncInstance, AsyncInstance } from '@/core/utils/async-instance'; import { CoreDatabaseTable } from '@classes/database/database-table'; import { CoreDatabaseCachingStrategy } from '@classes/database/database-table-proxy'; import { ADDON_MOD_SCORM_COMPONENT } from '../constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Service to handle offline SCORM. @@ -508,7 +509,7 @@ export class AddonModScormOfflineProvider { fullName = site.getInfo()?.fullname || ''; userName = site.getInfo()?.username || ''; } else { - const profile = await CoreUtils.ignoreErrors(CoreUser.getProfile(userId)); + const profile = await CorePromiseUtils.ignoreErrors(CoreUser.getProfile(userId)); fullName = profile?.fullname || ''; userName = profile?.username || ''; diff --git a/src/addons/mod/scorm/services/scorm-sync.ts b/src/addons/mod/scorm/services/scorm-sync.ts index 3c8ae7bb763..5a40a0f35f2 100644 --- a/src/addons/mod/scorm/services/scorm-sync.ts +++ b/src/addons/mod/scorm/services/scorm-sync.ts @@ -19,7 +19,7 @@ import { CoreCourse } from '@features/course/services/course'; import { CoreCourseLogHelper } from '@features/course/services/log-helper'; import { CoreSites, CoreSitesReadingStrategy } from '@services/sites'; import { CoreSync, CoreSyncResult } from '@services/sync'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { makeSingleton, Translate } from '@singletons'; import { CoreEvents } from '@singletons/events'; import { AddonModScormPrefetchHandler } from './handlers/prefetch'; @@ -91,7 +91,7 @@ export class AddonModScormSyncProvider extends CoreCourseActivitySyncBaseProvide // It can't be added because the last offline attempt is incomplete, delete it. this.logger.debug(`Try to delete attempt ${attempt} because it cannot be created as a new attempt.`); - await CoreUtils.ignoreErrors(AddonModScormOffline.deleteAttempt(scormId, attempt, siteId)); + await CorePromiseUtils.ignoreErrors(AddonModScormOffline.deleteAttempt(scormId, attempt, siteId)); // eslint-disable-next-line id-blacklist warnings.push(Translate.instant('addon.mod_scorm.warningofflinedatadeleted', { number: attempt })); @@ -159,7 +159,7 @@ export class AddonModScormSyncProvider extends CoreCourseActivitySyncBaseProvide return; } - await CoreUtils.allPromises(times.map((time, index) => { + await CorePromiseUtils.allPromises(times.map((time, index) => { const attempt = newAttempts[time]; return AddonModScormOffline.changeAttemptNumber(scormId, attempt, lastOffline + index + 1, siteId); @@ -204,7 +204,7 @@ export class AddonModScormSyncProvider extends CoreCourseActivitySyncBaseProvide } } - await CoreUtils.ignoreErrors(this.setSyncTime(scorm.id, siteId)); + await CorePromiseUtils.ignoreErrors(this.setSyncTime(scorm.id, siteId)); if (!initialCount) { return result; @@ -324,7 +324,7 @@ export class AddonModScormSyncProvider extends CoreCourseActivitySyncBaseProvide } catch (error) { // Moving the new attempts failed (it shouldn't happen). Let's undo the new attempts move. - await CoreUtils.allPromises(successful.map((attempt) => { + await CorePromiseUtils.allPromises(successful.map((attempt) => { const newNumber = lastOnline + newAttempts.indexOf(attempt) + 1; return AddonModScormOffline.changeAttemptNumber(scormId, newNumber, attempt, siteId); @@ -369,7 +369,7 @@ export class AddonModScormSyncProvider extends CoreCourseActivitySyncBaseProvide } catch { // Error getting user data from the site. We'll have to build it ourselves. // Let's try to get cached data about the attempt. - userData = await CoreUtils.ignoreErrors( + userData = await CorePromiseUtils.ignoreErrors( AddonModScorm.getScormUserData(scormId, attempt, { cmId, siteId }), {}, ); @@ -528,12 +528,12 @@ export class AddonModScormSyncProvider extends CoreCourseActivitySyncBaseProvide await AddonModScorm.saveTracksOnline(scormId, scoId, attempt, tracks, siteId); // Sco data successfully sent. Mark them as synced. This is needed because some SCOs sync might fail. - await CoreUtils.ignoreErrors(AddonModScormOffline.markAsSynced(scormId, attempt, scoId, siteId)); + await CorePromiseUtils.ignoreErrors(AddonModScormOffline.markAsSynced(scormId, attempt, scoId, siteId)); somethingSynced = true; }); - await CoreUtils.allPromises(promises); + await CorePromiseUtils.allPromises(promises); } catch (error) { if (somethingSynced) { // Some SCOs have been synced and some not. @@ -549,7 +549,7 @@ export class AddonModScormSyncProvider extends CoreCourseActivitySyncBaseProvide } // Attempt has been sent. Let's delete it from local. - await CoreUtils.ignoreErrors(AddonModScormOffline.deleteAttempt(scormId, attempt, siteId)); + await CorePromiseUtils.ignoreErrors(AddonModScormOffline.deleteAttempt(scormId, attempt, siteId)); } /** @@ -610,7 +610,7 @@ export class AddonModScormSyncProvider extends CoreCourseActivitySyncBaseProvide let lastOnlineWasFinished = false; // Sync offline logs. - await CoreUtils.ignoreErrors(CoreCourseLogHelper.syncActivity(ADDON_MOD_SCORM_COMPONENT, scorm.id, siteId)); + await CorePromiseUtils.ignoreErrors(CoreCourseLogHelper.syncActivity(ADDON_MOD_SCORM_COMPONENT, scorm.id, siteId)); // Get attempts data. We ignore cache for online attempts, so this call will fail if offline or server down. const attemptsData = await AddonModScorm.getAttemptCount(scorm.id, { @@ -761,7 +761,7 @@ export class AddonModScormSyncProvider extends CoreCourseActivitySyncBaseProvide if (!hasDataToSend) { // Nothing to sync, delete the attempt. - return CoreUtils.ignoreErrors(AddonModScormOffline.deleteAttempt(scormId, attempt, siteId)); + return CorePromiseUtils.ignoreErrors(AddonModScormOffline.deleteAttempt(scormId, attempt, siteId)); } // There are elements to sync. We need to check if it's possible to sync them or not. @@ -850,3 +850,16 @@ export type AddonModScormAutoSyncEventData = CoreSyncResult & { scormId: number; attemptFinished: boolean; }; + +declare module '@singletons/events' { + + /** + * Augment CoreEventsData interface with events specific to this service. + * + * @see https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation + */ + export interface CoreEventsData { + [ADDON_MOD_SCORM_DATA_AUTO_SYNCED]: AddonModScormAutoSyncEventData; + } + +} diff --git a/src/addons/mod/scorm/services/scorm.ts b/src/addons/mod/scorm/services/scorm.ts index 56f2bd64fcf..585c5f1e8da 100644 --- a/src/addons/mod/scorm/services/scorm.ts +++ b/src/addons/mod/scorm/services/scorm.ts @@ -12,10 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { DownloadStatus } from '@/core/constants'; +import { CoreCacheUpdateFrequency, DownloadStatus } from '@/core/constants'; import { Injectable } from '@angular/core'; import { CoreError } from '@classes/errors/error'; -import { CoreSite } from '@classes/sites/site'; import { CoreCourseCommonModWSOptions } from '@features/course/services/course'; import { CoreCourseLogHelper } from '@features/course/services/log-helper'; import { CoreFilepool } from '@services/filepool'; @@ -24,13 +23,12 @@ import { CoreSync } from '@services/sync'; import { CoreText } from '@singletons/text'; import { CoreTimeUtils } from '@services/utils/time'; import { CoreUrl } from '@singletons/url'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreObject } from '@singletons/object'; import { CoreWS, CoreWSExternalFile, CoreWSExternalWarning, CoreWSFile, CoreWSPreSets } from '@services/ws'; import { makeSingleton, Translate } from '@singletons'; import { CoreEvents } from '@singletons/events'; import { CorePath } from '@singletons/path'; import { AddonModScormOffline } from './scorm-offline'; -import { AddonModScormAutoSyncEventData } from './scorm-sync'; import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site'; import { ADDON_MOD_SCORM_COMPONENT, @@ -43,8 +41,8 @@ import { ADDON_MOD_SCORM_LAUNCH_NEXT_SCO_EVENT, ADDON_MOD_SCORM_LAUNCH_PREV_SCO_EVENT, ADDON_MOD_SCORM_UPDATE_TOC_EVENT, - ADDON_MOD_SCORM_DATA_AUTO_SYNCED, } from '../constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; // Private constants. const VALID_STATUSES = ['notattempted', 'passed', 'completed', 'failed', 'incomplete', 'browsed', 'suspend']; @@ -555,7 +553,7 @@ export class AddonModScormProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getAttemptCountCacheKey(scormId, userId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + updateFrequency: CoreCacheUpdateFrequency.SOMETIMES, component: ADDON_MOD_SCORM_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. @@ -810,12 +808,12 @@ export class AddonModScormProvider { response.data.forEach((sco) => { data[sco.scoid] = { scoid: sco.scoid, - defaultdata: > CoreUtils.objectToKeyValueMap( + defaultdata: > CoreObject.toKeyValueMap( sco.defaultdata, 'element', 'value', ), - userdata: > CoreUtils.objectToKeyValueMap(sco.userdata, 'element', 'value'), + userdata: > CoreObject.toKeyValueMap(sco.userdata, 'element', 'value'), }; }); @@ -851,7 +849,7 @@ export class AddonModScormProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getScosCacheKey(scormId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + updateFrequency: CoreCacheUpdateFrequency.SOMETIMES, component: ADDON_MOD_SCORM_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. @@ -1093,7 +1091,7 @@ export class AddonModScormProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getScormDataCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, component: ADDON_MOD_SCORM_COMPONENT, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; @@ -1116,7 +1114,7 @@ export class AddonModScormProvider { } if (response.options) { - const scormOptions = CoreUtils.objectToKeyValueMap(response.options, 'name', 'value'); + const scormOptions = CoreObject.toKeyValueMap(response.options, 'name', 'value'); if (scormOptions.scormstandard) { currentScorm.scormStandard = Number(scormOptions.scormstandard); @@ -1638,7 +1636,7 @@ export class AddonModScormProvider { if (isOutdated === undefined) { // Calculate if it's outdated. - const data = await CoreUtils.ignoreErrors(CoreFilepool.getPackageData(siteId, component, scorm.coursemodule)); + const data = await CorePromiseUtils.ignoreErrors(CoreFilepool.getPackageData(siteId, component, scorm.coursemodule)); if (!data) { // Package not found, not downloaded. @@ -1653,7 +1651,7 @@ export class AddonModScormProvider { } else if (isOutdated) { // The package is outdated, but maybe the file hasn't changed. - const extra = await CoreUtils.ignoreErrors(CoreFilepool.getPackageExtra(siteId, component, scorm.coursemodule)); + const extra = await CorePromiseUtils.ignoreErrors(CoreFilepool.getPackageExtra(siteId, component, scorm.coursemodule)); if (!extra) { // Package not found, not downloaded. @@ -2081,7 +2079,6 @@ declare module '@singletons/events' { [ADDON_MOD_SCORM_UPDATE_TOC_EVENT]: AddonModScormCommonEventData; [ADDON_MOD_SCORM_GO_OFFLINE_EVENT]: AddonModScormCommonEventData; [ADDON_MOD_SCORM_DATA_SENT_EVENT]: AddonModScormCommonEventData; - [ADDON_MOD_SCORM_DATA_AUTO_SYNCED]: AddonModScormAutoSyncEventData; } } diff --git a/src/addons/mod/survey/components/index/index.ts b/src/addons/mod/survey/components/index/index.ts index c87fba5f6ed..ab9bf61ed25 100644 --- a/src/addons/mod/survey/components/index/index.ts +++ b/src/addons/mod/survey/components/index/index.ts @@ -36,7 +36,7 @@ import { AddonModSurveySync, AddonModSurveySyncResult, } from '../../services/survey-sync'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { ADDON_MOD_SURVEY_AUTO_SYNCED, ADDON_MOD_SURVEY_COMPONENT } from '../../constants'; import { CoreLoadings } from '@services/loadings'; @@ -46,7 +46,7 @@ import { CoreLoadings } from '@services/loadings'; @Component({ selector: 'addon-mod-survey-index', templateUrl: 'addon-mod-survey-index.html', - styleUrls: ['index.scss'], + styleUrl: 'index.scss', }) export class AddonModSurveyIndexComponent extends CoreCourseModuleMainActivityComponent implements OnInit { @@ -169,7 +169,7 @@ export class AddonModSurveyIndexComponent extends CoreCourseModuleMainActivityCo return; // Shouldn't happen. } - await CoreUtils.ignoreErrors(AddonModSurvey.logView(this.survey.id)); + await CorePromiseUtils.ignoreErrors(AddonModSurvey.logView(this.survey.id)); this.analyticsLogEvent('mod_survey_view_survey'); } diff --git a/src/addons/mod/survey/services/handlers/prefetch-lazy.ts b/src/addons/mod/survey/services/handlers/prefetch-lazy.ts index 32b8120c98b..cd7ea7a6ef6 100644 --- a/src/addons/mod/survey/services/handlers/prefetch-lazy.ts +++ b/src/addons/mod/survey/services/handlers/prefetch-lazy.ts @@ -16,7 +16,7 @@ import { Injectable } from '@angular/core'; import { CoreCourseAnyModuleData } from '@features/course/services/course'; import { CoreFilepool } from '@services/filepool'; import { CoreSitesReadingStrategy } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreWSFile } from '@services/ws'; import { makeSingleton } from '@singletons'; import { AddonModSurvey } from '../survey'; @@ -34,7 +34,7 @@ export class AddonModSurveyPrefetchHandlerLazyService extends AddonModSurveyPref * @inheritdoc */ async getIntroFiles(module: CoreCourseAnyModuleData, courseId: number): Promise { - const survey = await CoreUtils.ignoreErrors(AddonModSurvey.getSurvey(courseId, module.id)); + const survey = await CorePromiseUtils.ignoreErrors(AddonModSurvey.getSurvey(courseId, module.id)); return this.getIntroFilesFromInstance(module, survey); } diff --git a/src/addons/mod/survey/services/survey-sync.ts b/src/addons/mod/survey/services/survey-sync.ts index 54a793fe649..d747a55aa44 100644 --- a/src/addons/mod/survey/services/survey-sync.ts +++ b/src/addons/mod/survey/services/survey-sync.ts @@ -20,13 +20,14 @@ import { CoreCourse } from '@features/course/services/course'; import { CoreCourseLogHelper } from '@features/course/services/log-helper'; import { CoreNetwork } from '@services/network'; import { CoreSites } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreWSError } from '@classes/errors/wserror'; import { makeSingleton } from '@singletons'; import { CoreEvents } from '@singletons/events'; import { getPrefetchHandlerInstance } from './handlers/prefetch'; import { AddonModSurvey } from './survey'; import { AddonModSurveyAnswersDBRecordFormatted, AddonModSurveyOffline } from './survey-offline'; import { ADDON_MOD_SURVEY_AUTO_SYNCED, ADDON_MOD_SURVEY_COMPONENT } from '../constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Service to sync surveys. @@ -154,7 +155,7 @@ export class AddonModSurveySyncProvider extends CoreCourseActivitySyncBaseProvid }; // Sync offline logs. - CoreUtils.ignoreErrors(CoreCourseLogHelper.syncActivity(ADDON_MOD_SURVEY_COMPONENT, surveyId, siteId)); + CorePromiseUtils.ignoreErrors(CoreCourseLogHelper.syncActivity(ADDON_MOD_SURVEY_COMPONENT, surveyId, siteId)); let answersNumber = 0; let data: AddonModSurveyAnswersDBRecordFormatted | undefined; @@ -184,7 +185,7 @@ export class AddonModSurveySyncProvider extends CoreCourseActivitySyncBaseProvid // Answers sent, delete them. await AddonModSurveyOffline.deleteSurveyAnswers(surveyId, siteId, userId); } catch (error) { - if (!CoreUtils.isWebServiceError(error)) { + if (!CoreWSError.isWebServiceError(error)) { // Local error, reject. throw error; } @@ -204,7 +205,7 @@ export class AddonModSurveySyncProvider extends CoreCourseActivitySyncBaseProvid // Data has been sent to server, update survey data. const module = await CoreCourse.getModuleBasicInfoByInstance(surveyId, 'survey', { siteId }); - CoreUtils.ignoreErrors( + CorePromiseUtils.ignoreErrors( this.prefetchAfterUpdate(getPrefetchHandlerInstance(), module, result.courseId, undefined, siteId), ); } @@ -212,7 +213,7 @@ export class AddonModSurveySyncProvider extends CoreCourseActivitySyncBaseProvid const syncId = this.getSyncId(surveyId, userId); // Sync finished, set sync time. - CoreUtils.ignoreErrors(this.setSyncTime(syncId, siteId)); + CorePromiseUtils.ignoreErrors(this.setSyncTime(syncId, siteId)); return result; } @@ -241,7 +242,7 @@ export type AddonModSurveySyncResult = CoreSyncResult & { }; /** - * Data passed to AUTO_SYNCED event. + * Data passed to ADDON_MOD_SURVEY_AUTO_SYNCED event. */ export type AddonModSurveyAutoSyncData = { surveyId: number; diff --git a/src/addons/mod/survey/services/survey.ts b/src/addons/mod/survey/services/survey.ts index ca76d8e342a..66e1344715c 100644 --- a/src/addons/mod/survey/services/survey.ts +++ b/src/addons/mod/survey/services/survey.ts @@ -14,18 +14,19 @@ import { Injectable } from '@angular/core'; import { CoreError } from '@classes/errors/error'; -import { CoreSite } from '@classes/sites/site'; import { CoreCourseCommonModWSOptions } from '@features/course/services/course'; import { CoreCourseLogHelper } from '@features/course/services/log-helper'; import { CoreNetwork } from '@services/network'; import { CoreFilepool } from '@services/filepool'; import { CoreSites, CoreSitesCommonWSOptions } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreWSError } from '@classes/errors/wserror'; import { CoreStatusWithWarningsWSResponse, CoreWSExternalFile, CoreWSExternalWarning } from '@services/ws'; import { makeSingleton, Translate } from '@singletons'; import { AddonModSurveyOffline } from './survey-offline'; import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site'; import { ADDON_MOD_SURVEY_COMPONENT } from '../constants'; +import { CoreCacheUpdateFrequency } from '@/core/constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Service that provides some features for surveys. @@ -51,7 +52,7 @@ export class AddonModSurveyProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getQuestionsCacheKey(surveyId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, component: ADDON_MOD_SURVEY_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. @@ -108,7 +109,7 @@ export class AddonModSurveyProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getSurveyCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, component: ADDON_MOD_SURVEY_COMPONENT, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; @@ -175,7 +176,7 @@ export class AddonModSurveyProvider { promises.push(CoreFilepool.invalidateFilesByComponent(siteId, ADDON_MOD_SURVEY_COMPONENT, moduleId)); - await CoreUtils.allPromises(promises); + await CorePromiseUtils.allPromises(promises); } /** @@ -267,7 +268,7 @@ export class AddonModSurveyProvider { return true; } catch (error) { - if (CoreUtils.isWebServiceError(error)) { + if (CoreWSError.isWebServiceError(error)) { // It's a WebService error, the user cannot send the message so don't store it. throw error; } diff --git a/src/addons/mod/url/components/index/index.ts b/src/addons/mod/url/components/index/index.ts index 9c2cc0463bd..c4a2ea271b3 100644 --- a/src/addons/mod/url/components/index/index.ts +++ b/src/addons/mod/url/components/index/index.ts @@ -31,7 +31,7 @@ import { CoreSites } from '@services/sites'; @Component({ selector: 'addon-mod-url-index', templateUrl: 'addon-mod-url-index.html', - styleUrls: ['index.scss'], + styleUrl: 'index.scss', }) export class AddonModUrlIndexComponent extends CoreCourseModuleMainResourceComponent implements OnInit { diff --git a/src/addons/mod/url/services/handlers/module.ts b/src/addons/mod/url/services/handlers/module.ts index 1b35ed38682..27512cf62ba 100644 --- a/src/addons/mod/url/services/handlers/module.ts +++ b/src/addons/mod/url/services/handlers/module.ts @@ -21,7 +21,7 @@ import { CoreCourseModuleData } from '@features/course/services/course-helper'; import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate'; import { CoreNavigationOptions } from '@services/navigator'; import { CoreLoadings } from '@services/loadings'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { makeSingleton } from '@singletons'; import { AddonModUrl } from '../url'; import { AddonModUrlHelper } from '../url-helper'; @@ -215,7 +215,7 @@ export class AddonModUrlModuleHandlerService extends CoreModuleHandlerBase imple return true; } else { // Not handled by the app, check the display type. - const url = await CoreUtils.ignoreErrors(AddonModUrl.getUrl(module.course, module.id)); + const url = await CorePromiseUtils.ignoreErrors(AddonModUrl.getUrl(module.course, module.id)); const displayType = AddonModUrl.getFinalDisplayType(url); return displayType === CoreConstants.RESOURCELIB_DISPLAY_OPEN || diff --git a/src/addons/mod/url/services/url.ts b/src/addons/mod/url/services/url.ts index cec979f772a..6dc4f43905b 100644 --- a/src/addons/mod/url/services/url.ts +++ b/src/addons/mod/url/services/url.ts @@ -14,13 +14,12 @@ import { Injectable } from '@angular/core'; import { CoreSites, CoreSitesCommonWSOptions } from '@services/sites'; -import { CoreSite } from '@classes/sites/site'; import { CoreWSExternalWarning, CoreWSExternalFile } from '@services/ws'; import { makeSingleton, Translate } from '@singletons'; -import { CoreConstants } from '@/core/constants'; +import { CoreCacheUpdateFrequency, CoreConstants } from '@/core/constants'; import { CoreMimetypeUtils } from '@services/utils/mimetype'; import { CoreCourse } from '@features/course/services/course'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreCourseLogHelper } from '@features/course/services/log-helper'; import { CoreError } from '@classes/errors/error'; import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site'; @@ -119,7 +118,7 @@ export class AddonModUrlProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getUrlCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, component: ADDON_MOD_URL_COMPONENT, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), }; @@ -190,7 +189,7 @@ export class AddonModUrlProvider { promises.push(this.invalidateUrlData(courseId, siteId)); promises.push(CoreCourse.invalidateModule(moduleId, siteId, 'url')); - return CoreUtils.allPromises(promises); + return CorePromiseUtils.allPromises(promises); } /** diff --git a/src/addons/mod/wiki/components/index/index.ts b/src/addons/mod/wiki/components/index/index.ts index 3ec06261491..77bef4766ed 100644 --- a/src/addons/mod/wiki/components/index/index.ts +++ b/src/addons/mod/wiki/components/index/index.ts @@ -26,7 +26,7 @@ import { CoreGroup, CoreGroups } from '@services/groups'; import { CoreNavigator } from '@services/navigator'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { Translate, NgZone } from '@singletons'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CorePath } from '@singletons/path'; @@ -67,7 +67,7 @@ import { CorePopovers } from '@services/popovers'; @Component({ selector: 'addon-mod-wiki-index', templateUrl: 'addon-mod-wiki-index.html', - styleUrls: ['index.scss'], + styleUrl: 'index.scss', }) export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComponent implements OnInit, OnDestroy { @@ -228,7 +228,7 @@ export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComp if (sync) { // Try to synchronize the wiki. - await CoreUtils.ignoreErrors(this.syncActivity(showErrors)); + await CorePromiseUtils.ignoreErrors(this.syncActivity(showErrors)); } if (this.pageWarning) { @@ -496,7 +496,7 @@ export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComp return; // Shouldn't happen. } - await CoreUtils.ignoreErrors(AddonModWiki.logPageView(pageId, this.wiki.id)); + await CorePromiseUtils.ignoreErrors(AddonModWiki.logPageView(pageId, this.wiki.id)); this.analyticsLogEvent('mod_wiki_view_page', { name: this.currentPageObj?.title, diff --git a/src/addons/mod/wiki/pages/edit/edit.ts b/src/addons/mod/wiki/pages/edit/edit.ts index c9d11962f18..daf0e2d40d7 100644 --- a/src/addons/mod/wiki/pages/edit/edit.ts +++ b/src/addons/mod/wiki/pages/edit/edit.ts @@ -22,7 +22,7 @@ import { CoreSites, CoreSitesReadingStrategy } from '@services/sites'; import { CoreSync } from '@services/sync'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreText } from '@singletons/text'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreWSFile } from '@services/ws'; import { Translate } from '@singletons'; import { CoreEvents } from '@singletons/events'; @@ -224,7 +224,7 @@ export class AddonModWikiEditPage implements OnInit, OnDestroy, CanLeave { } // Check if there's already some offline data for this page. - const page = await CoreUtils.ignoreErrors( + const page = await CorePromiseUtils.ignoreErrors( AddonModWikiOffline.getNewPage(pageTitle, this.subwikiId, this.wikiId, this.userId, this.groupId), ); @@ -396,7 +396,7 @@ export class AddonModWikiEditPage implements OnInit, OnDestroy, CanLeave { if (!this.editOffline) { // Check if the user has an offline page with the same title. - const page = await CoreUtils.ignoreErrors( + const page = await CorePromiseUtils.ignoreErrors( AddonModWikiOffline.getNewPage(title, this.subwikiId, this.wikiId, this.userId, this.groupId), ); @@ -442,7 +442,7 @@ export class AddonModWikiEditPage implements OnInit, OnDestroy, CanLeave { this.userId = pageContents.userid; this.groupId = pageContents.groupid; - await CoreUtils.ignoreErrors(Promise.all(promises)); + await CorePromiseUtils.ignoreErrors(Promise.all(promises)); // Notify page created. CoreEvents.trigger(ADDON_MOD_WIKI_PAGE_CREATED_EVENT, { diff --git a/src/addons/mod/wiki/services/handlers/prefetch-lazy.ts b/src/addons/mod/wiki/services/handlers/prefetch-lazy.ts index d3629b3b90c..97c50fee6fc 100644 --- a/src/addons/mod/wiki/services/handlers/prefetch-lazy.ts +++ b/src/addons/mod/wiki/services/handlers/prefetch-lazy.ts @@ -18,7 +18,7 @@ import { CoreFilepool } from '@services/filepool'; import { CoreGroups } from '@services/groups'; import { CoreFileSizeSum, CorePluginFileDelegate } from '@services/plugin-file-delegate'; import { CoreSites, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreWSFile } from '@services/ws'; import { makeSingleton } from '@singletons'; import { AddonModWiki, AddonModWikiSubwikiPage } from '../wiki'; @@ -128,7 +128,7 @@ export class AddonModWikiPrefetchHandlerLazyService extends AddonModWikiPrefetch // Get the download time of the package before starting the download (otherwise we'd always get current time). const siteId = CoreSites.getCurrentSiteId(); - const data = await CoreUtils.ignoreErrors(CoreFilepool.getPackageData(siteId, this.component, module.id)); + const data = await CorePromiseUtils.ignoreErrors(CoreFilepool.getPackageData(siteId, this.component, module.id)); const downloadTime = data?.downloadTime || 0; diff --git a/src/addons/mod/wiki/services/handlers/tag-area.ts b/src/addons/mod/wiki/services/handlers/tag-area.ts index 878c3de1903..d4646fdd689 100644 --- a/src/addons/mod/wiki/services/handlers/tag-area.ts +++ b/src/addons/mod/wiki/services/handlers/tag-area.ts @@ -13,7 +13,6 @@ // limitations under the License. import { Injectable, Type } from '@angular/core'; -import { CoreTagFeedComponent } from '@features/tag/components/feed/feed'; import { CoreTagAreaHandler } from '@features/tag/services/tag-area-delegate'; import { CoreTagFeedElement, CoreTagHelper } from '@features/tag/services/tag-helper'; import { makeSingleton } from '@singletons'; @@ -44,7 +43,9 @@ export class AddonModWikiTagAreaHandlerService implements CoreTagAreaHandler { /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { + const { CoreTagFeedComponent } = await import('@features/tag/components/feed/feed'); + return CoreTagFeedComponent; } diff --git a/src/addons/mod/wiki/services/wiki-sync.ts b/src/addons/mod/wiki/services/wiki-sync.ts index c79f614f9f3..31b43cd2cd2 100644 --- a/src/addons/mod/wiki/services/wiki-sync.ts +++ b/src/addons/mod/wiki/services/wiki-sync.ts @@ -20,13 +20,14 @@ import { CoreNetwork } from '@services/network'; import { CoreGroups } from '@services/groups'; import { CoreSites } from '@services/sites'; import { CoreSync, CoreSyncResult } from '@services/sync'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreWSError } from '@classes/errors/wserror'; import { makeSingleton, Translate } from '@singletons'; import { CoreEvents } from '@singletons/events'; import { AddonModWikiPageDBRecord } from './database/wiki'; import { AddonModWiki } from './wiki'; import { AddonModWikiOffline } from './wiki-offline'; -import { ADDON_MOD_WIKI_AUTO_SYNCED, ADDON_MOD_WIKI_COMPONENT } from '../constants'; +import { ADDON_MOD_WIKI_AUTO_SYNCED, ADDON_MOD_WIKI_COMPONENT, ADDON_MOD_WIKI_MANUAL_SYNCED } from '../constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Service to sync wikis. @@ -210,14 +211,14 @@ export class AddonModWikiSyncProvider extends CoreSyncBaseProvider [], ); if (!pages || !pages.length) { // Nothing to sync. - await CoreUtils.ignoreErrors(this.setSyncTime(subwikiBlockId, siteId)); + await CorePromiseUtils.ignoreErrors(this.setSyncTime(subwikiBlockId, siteId)); return result; } @@ -247,7 +248,7 @@ export class AddonModWikiSyncProvider extends CoreSyncBaseProvider => { if (options.wikiId && options.subwikiId) { // We have wiki ID, check if there's already an online page with this title and subwiki. - const used = await CoreUtils.ignoreErrors(this.isTitleUsed(options.wikiId, options.subwikiId, title, { + const used = await CorePromiseUtils.ignoreErrors(this.isTitleUsed(options.wikiId, options.subwikiId, title, { cmId: options.cmId, readingStrategy: CoreSitesReadingStrategy.PREFER_CACHE, siteId: options.siteId, @@ -721,7 +719,7 @@ export class AddonModWikiProvider { // Try to create it in online. return await this.newPageOnline(title, content, options); } catch (error) { - if (CoreUtils.isWebServiceError(error)) { + if (CoreWSError.isWebServiceError(error)) { // The WebService has thrown an error, this means that the page cannot be added. throw error; } @@ -898,8 +896,6 @@ declare module '@singletons/events' { */ export interface CoreEventsData { [ADDON_MOD_WIKI_PAGE_CREATED_EVENT]: AddonModWikiPageCreatedData; - [ADDON_MOD_WIKI_AUTO_SYNCED]: AddonModWikiAutoSyncData; - [ADDON_MOD_WIKI_MANUAL_SYNCED]: AddonModWikiManualSyncData; } } diff --git a/src/addons/mod/workshop/assessment/accumulative/services/handler-lazy.ts b/src/addons/mod/workshop/assessment/accumulative/services/handler-lazy.ts index fc3f025b1c3..613fe44b1d9 100644 --- a/src/addons/mod/workshop/assessment/accumulative/services/handler-lazy.ts +++ b/src/addons/mod/workshop/assessment/accumulative/services/handler-lazy.ts @@ -24,7 +24,6 @@ import { CoreGradesHelper } from '@features/grades/services/grades-helper'; import { makeSingleton, Translate } from '@singletons'; import { CoreFormFields } from '@singletons/form'; import { AddonWorkshopAssessmentStrategyHandler } from '../../../services/assessment-strategy-delegate'; -import { AddonModWorkshopAssessmentStrategyAccumulativeComponent } from '../component/accumulative'; import { AddonModWorkshopAssessmentStrategyAccumulativeHandlerService } from './handler'; /** @@ -38,7 +37,9 @@ export class AddonModWorkshopAssessmentStrategyAccumulativeHandlerLazyService /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { + const { AddonModWorkshopAssessmentStrategyAccumulativeComponent } = await import('../component/accumulative'); + return AddonModWorkshopAssessmentStrategyAccumulativeComponent; } diff --git a/src/addons/mod/workshop/assessment/comments/services/handler-lazy.ts b/src/addons/mod/workshop/assessment/comments/services/handler-lazy.ts index 3a889063dda..ec9d2cb2141 100644 --- a/src/addons/mod/workshop/assessment/comments/services/handler-lazy.ts +++ b/src/addons/mod/workshop/assessment/comments/services/handler-lazy.ts @@ -23,7 +23,6 @@ import { import { Injectable, Type } from '@angular/core'; import { makeSingleton, Translate } from '@singletons'; import { CoreFormFields } from '@singletons/form'; -import { AddonModWorkshopAssessmentStrategyCommentsComponent } from '../component/comments'; import { AddonModWorkshopAssessmentStrategyCommentsHandlerService } from './handler'; /** @@ -37,7 +36,9 @@ export class AddonModWorkshopAssessmentStrategyCommentsHandlerLazyService /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { + const { AddonModWorkshopAssessmentStrategyCommentsComponent } = await import('../component/comments'); + return AddonModWorkshopAssessmentStrategyCommentsComponent; } diff --git a/src/addons/mod/workshop/assessment/numerrors/services/handler-lazy.ts b/src/addons/mod/workshop/assessment/numerrors/services/handler-lazy.ts index 6d925c167a2..6414affec48 100644 --- a/src/addons/mod/workshop/assessment/numerrors/services/handler-lazy.ts +++ b/src/addons/mod/workshop/assessment/numerrors/services/handler-lazy.ts @@ -23,7 +23,6 @@ import { import { Injectable, Type } from '@angular/core'; import { Translate, makeSingleton } from '@singletons'; import { CoreFormFields } from '@singletons/form'; -import { AddonModWorkshopAssessmentStrategyNumErrorsComponent } from '../component/numerrors'; import { AddonModWorkshopAssessmentStrategyNumErrorsHandlerService } from './handler'; /** @@ -37,7 +36,9 @@ export class AddonModWorkshopAssessmentStrategyNumErrorsHandlerLazyService /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { + const { AddonModWorkshopAssessmentStrategyNumErrorsComponent } = await import('../component/numerrors'); + return AddonModWorkshopAssessmentStrategyNumErrorsComponent; } diff --git a/src/addons/mod/workshop/assessment/rubric/services/handler-lazy.ts b/src/addons/mod/workshop/assessment/rubric/services/handler-lazy.ts index 0067006ccde..5a11535246d 100644 --- a/src/addons/mod/workshop/assessment/rubric/services/handler-lazy.ts +++ b/src/addons/mod/workshop/assessment/rubric/services/handler-lazy.ts @@ -23,7 +23,6 @@ import { import { Injectable, Type } from '@angular/core'; import { Translate, makeSingleton } from '@singletons'; import { CoreFormFields } from '@singletons/form'; -import { AddonModWorkshopAssessmentStrategyRubricComponent } from '../component/rubric'; import { AddonModWorkshopAssessmentStrategyRubricHandlerService } from './handler'; /** @@ -37,7 +36,9 @@ export class AddonModWorkshopAssessmentStrategyRubricHandlerLazyService /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { + const { AddonModWorkshopAssessmentStrategyRubricComponent } = await import('../component/rubric'); + return AddonModWorkshopAssessmentStrategyRubricComponent; } diff --git a/src/addons/mod/workshop/components/assessment-strategy/assessment-strategy.ts b/src/addons/mod/workshop/components/assessment-strategy/assessment-strategy.ts index 2ae834566d8..d237d04827b 100644 --- a/src/addons/mod/workshop/components/assessment-strategy/assessment-strategy.ts +++ b/src/addons/mod/workshop/components/assessment-strategy/assessment-strategy.ts @@ -22,7 +22,7 @@ import { CoreFileSession } from '@services/file-session'; import { CoreSites } from '@services/sites'; import { CoreSync } from '@services/sync'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { Translate } from '@singletons'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreFormFields, CoreForms } from '@singletons/form'; @@ -43,6 +43,9 @@ import { } from '@addons/mod/workshop/constants'; import { toBoolean } from '@/core/transforms/boolean'; import { CoreLoadings } from '@services/loadings'; +import { CorePromiseUtils } from '@singletons/promise-utils'; +import { CoreWSError } from '@classes/errors/wserror'; +import { CoreObject } from '@singletons/object'; /** * Component that displays workshop assessment strategy form. @@ -186,7 +189,7 @@ export class AddonModWorkshopAssessmentStrategyComponent implements OnInit, OnDe // Override assessment plugins values. this.data.assessment.form.current = AddonModWorkshop.parseFields( - CoreUtils.objectToArrayOfObjects(offlineData, 'name', 'value'), + CoreObject.toArrayOfObjects(offlineData, 'name', 'value'), ); // Override offline files. @@ -317,7 +320,7 @@ export class AddonModWorkshopAssessmentStrategyComponent implements OnInit, OnDe saveOffline, ); } catch (error) { - if (CoreUtils.isWebServiceError(error)) { + if (CoreWSError.isWebServiceError(error)) { throw error; } @@ -384,7 +387,7 @@ export class AddonModWorkshopAssessmentStrategyComponent implements OnInit, OnDe promises.push(AddonModWorkshop.invalidateAssessmentData(this.workshop.id, this.assessmentId)); } - await CoreUtils.ignoreErrors(Promise.all(promises)); + await CorePromiseUtils.ignoreErrors(Promise.all(promises)); CoreEvents.trigger(ADDON_MOD_WORKSHOP_ASSESSMENT_SAVED, { workshopId: this.workshop.id, diff --git a/src/addons/mod/workshop/components/index/index.ts b/src/addons/mod/workshop/components/index/index.ts index ee433777842..20e3d6346d9 100644 --- a/src/addons/mod/workshop/components/index/index.ts +++ b/src/addons/mod/workshop/components/index/index.ts @@ -22,7 +22,7 @@ import { CoreGroupInfo, CoreGroups } from '@services/groups'; import { CoreNavigator } from '@services/navigator'; import { CorePlatform } from '@services/platform'; import { CoreModals } from '@services/modals'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreObject } from '@singletons/object'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { Subscription } from 'rxjs'; import { @@ -57,6 +57,8 @@ import { ADDON_MOD_WORKSHOP_SUBMISSION_CHANGED, AddonModWorkshopPhase, } from '@addons/mod/workshop/constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; +import { CoreOpener } from '@singletons/opener'; /** * Component that displays a workshop index page. @@ -259,7 +261,7 @@ export class AddonModWorkshopIndexComponent extends CoreCourseModuleMainActivity return; // Shouldn't happen. } - await CoreUtils.ignoreErrors(AddonModWorkshop.logView(this.workshop.id)); + await CorePromiseUtils.ignoreErrors(AddonModWorkshop.logView(this.workshop.id)); this.analyticsLogEvent('mod_workshop_view_workshop'); } @@ -362,7 +364,7 @@ export class AddonModWorkshopIndexComponent extends CoreCourseModuleMainActivity if (task.code == 'submit') { this.gotoSubmit(); } else if (task.link) { - CoreUtils.openInBrowser(task.link); + CoreOpener.openInBrowser(task.link); } } @@ -403,7 +405,7 @@ export class AddonModWorkshopIndexComponent extends CoreCourseModuleMainActivity const modalData = await CoreModals.openModal({ component: AddonModWorkshopPhaseInfoModalComponent, componentProps: { - phases: CoreUtils.objectToArray(this.phases), + phases: CoreObject.toArray(this.phases), workshopPhase: this.workshop.phase, externalUrl: this.module.url, showSubmit: this.showSubmit, diff --git a/src/addons/mod/workshop/components/phase-modal/phase-modal.ts b/src/addons/mod/workshop/components/phase-modal/phase-modal.ts index a6a138526ec..1091cb673d2 100644 --- a/src/addons/mod/workshop/components/phase-modal/phase-modal.ts +++ b/src/addons/mod/workshop/components/phase-modal/phase-modal.ts @@ -13,7 +13,7 @@ // limitations under the License. import { Component, Input, OnInit } from '@angular/core'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreOpener } from '@singletons/opener'; import { ModalController } from '@singletons'; import { AddonModWorkshopPhaseData, AddonModWorkshopPhaseTaskData } from '../../services/workshop'; import { AddonModWorkshopPhase } from '../../constants'; @@ -69,7 +69,7 @@ export class AddonModWorkshopPhaseInfoModalComponent implements OnInit { // This will close the modal and go to the submit. ModalController.dismiss(true); } else if (task.link) { - CoreUtils.openInBrowser(task.link); + CoreOpener.openInBrowser(task.link); } } diff --git a/src/addons/mod/workshop/components/submission/submission.ts b/src/addons/mod/workshop/components/submission/submission.ts index 0b3481f0958..5e576bf6f89 100644 --- a/src/addons/mod/workshop/components/submission/submission.ts +++ b/src/addons/mod/workshop/components/submission/submission.ts @@ -38,7 +38,7 @@ import { toBoolean } from '@/core/transforms/boolean'; @Component({ selector: 'addon-mod-workshop-submission', templateUrl: 'addon-mod-workshop-submission.html', - styleUrls: ['submission.scss'], + styleUrl: 'submission.scss', }) export class AddonModWorkshopSubmissionComponent implements OnInit { diff --git a/src/addons/mod/workshop/pages/edit-submission/edit-submission.ts b/src/addons/mod/workshop/pages/edit-submission/edit-submission.ts index 8e884b84d96..5908e9c2e0c 100644 --- a/src/addons/mod/workshop/pages/edit-submission/edit-submission.ts +++ b/src/addons/mod/workshop/pages/edit-submission/edit-submission.ts @@ -25,7 +25,7 @@ import { CoreSites } from '@services/sites'; import { CoreSync } from '@services/sync'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreText } from '@singletons/text'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreWSError } from '@classes/errors/wserror'; import { Translate } from '@singletons'; import { CoreEvents } from '@singletons/events'; import { CoreForms } from '@singletons/form'; @@ -376,7 +376,7 @@ export class AddonModWorkshopEditSubmissionPage implements OnInit, OnDestroy, Ca false, ); } catch (error) { - if (CoreUtils.isWebServiceError(error)) { + if (CoreWSError.isWebServiceError(error)) { throw error; } diff --git a/src/addons/mod/workshop/services/handlers/prefetch-lazy.ts b/src/addons/mod/workshop/services/handlers/prefetch-lazy.ts index 0e69b40aa94..a0b9de9f965 100644 --- a/src/addons/mod/workshop/services/handlers/prefetch-lazy.ts +++ b/src/addons/mod/workshop/services/handlers/prefetch-lazy.ts @@ -20,7 +20,7 @@ import { CoreUser } from '@features/user/services/user'; import { CoreFilepool } from '@services/filepool'; import { CoreGroup, CoreGroups } from '@services/groups'; import { CoreSites, CoreSitesReadingStrategy, CoreSitesCommonWSOptions } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreObject } from '@singletons/object'; import { CoreWSExternalFile, CoreWSFile } from '@services/ws'; import { makeSingleton } from '@singletons'; import { @@ -33,6 +33,7 @@ import { AddonModWorkshopHelper } from '../workshop-helper'; import { AddonModWorkshopSync } from '../workshop-sync'; import { AddonModWorkshopPrefetchHandlerService } from '@addons/mod/workshop/services/handlers/prefetch'; import { AddonModWorkshopPhase } from '../../constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Handler to prefetch workshops. @@ -247,7 +248,7 @@ export class AddonModWorkshopPrefetchHandlerLazyService extends AddonModWorkshop }); }); - return CoreUtils.objectToArray(uniqueGrades); + return CoreObject.toArray(uniqueGrades); } /** @@ -379,7 +380,7 @@ export class AddonModWorkshopPrefetchHandlerLazyService extends AddonModWorkshop promises.push(CoreCourse.getModuleBasicGradeInfo(module.id, siteId)); // Get course data, needed to determine upload max size if it's configured to be course limit. - promises.push(CoreUtils.ignoreErrors(CoreCourses.getCourseByField('id', courseId, siteId))); + promises.push(CorePromiseUtils.ignoreErrors(CoreCourses.getCourseByField('id', courseId, siteId))); await Promise.all(promises); diff --git a/src/addons/mod/workshop/services/workshop-helper.ts b/src/addons/mod/workshop/services/workshop-helper.ts index 54091eeedc1..66ee315b735 100644 --- a/src/addons/mod/workshop/services/workshop-helper.ts +++ b/src/addons/mod/workshop/services/workshop-helper.ts @@ -20,7 +20,7 @@ import { CoreFile } from '@services/file'; import { CoreFileEntry } from '@services/file-helper'; import { CoreSites } from '@services/sites'; import { CoreText, CoreTextFormat } from '@singletons/text'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { makeSingleton, Translate } from '@singletons'; import { CoreFormFields } from '@singletons/form'; import { AddonModWorkshopAssessmentStrategyFieldErrors } from '../components/assessment-strategy/assessment-strategy'; @@ -44,6 +44,7 @@ import { AddonModWorkshopOverallFeedbackMode, AddonModWorkshopPhase, } from '@addons/mod/workshop/constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Helper to gather some common functions for workshop. @@ -240,8 +241,8 @@ export class AddonModWorkshopHelperProvider { async deleteSubmissionStoredFiles(workshopId: number, siteId?: string): Promise { const folderPath = await AddonModWorkshopOffline.getSubmissionFolder(workshopId, siteId); - // Ignore any errors, CoreFileProvider.removeDir fails if folder doesn't exists. - await CoreUtils.ignoreErrors(CoreFile.removeDir(folderPath)); + // Ignore any errors, CoreFile.removeDir fails if folder doesn't exists. + await CorePromiseUtils.ignoreErrors(CoreFile.removeDir(folderPath)); } /** @@ -312,7 +313,7 @@ export class AddonModWorkshopHelperProvider { const folderPath = await AddonModWorkshopOffline.getSubmissionFolder(workshopId, siteId); // Ignore not found files. - return CoreUtils.ignoreErrors(CoreFileUploader.getStoredFiles(folderPath), []); + return CorePromiseUtils.ignoreErrors(CoreFileUploader.getStoredFiles(folderPath), []); } /** @@ -344,8 +345,8 @@ export class AddonModWorkshopHelperProvider { async deleteAssessmentStoredFiles(workshopId: number, assessmentId: number, siteId?: string): Promise { const folderPath = await AddonModWorkshopOffline.getAssessmentFolder(workshopId, assessmentId, siteId); - // Ignore any errors, CoreFileProvider.removeDir fails if folder doesn't exists. - await CoreUtils.ignoreErrors(CoreFile.removeDir(folderPath)); + // Ignore any errors, CoreFile.removeDir fails if folder doesn't exists. + await CorePromiseUtils.ignoreErrors(CoreFile.removeDir(folderPath)); } /** @@ -420,7 +421,7 @@ export class AddonModWorkshopHelperProvider { const folderPath = await AddonModWorkshopOffline.getAssessmentFolder(workshopId, assessmentId, siteId); // Ignore not found files. - return CoreUtils.ignoreErrors(CoreFileUploader.getStoredFiles(folderPath), []); + return CorePromiseUtils.ignoreErrors(CoreFileUploader.getStoredFiles(folderPath), []); } /** diff --git a/src/addons/mod/workshop/services/workshop-sync.ts b/src/addons/mod/workshop/services/workshop-sync.ts index 645a3fa38c8..ac6ddca217a 100644 --- a/src/addons/mod/workshop/services/workshop-sync.ts +++ b/src/addons/mod/workshop/services/workshop-sync.ts @@ -22,7 +22,7 @@ import { CoreFileEntry } from '@services/file-helper'; import { CoreSites } from '@services/sites'; import { CoreSync, CoreSyncResult } from '@services/sync'; import { CoreErrorHelper } from '@services/error-helper'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreWSError } from '@classes/errors/wserror'; import { Translate, makeSingleton } from '@singletons'; import { CoreEvents } from '@singletons/events'; import { AddonModWorkshop, @@ -41,6 +41,7 @@ import { AddonModWorkshopAction, AddonModWorkshopSubmissionType, } from '@addons/mod/workshop/constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Service to sync workshops. @@ -163,18 +164,18 @@ export class AddonModWorkshopSyncProvider extends CoreSyncBaseProvider('mod_workshop_get_user_plan', params, preSets); - return CoreUtils.arrayToObject(response.userplan.phases, 'code'); + return CoreArray.toObject(response.userplan.phases, 'code'); } /** @@ -407,7 +406,7 @@ export class AddonModWorkshopProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getSubmissionsDataCacheKey(workshopId, userId, groupId), - updateFrequency: CoreSite.FREQUENCY_OFTEN, + updateFrequency: CoreCacheUpdateFrequency.OFTEN, component: ADDON_MOD_WORKSHOP_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. @@ -537,7 +536,7 @@ export class AddonModWorkshopProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getGradesReportDataCacheKey(workshopId, options.groupId), - updateFrequency: CoreSite.FREQUENCY_OFTEN, + updateFrequency: CoreCacheUpdateFrequency.OFTEN, component: ADDON_MOD_WORKSHOP_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. @@ -711,7 +710,7 @@ export class AddonModWorkshopProvider { try { return await this.addSubmissionOnline(workshopId, title, content, attachmentsId as number, siteId); } catch (error) { - if (allowOffline && !CoreUtils.isWebServiceError(error)) { + if (allowOffline && !CoreWSError.isWebServiceError(error)) { // Couldn't connect to server, store in offline. return storeOffline(); } @@ -811,7 +810,7 @@ export class AddonModWorkshopProvider { try { return await this.updateSubmissionOnline(submissionId, title, content, attachmentsId as number, siteId); } catch (error) { - if (allowOffline && !CoreUtils.isWebServiceError(error)) { + if (allowOffline && !CoreWSError.isWebServiceError(error)) { // Couldn't connect to server, store in offline. return storeOffline(); } @@ -890,7 +889,7 @@ export class AddonModWorkshopProvider { try { return await this.deleteSubmissionOnline(submissionId, siteId); } catch (error) { - if (!CoreUtils.isWebServiceError(error)) { + if (!CoreWSError.isWebServiceError(error)) { // Couldn't connect to server, store in offline. return storeOffline(); } @@ -1035,7 +1034,7 @@ export class AddonModWorkshopProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getAssessmentFormDataCacheKey(workshopId, assessmentId, mode), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, component: ADDON_MOD_WORKSHOP_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. @@ -1054,7 +1053,7 @@ export class AddonModWorkshopProvider { warnings: response.warnings, fields: this.parseFields(response.fields), current: this.parseFields(response.current), - options: CoreUtils.objectToKeyValueMap(response.options, 'name', 'value'), + options: CoreObject.toKeyValueMap(response.options, 'name', 'value'), }; } @@ -1162,7 +1161,7 @@ export class AddonModWorkshopProvider { return true; } catch (error) { - if (allowOffline && !CoreUtils.isWebServiceError(error)) { + if (allowOffline && !CoreWSError.isWebServiceError(error)) { // Couldn't connect to server, store in offline. return storeOffline(); } @@ -1185,7 +1184,7 @@ export class AddonModWorkshopProvider { const params: AddonModWorkshopUpdateAssessmentWSParams = { assessmentid: assessmentId, - data: CoreUtils.objectToArrayOfObjects(inputData, 'name', 'value'), + data: CoreObject.toArrayOfObjects(inputData, 'name', 'value'), }; const response = await site.write('mod_workshop_update_assessment', params); @@ -1239,7 +1238,7 @@ export class AddonModWorkshopProvider { try { return await this.evaluateSubmissionOnline(submissionId, feedbackText, published, gradeOver, siteId); } catch (error) { - if (CoreUtils.isWebServiceError(error)) { + if (CoreWSError.isWebServiceError(error)) { // The WebService has thrown an error or offline not supported, reject. throw error; } @@ -1329,7 +1328,7 @@ export class AddonModWorkshopProvider { try { return await this.evaluateAssessmentOnline(assessmentId, feedbackText, weight, gradingGradeOver, siteId); } catch (error) { - if (!CoreUtils.isWebServiceError(error)) { + if (!CoreWSError.isWebServiceError(error)) { // Couldn't connect to server, store in offline. return storeOffline(); } diff --git a/src/addons/notes/notes-lazy.module.ts b/src/addons/notes/notes-lazy.module.ts index 49997cb2e70..fa97c950bfc 100644 --- a/src/addons/notes/notes-lazy.module.ts +++ b/src/addons/notes/notes-lazy.module.ts @@ -37,4 +37,4 @@ const routes: Routes = [ AddonNotesListPage, ], }) -export class AddonNotesLazyModule {} +export default class AddonNotesLazyModule {} diff --git a/src/addons/notes/notes.module.ts b/src/addons/notes/notes.module.ts index 3791c309e51..655e4dc856e 100644 --- a/src/addons/notes/notes.module.ts +++ b/src/addons/notes/notes.module.ts @@ -45,7 +45,7 @@ export async function getNotesServices(): Promise[]> { const routes: Routes = [ { path: 'notes', - loadChildren: () => import('@addons/notes/notes-lazy.module').then(m => m.AddonNotesLazyModule), + loadChildren: () => import('@addons/notes/notes-lazy.module'), }, ]; diff --git a/src/addons/notes/pages/list/list.ts b/src/addons/notes/pages/list/list.ts index ec187befa59..31e46570f05 100644 --- a/src/addons/notes/pages/list/list.ts +++ b/src/addons/notes/pages/list/list.ts @@ -16,7 +16,7 @@ import { CoreConstants } from '@/core/constants'; import { AddonNotesAddModalReturn } from '@addons/notes/components/add/add-modal'; import { AddonNotes, AddonNotesNoteFormatted, AddonNotesPublishState } from '@addons/notes/services/notes'; import { AddonNotesOffline } from '@addons/notes/services/notes-offline'; -import { AddonNotesSync, AddonNotesSyncProvider } from '@addons/notes/services/notes-sync'; +import { AddonNotesSync } from '@addons/notes/services/notes-sync'; import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { CoreAnimations } from '@components/animations'; import { CoreUser, CoreUserProfile } from '@features/user/services/user'; @@ -27,12 +27,13 @@ import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreText } from '@singletons/text'; import { CoreUrl } from '@singletons/url'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { Translate } from '@singletons'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreTime } from '@singletons/time'; import { CoreToasts, ToastDuration } from '@services/toasts'; import { CoreModals } from '@services/modals'; +import { ADDON_NOTES_AUTO_SYNCED } from '@addons/notes/services/constants'; /** * Page that displays a list of notes. @@ -77,7 +78,7 @@ export class AddonNotesListPage implements OnInit, OnDestroy { } // Refresh data if notes are synchronized automatically. - this.syncObserver = CoreEvents.on(AddonNotesSyncProvider.AUTO_SYNCED, (data) => { + this.syncObserver = CoreEvents.on(ADDON_NOTES_AUTO_SYNCED, (data) => { if (data.courseId == this.courseId) { // Show the sync warnings. this.showSyncWarnings(data.warnings); @@ -312,7 +313,7 @@ export class AddonNotesListPage implements OnInit, OnDestroy { * Log view. */ protected async performLogView(): Promise { - await CoreUtils.ignoreErrors(AddonNotes.logView(this.courseId, this.userId)); + await CorePromiseUtils.ignoreErrors(AddonNotes.logView(this.courseId, this.userId)); CoreAnalytics.logEvent({ type: CoreAnalyticsEventType.VIEW_ITEM_LIST, diff --git a/src/addons/notes/services/constants.ts b/src/addons/notes/services/constants.ts new file mode 100644 index 00000000000..1878416be29 --- /dev/null +++ b/src/addons/notes/services/constants.ts @@ -0,0 +1,15 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +export const ADDON_NOTES_AUTO_SYNCED = 'addon_notes_autom_synced'; diff --git a/src/addons/notes/services/handlers/course-option.ts b/src/addons/notes/services/handlers/course-option.ts index acb9153e6a9..b987dbd5603 100644 --- a/src/addons/notes/services/handlers/course-option.ts +++ b/src/addons/notes/services/handlers/course-option.ts @@ -13,7 +13,7 @@ // limitations under the License. import { Injectable } from '@angular/core'; -import { CoreCourseAccessDataType } from '@features/course/services/course'; +import { CoreCourseAccessDataType } from '@features/course/constants'; import { CoreCourseAccess, CoreCourseOptionsHandler, @@ -47,7 +47,7 @@ export class AddonNotesCourseOptionHandlerService implements CoreCourseOptionsHa accessData: CoreCourseAccess, navOptions?: CoreCourseUserAdminOrNavOptionIndexed, ): Promise { - if (accessData && accessData.type === CoreCourseAccessDataType.ACCESS_GUEST) { + if (accessData?.type === CoreCourseAccessDataType.ACCESS_GUEST) { return false; // Not enabled for guest access. } diff --git a/src/addons/notes/services/notes-sync.ts b/src/addons/notes/services/notes-sync.ts index c60a6f431c4..c0e7e254715 100644 --- a/src/addons/notes/services/notes-sync.ts +++ b/src/addons/notes/services/notes-sync.ts @@ -18,7 +18,7 @@ import { CoreNetworkError } from '@classes/errors/network-error'; import { CoreCourses } from '@features/courses/services/courses'; import { CoreNetwork } from '@services/network'; import { CoreSites } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreWSError } from '@classes/errors/wserror'; import { Translate, makeSingleton } from '@singletons'; import { CoreEvents } from '@singletons/events'; import { AddonNotesDBRecord, AddonNotesDeletedDBRecord } from './database/notes'; @@ -27,6 +27,8 @@ import { AddonNotesOffline } from './notes-offline'; import { CoreArray } from '@singletons/array'; import { CoreAnyError } from '@classes/errors/error'; import { CoreErrorHelper } from '@services/error-helper'; +import { ADDON_NOTES_AUTO_SYNCED } from './constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Service to sync notes. @@ -34,8 +36,6 @@ import { CoreErrorHelper } from '@services/error-helper'; @Injectable( { providedIn: 'root' } ) export class AddonNotesSyncProvider extends CoreSyncBaseProvider { - static readonly AUTO_SYNCED = 'addon_notes_autom_synced'; - constructor() { super('AddonNotesSync'); } @@ -80,7 +80,7 @@ export class AddonNotesSyncProvider extends CoreSyncBaseProvider { - if (CoreUtils.isWebServiceError(error)) { + if (CoreWSError.isWebServiceError(error)) { // It's a WebService error, this means the user cannot send notes. errors.push(error); @@ -203,7 +203,7 @@ export class AddonNotesSyncProvider extends CoreSyncBaseProvider { - if (CoreUtils.isWebServiceError(error)) { + if (CoreWSError.isWebServiceError(error)) { // It's a WebService error, this means the user cannot send notes. errors.push(error); @@ -224,13 +224,13 @@ export class AddonNotesSyncProvider extends CoreSyncBaseProvider Translate.instant('addon.notes.warningnotenotsent', { @@ -251,7 +251,7 @@ export type AddonNotesSyncResult = { }; /** - * Data passed to AUTO_SYNCED event. + * Data passed to ADDON_NOTES_AUTO_SYNCED event. */ export type AddonNotesSyncAutoSyncData = { courseId: number; @@ -266,7 +266,7 @@ declare module '@singletons/events' { * @see https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation */ export interface CoreEventsData { - [AddonNotesSyncProvider.AUTO_SYNCED]: AddonNotesSyncAutoSyncData; + [ADDON_NOTES_AUTO_SYNCED]: AddonNotesSyncAutoSyncData; } } diff --git a/src/addons/notes/services/notes.ts b/src/addons/notes/services/notes.ts index e15e81d6414..802d4b9b199 100644 --- a/src/addons/notes/services/notes.ts +++ b/src/addons/notes/services/notes.ts @@ -14,15 +14,15 @@ import { Injectable } from '@angular/core'; import { CoreWSError } from '@classes/errors/wserror'; -import { CoreSite } from '@classes/sites/site'; import { CoreUser } from '@features/user/services/user'; import { CoreNetwork } from '@services/network'; import { CoreSites } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; import { CoreWSExternalWarning } from '@services/ws'; import { makeSingleton, Translate } from '@singletons'; import { AddonNotesOffline } from './notes-offline'; import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site'; +import { CoreCacheUpdateFrequency } from '@/core/constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; const ROOT_CACHE_KEY = 'mmaNotes:'; @@ -69,7 +69,7 @@ export class AddonNotesProvider { return true; } catch (error) { - if (CoreUtils.isWebServiceError(error)) { + if (CoreWSError.isWebServiceError(error)) { // It's a WebService error, the user cannot send the message so don't store it. throw error; } @@ -111,7 +111,7 @@ export class AddonNotesProvider { throw new CoreWSError({ message: response[0].errormessage }); } - await CoreUtils.ignoreErrors(this.invalidateNotes(courseId, undefined, siteId)); + await CorePromiseUtils.ignoreErrors(this.invalidateNotes(courseId, undefined, siteId)); } /** @@ -172,7 +172,7 @@ export class AddonNotesProvider { return true; } catch (error) { - if (CoreUtils.isWebServiceError(error)) { + if (CoreWSError.isWebServiceError(error)) { // It's a WebService error, the user cannot send the note so don't store it. throw error; } @@ -199,7 +199,7 @@ export class AddonNotesProvider { await site.write('core_notes_delete_notes', params); - CoreUtils.ignoreErrors(this.invalidateNotes(courseId, undefined, siteId)); + CorePromiseUtils.ignoreErrors(this.invalidateNotes(courseId, undefined, siteId)); } /** @@ -241,12 +241,12 @@ export class AddonNotesProvider { ], }; const preSets: CoreSiteWSPreSets = { - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, }; // Use .read to cache data and be able to check it in offline. This means that, if a user loses the capabilities // to add notes, he'll still see the option in the app. - return CoreUtils.promiseWorks(site.read('core_notes_create_notes', params, preSets)); + return CorePromiseUtils.promiseWorks(site.read('core_notes_create_notes', params, preSets)); } /** @@ -257,7 +257,7 @@ export class AddonNotesProvider { * @returns Promise resolved with true if enabled, resolved with false or rejected otherwise. */ isPluginViewNotesEnabledForCourse(courseId: number, siteId?: string): Promise { - return CoreUtils.promiseWorks(this.getNotes(courseId, undefined, false, true, siteId)); + return CorePromiseUtils.promiseWorks(this.getNotes(courseId, undefined, false, true, siteId)); } /** @@ -309,7 +309,7 @@ export class AddonNotesProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getNotesCacheKey(courseId, userId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + updateFrequency: CoreCacheUpdateFrequency.SOMETIMES, }; if (ignoreCache) { diff --git a/src/addons/notifications/notifications-lazy.module.ts b/src/addons/notifications/notifications-lazy.module.ts index fddc7ecf546..28ad32bdf61 100644 --- a/src/addons/notifications/notifications-lazy.module.ts +++ b/src/addons/notifications/notifications-lazy.module.ts @@ -78,4 +78,4 @@ function buildRoutes(injector: Injector): Routes { }, ], }) -export class AddonNotificationsLazyModule {} +export default class AddonNotificationsLazyModule {} diff --git a/src/addons/notifications/notifications-settings-lazy.module.ts b/src/addons/notifications/notifications-settings-lazy.module.ts index 83d2cd48fef..e87cc52556d 100644 --- a/src/addons/notifications/notifications-settings-lazy.module.ts +++ b/src/addons/notifications/notifications-settings-lazy.module.ts @@ -34,4 +34,4 @@ const routes: Routes = [ AddonNotificationsSettingsPage, ], }) -export class AddonNotificationsSettingsLazyModule {} +export default class AddonNotificationsSettingsLazyModule {} diff --git a/src/addons/notifications/notifications.module.ts b/src/addons/notifications/notifications.module.ts index 855191c7e11..202bc62f4cb 100644 --- a/src/addons/notifications/notifications.module.ts +++ b/src/addons/notifications/notifications.module.ts @@ -48,13 +48,13 @@ export async function getNotificationsServices(): Promise[]> { const routes: Routes = [ { path: AddonNotificationsMainMenuHandlerService.PAGE_NAME, - loadChildren: () => import('./notifications-lazy.module').then(m => m.AddonNotificationsLazyModule), + loadChildren: () => import('./notifications-lazy.module'), }, ]; const preferencesRoutes: Routes = [ { path: AddonNotificationsSettingsHandlerService.PAGE_NAME, - loadChildren: () => import('./notifications-settings-lazy.module').then(m => m.AddonNotificationsSettingsLazyModule), + loadChildren: () => import('./notifications-settings-lazy.module'), }, ]; diff --git a/src/addons/notifications/pages/list/list.ts b/src/addons/notifications/pages/list/list.ts index 79602218eb4..b78a98c65de 100644 --- a/src/addons/notifications/pages/list/list.ts +++ b/src/addons/notifications/pages/list/list.ts @@ -16,7 +16,7 @@ import { AfterViewInit, Component, OnDestroy, ViewChild } from '@angular/core'; import { Subscription } from 'rxjs'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { AddonNotifications, AddonNotificationsNotificationMessageFormatted, AddonNotificationsProvider, @@ -34,6 +34,7 @@ import { CoreLocalNotifications } from '@services/local-notifications'; import { CoreConfig } from '@services/config'; import { CoreConstants } from '@/core/constants'; import { CorePlatform } from '@services/platform'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Page that displays the list of notifications. @@ -192,7 +193,7 @@ export class AddonNotificationsListPage implements AfterViewInit, OnDestroy { async markAllNotificationsAsRead(): Promise { this.loadingMarkAllNotificationsAsRead = true; - await CoreUtils.ignoreErrors(AddonNotifications.markAllNotificationsAsRead()); + await CorePromiseUtils.ignoreErrors(AddonNotifications.markAllNotificationsAsRead()); CoreEvents.trigger(AddonNotificationsProvider.READ_CHANGED_EVENT, { time: CoreTimeUtils.timestamp(), @@ -225,8 +226,8 @@ export class AddonNotificationsListPage implements AfterViewInit, OnDestroy { * @param refresher Refresher. */ async refreshNotifications(refresher?: HTMLIonRefresherElement): Promise { - await CoreUtils.ignoreErrors(AddonNotifications.invalidateNotificationsList()); - await CoreUtils.ignoreErrors(this.fetchNotifications(true)); + await CorePromiseUtils.ignoreErrors(AddonNotifications.invalidateNotificationsList()); + await CorePromiseUtils.ignoreErrors(this.fetchNotifications(true)); refresher?.complete(); } diff --git a/src/addons/notifications/pages/settings/settings.ts b/src/addons/notifications/pages/settings/settings.ts index c6702431d1b..f8729db9ab6 100644 --- a/src/addons/notifications/pages/settings/settings.ts +++ b/src/addons/notifications/pages/settings/settings.ts @@ -18,7 +18,7 @@ import { CoreConfig } from '@services/config'; import { CoreLocalNotifications } from '@services/local-notifications'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreUser } from '@features/user/services/user'; import { AddonMessageOutputDelegate, AddonMessageOutputHandlerData } from '@addons/messageoutput/services/messageoutput-delegate'; import { CoreConstants } from '@/core/constants'; @@ -48,7 +48,7 @@ import { CoreLoadings } from '@services/loadings'; @Component({ selector: 'page-addon-notifications-settings', templateUrl: 'settings.html', - styleUrls: ['settings.scss'], + styleUrl: 'settings.scss', }) export class AddonNotificationsSettingsPage implements OnInit, OnDestroy { @@ -179,7 +179,7 @@ export class AddonNotificationsSettingsPage implements OnInit, OnDestroy { * @returns Promise resolved when done. */ protected async updatePreferences(): Promise { - await CoreUtils.ignoreErrors(AddonNotifications.invalidateNotificationPreferences()); + await CorePromiseUtils.ignoreErrors(AddonNotifications.invalidateNotificationPreferences()); await AddonNotifications.getNotificationPreferences(); } @@ -204,7 +204,7 @@ export class AddonNotificationsSettingsPage implements OnInit, OnDestroy { */ async refreshPreferences(refresher?: HTMLIonRefresherElement): Promise { try { - await CoreUtils.ignoreErrors(AddonNotifications.invalidateNotificationPreferences()); + await CorePromiseUtils.ignoreErrors(AddonNotifications.invalidateNotificationPreferences()); await this.fetchPreferences(); } finally { @@ -334,7 +334,7 @@ export class AddonNotificationsSettingsPage implements OnInit, OnDestroy { * @param enabled True to enable the notification sound, false to disable it. */ async changeNotificationSound(enabled: boolean): Promise { - await CoreUtils.ignoreErrors(CoreConfig.set(CoreConstants.SETTINGS_NOTIFICATION_SOUND, enabled ? 1 : 0)); + await CorePromiseUtils.ignoreErrors(CoreConfig.set(CoreConstants.SETTINGS_NOTIFICATION_SOUND, enabled ? 1 : 0)); const siteId = CoreSites.getCurrentSiteId(); CoreEvents.trigger(CoreEvents.NOTIFICATION_SOUND_CHANGED, { enabled }, siteId); diff --git a/src/addons/notifications/services/handlers/mainmenu.ts b/src/addons/notifications/services/handlers/mainmenu.ts index ddf7ce72df1..6dda0771b2a 100644 --- a/src/addons/notifications/services/handlers/mainmenu.ts +++ b/src/addons/notifications/services/handlers/mainmenu.ts @@ -15,14 +15,14 @@ import { Injectable } from '@angular/core'; import { CoreSites } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { makeSingleton } from '@singletons'; import { CoreEvents } from '@singletons/events'; import { CoreMainMenuHandler, CoreMainMenuHandlerData } from '@features/mainmenu/services/mainmenu-delegate'; import { CorePushNotifications } from '@features/pushnotifications/services/pushnotifications'; import { CorePushNotificationsDelegate } from '@features/pushnotifications/services/push-delegate'; import { AddonNotifications, AddonNotificationsProvider } from '../notifications'; -import { CoreMainMenuProvider } from '@features/mainmenu/services/mainmenu'; +import { MAIN_MENU_HANDLER_BADGE_UPDATED_EVENT } from '@features/mainmenu/constants'; /** * Handler to inject an option into main menu. @@ -120,7 +120,7 @@ export class AddonNotificationsMainMenuHandlerService implements CoreMainMenuHan CorePushNotifications.updateAddonCounter(AddonNotificationsMainMenuHandlerService.name, unreadCountData.count, siteId); CoreEvents.trigger( - CoreMainMenuProvider.MAIN_MENU_HANDLER_BADGE_UPDATED, + MAIN_MENU_HANDLER_BADGE_UPDATED_EVENT, { handler: AddonNotificationsMainMenuHandlerService.name, value: unreadCountData.count, diff --git a/src/addons/notifications/services/handlers/push-click.ts b/src/addons/notifications/services/handlers/push-click.ts index 9866bd5f4fc..a46d4d273ad 100644 --- a/src/addons/notifications/services/handlers/push-click.ts +++ b/src/addons/notifications/services/handlers/push-click.ts @@ -15,7 +15,7 @@ import { Injectable } from '@angular/core'; import { CoreNavigator } from '@services/navigator'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { makeSingleton } from '@singletons'; import { CorePushNotificationsClickHandler } from '@features/pushnotifications/services/push-delegate'; import { CorePushNotificationsNotificationBasicData } from '@features/pushnotifications/services/pushnotifications'; @@ -24,6 +24,8 @@ import { AddonNotifications } from '../notifications'; import { AddonNotificationsMainMenuHandlerService } from './mainmenu'; import { AddonNotificationsHelper } from '../notifications-helper'; import { CoreViewer } from '@features/viewer/services/viewer'; +import { CorePromiseUtils } from '@singletons/promise-utils'; +import { CoreOpener } from '@singletons/opener'; /** * Handler for non-messaging push notifications clicks. @@ -64,7 +66,7 @@ export class AddonNotificationsPushClickHandlerService implements CorePushNotifi * @returns Promise resolved when done. */ protected async markAsRead(notification: AddonNotificationsPushNotification): Promise { - await CoreUtils.ignoreErrors(AddonNotificationsHelper.markNotificationAsRead(notification)); + await CorePromiseUtils.ignoreErrors(AddonNotificationsHelper.markNotificationAsRead(notification)); } /** @@ -89,12 +91,12 @@ export class AddonNotificationsPushClickHandlerService implements CorePushNotifi switch (notification.customdata.appurlopenin) { case 'inapp': - CoreUtils.openInApp(url); + CoreOpener.openInApp(url); return; case 'browser': - return CoreUtils.openInBrowser(url); + return CoreOpener.openInBrowser(url); default: { const treated = await CoreContentLinksHelper.handleLink(url, undefined, undefined, true); @@ -116,7 +118,7 @@ export class AddonNotificationsPushClickHandlerService implements CorePushNotifi } // No contexturl or cannot be handled by the app. Open the notifications page. - await CoreUtils.ignoreErrors(AddonNotifications.invalidateNotificationsList(notification.site)); + await CorePromiseUtils.ignoreErrors(AddonNotifications.invalidateNotificationsList(notification.site)); await CoreNavigator.navigateToSitePath( `${AddonNotificationsMainMenuHandlerService.PAGE_NAME}/list`, diff --git a/src/addons/notifications/services/notifications-helper.ts b/src/addons/notifications/services/notifications-helper.ts index e03c2b10c76..c1661198d93 100644 --- a/src/addons/notifications/services/notifications-helper.ts +++ b/src/addons/notifications/services/notifications-helper.ts @@ -14,7 +14,7 @@ import { Injectable } from '@angular/core'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreArray } from '@singletons/array'; import { makeSingleton } from '@singletons'; import { AddonMessageOutputDelegate } from '@addons/messageoutput/services/messageoutput-delegate'; import { @@ -30,6 +30,7 @@ import { import { CoreEvents } from '@singletons/events'; import { AddonNotificationsPushNotification } from './handlers/push-click'; import { CoreTimeUtils } from '@services/utils/time'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Service that provides some helper functions for notifications. @@ -37,19 +38,6 @@ import { CoreTimeUtils } from '@services/utils/time'; @Injectable({ providedIn: 'root' }) export class AddonNotificationsHelperProvider { - /** - * Formats the text of a notification. - * - * @param notification The notification object. - * @returns The notification formatted to render. - * @deprecated since 4.2. This function isn't needed anymore. - */ - formatNotificationText( - notification: AddonNotificationsNotificationMessageFormatted, - ): AddonNotificationsNotificationMessageFormatted { - return notification; - } - /** * Format preferences data. * @@ -65,7 +53,7 @@ export class AddonNotificationsHelperProvider { formattedPreferences.components.forEach((component) => { component.notifications.forEach((notification) => { - notification.processorsByName = CoreUtils.arrayToObject(notification.processors, 'name'); + notification.processorsByName = CoreArray.toObject(notification.processors, 'name'); }); }); @@ -132,7 +120,7 @@ export class AddonNotificationsHelperProvider { siteId = 'site' in notification ? notification.site : siteId; - await CoreUtils.ignoreErrors(AddonNotifications.markNotificationRead(notifId, siteId)); + await CorePromiseUtils.ignoreErrors(AddonNotifications.markNotificationRead(notifId, siteId)); const time = CoreTimeUtils.timestamp(); if ('read' in notification) { @@ -140,7 +128,7 @@ export class AddonNotificationsHelperProvider { notification.timeread = time; } - await CoreUtils.ignoreErrors(AddonNotifications.invalidateNotificationsList()); + await CorePromiseUtils.ignoreErrors(AddonNotifications.invalidateNotificationsList()); CoreEvents.trigger(AddonNotificationsProvider.READ_CHANGED_EVENT, { id: notifId, diff --git a/src/addons/notifications/services/notifications.ts b/src/addons/notifications/services/notifications.ts index 542b24e7b1c..d80353f8d4c 100644 --- a/src/addons/notifications/services/notifications.ts +++ b/src/addons/notifications/services/notifications.ts @@ -19,12 +19,12 @@ import { CoreWSExternalWarning } from '@services/ws'; import { CoreText } from '@singletons/text'; import { CoreTimeUtils } from '@services/utils/time'; import { CoreUser, USER_NOREPLY_USER } from '@features/user/services/user'; -import { CoreSite } from '@classes/sites/site'; import { CoreLogger } from '@singletons/logger'; import { Translate, makeSingleton } from '@singletons'; import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate'; import { AddonNotificationsPushNotification } from './handlers/push-click'; import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site'; +import { CoreCacheUpdateFrequency } from '@/core/constants'; declare module '@singletons/events' { @@ -187,7 +187,7 @@ export class AddonNotificationsProvider { const site = await CoreSites.getSite(options.siteId); const preSets: CoreSiteWSPreSets = { cacheKey: this.getNotificationPreferencesCacheKey(), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + updateFrequency: CoreCacheUpdateFrequency.SOMETIMES, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; diff --git a/src/addons/privatefiles/components/file/file.ts b/src/addons/privatefiles/components/file/file.ts index f274a0d4a5b..061dd382cae 100644 --- a/src/addons/privatefiles/components/file/file.ts +++ b/src/addons/privatefiles/components/file/file.ts @@ -21,7 +21,7 @@ import { CoreFileComponent } from '@components/file/file'; selector: 'addon-privatefiles-file', templateUrl: 'file.html', standalone: true, - styleUrls: ['file.scss'], + styleUrl: 'file.scss', imports: [CoreSharedModule], }) export class AddonPrivateFilesFileComponent extends CoreFileComponent implements OnDestroy { diff --git a/src/addons/privatefiles/pages/index/index.ts b/src/addons/privatefiles/pages/index/index.ts index 8079da293cc..1e4668a6504 100644 --- a/src/addons/privatefiles/pages/index/index.ts +++ b/src/addons/privatefiles/pages/index/index.ts @@ -29,7 +29,7 @@ import { AddonPrivateFilesGetFilesWSParams, } from '@addons/privatefiles/services/privatefiles'; import { AddonPrivateFilesHelper } from '@addons/privatefiles/services/privatefiles-helper'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreNavigator } from '@services/navigator'; import { CoreTime } from '@singletons/time'; import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics'; @@ -45,7 +45,7 @@ import { AddonPrivateFilesFileComponent } from '@addons/privatefiles/components/ @Component({ selector: 'page-addon-privatefiles-index', templateUrl: 'index.html', - styleUrls: ['./index.scss'], + styleUrl: './index.scss', }) export class AddonPrivateFilesIndexPage implements OnInit, OnDestroy { @@ -192,7 +192,7 @@ export class AddonPrivateFilesIndexPage implements OnInit, OnDestroy { // File uploaded, refresh the list. this.filesLoaded = false; - await CoreUtils.ignoreErrors(this.refreshFiles()); + await CorePromiseUtils.ignoreErrors(this.refreshFiles()); this.filesLoaded = true; } catch (error) { diff --git a/src/addons/privatefiles/privatefiles-lazy.module.ts b/src/addons/privatefiles/privatefiles-lazy.module.ts index 8677298e31b..a0468983d07 100644 --- a/src/addons/privatefiles/privatefiles-lazy.module.ts +++ b/src/addons/privatefiles/privatefiles-lazy.module.ts @@ -60,4 +60,4 @@ function buildRoutes(injector: Injector): Routes { }, ], }) -export class AddonPrivateFilesLazyModule {} +export default class AddonPrivateFilesLazyModule {} diff --git a/src/addons/privatefiles/privatefiles.module.ts b/src/addons/privatefiles/privatefiles.module.ts index 2bdaa95cafa..f4ed512208e 100644 --- a/src/addons/privatefiles/privatefiles.module.ts +++ b/src/addons/privatefiles/privatefiles.module.ts @@ -38,7 +38,7 @@ export async function getPrivateFilesServices(): Promise[]> { const routes: Routes = [ { path: AddonPrivateFilesUserHandlerService.PAGE_NAME, - loadChildren: () => import('@addons/privatefiles/privatefiles-lazy.module').then(m => m.AddonPrivateFilesLazyModule), + loadChildren: () => import('@addons/privatefiles/privatefiles-lazy.module'), }, ]; diff --git a/src/addons/privatefiles/services/privatefiles.ts b/src/addons/privatefiles/services/privatefiles.ts index 3cb2cd9b2aa..66e615ffc10 100644 --- a/src/addons/privatefiles/services/privatefiles.ts +++ b/src/addons/privatefiles/services/privatefiles.ts @@ -19,7 +19,7 @@ import { CoreMimetypeUtils } from '@services/utils/mimetype'; import { CoreWSExternalWarning } from '@services/ws'; import { CoreSite } from '@classes/sites/site'; import { makeSingleton } from '@singletons'; -import { ContextLevel } from '@/core/constants'; +import { ContextLevel, CoreCacheUpdateFrequency } from '@/core/constants'; import { CoreFileUploader } from '@features/fileuploader/services/fileuploader'; const ROOT_CACHE_KEY = 'mmaFiles:'; @@ -84,7 +84,7 @@ export class AddonPrivateFilesProvider { const preSets = { cacheKey: this.getFilesListCacheKey(params), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + updateFrequency: CoreCacheUpdateFrequency.SOMETIMES, }; const result: AddonPrivateFilesGetFilesWSResult = await site.read('core_files_get_files', params, preSets); @@ -163,7 +163,7 @@ export class AddonPrivateFilesProvider { }; const preSets = { cacheKey: this.getPrivateFilesInfoCacheKey(userId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + updateFrequency: CoreCacheUpdateFrequency.SOMETIMES, }; return site.read('core_user_get_private_files_info', params, preSets); diff --git a/src/addons/qtype/calculated/component/calculated.ts b/src/addons/qtype/calculated/component/calculated.ts index 18c06481967..1b5121a6ffe 100644 --- a/src/addons/qtype/calculated/component/calculated.ts +++ b/src/addons/qtype/calculated/component/calculated.ts @@ -22,7 +22,7 @@ import { AddonModQuizCalculatedQuestion, CoreQuestionBaseComponent } from '@feat @Component({ selector: 'addon-qtype-calculated', templateUrl: 'addon-qtype-calculated.html', - styleUrls: ['calculated.scss'], + styleUrl: 'calculated.scss', }) export class AddonQtypeCalculatedComponent extends CoreQuestionBaseComponent { diff --git a/src/addons/qtype/calculated/services/handlers/calculated.ts b/src/addons/qtype/calculated/services/handlers/calculated.ts index 5bc61035659..316372f107f 100644 --- a/src/addons/qtype/calculated/services/handlers/calculated.ts +++ b/src/addons/qtype/calculated/services/handlers/calculated.ts @@ -17,9 +17,8 @@ import { Injectable, Type } from '@angular/core'; import { CoreQuestionQuestionParsed, CoreQuestionsAnswers } from '@features/question/services/question'; import { CoreQuestionHandler } from '@features/question/services/question-delegate'; import { convertTextToHTMLElement } from '@/core/utils/create-html-element'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreObject } from '@singletons/object'; import { makeSingleton } from '@singletons'; -import { AddonQtypeCalculatedComponent } from '../../component/calculated'; /** * Handler to support calculated question type. @@ -41,7 +40,9 @@ export class AddonQtypeCalculatedHandlerService implements CoreQuestionHandler { /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { + const { AddonQtypeCalculatedComponent } = await import('../../component/calculated'); + return AddonQtypeCalculatedComponent; } @@ -132,8 +133,8 @@ export class AddonQtypeCalculatedHandlerService implements CoreQuestionHandler { prevAnswers: CoreQuestionsAnswers, newAnswers: CoreQuestionsAnswers, ): boolean { - return CoreUtils.sameAtKeyMissingIsBlank(prevAnswers, newAnswers, 'answer') && - CoreUtils.sameAtKeyMissingIsBlank(prevAnswers, newAnswers, 'unit'); + return CoreObject.sameAtKeyMissingIsBlank(prevAnswers, newAnswers, 'answer') && + CoreObject.sameAtKeyMissingIsBlank(prevAnswers, newAnswers, 'unit'); } /** diff --git a/src/addons/qtype/calculatedmulti/services/handlers/calculatedmulti.ts b/src/addons/qtype/calculatedmulti/services/handlers/calculatedmulti.ts index 65abda13aa8..3d06667c31f 100644 --- a/src/addons/qtype/calculatedmulti/services/handlers/calculatedmulti.ts +++ b/src/addons/qtype/calculatedmulti/services/handlers/calculatedmulti.ts @@ -14,7 +14,6 @@ import { Injectable, Type } from '@angular/core'; -import { AddonQtypeMultichoiceComponent } from '@addons/qtype/multichoice/component/multichoice'; import { CoreQuestionQuestionParsed, CoreQuestionsAnswers } from '@features/question/services/question'; import { CoreQuestionHandler } from '@features/question/services/question-delegate'; import { makeSingleton } from '@singletons'; @@ -32,8 +31,10 @@ export class AddonQtypeCalculatedMultiHandlerService implements CoreQuestionHand /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { // Calculated multi behaves like a multichoice, use the same component. + const { AddonQtypeMultichoiceComponent } = await import('@addons/qtype/multichoice/component/multichoice'); + return AddonQtypeMultichoiceComponent; } diff --git a/src/addons/qtype/calculatedsimple/services/handlers/calculatedsimple.ts b/src/addons/qtype/calculatedsimple/services/handlers/calculatedsimple.ts index 4a7f60532d7..787e2d552cf 100644 --- a/src/addons/qtype/calculatedsimple/services/handlers/calculatedsimple.ts +++ b/src/addons/qtype/calculatedsimple/services/handlers/calculatedsimple.ts @@ -14,7 +14,6 @@ import { Injectable, Type } from '@angular/core'; -import { AddonQtypeCalculatedComponent } from '@addons/qtype/calculated/component/calculated'; import { CoreQuestionHandler } from '@features/question/services/question-delegate'; import { AddonQtypeCalculatedHandler } from '@addons/qtype/calculated/services/handlers/calculated'; import { CoreQuestionQuestionParsed, CoreQuestionsAnswers } from '@features/question/services/question'; @@ -32,8 +31,10 @@ export class AddonQtypeCalculatedSimpleHandlerService implements CoreQuestionHan /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { // Calculated simple behaves like a calculated, use the same component. + const { AddonQtypeCalculatedComponent } = await import('@addons/qtype/calculated/component/calculated'); + return AddonQtypeCalculatedComponent; } diff --git a/src/addons/qtype/ddimageortext/services/handlers/ddimageortext.ts b/src/addons/qtype/ddimageortext/services/handlers/ddimageortext.ts index ad9431be59d..5729a9fe05e 100644 --- a/src/addons/qtype/ddimageortext/services/handlers/ddimageortext.ts +++ b/src/addons/qtype/ddimageortext/services/handlers/ddimageortext.ts @@ -17,7 +17,6 @@ import { Injectable, Type } from '@angular/core'; import { CoreQuestion, CoreQuestionQuestionParsed, CoreQuestionsAnswers } from '@features/question/services/question'; import { CoreQuestionHandler } from '@features/question/services/question-delegate'; import { makeSingleton } from '@singletons'; -import { AddonQtypeDdImageOrTextComponent } from '../../component/ddimageortext'; /** * Handler to support drag-and-drop onto image question type. @@ -42,7 +41,9 @@ export class AddonQtypeDdImageOrTextHandlerService implements CoreQuestionHandle /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { + const { AddonQtypeDdImageOrTextComponent } = await import('../../component/ddimageortext'); + return AddonQtypeDdImageOrTextComponent; } diff --git a/src/addons/qtype/ddmarker/classes/ddmarker.ts b/src/addons/qtype/ddmarker/classes/ddmarker.ts index 1123535717e..05e0b8fd73c 100644 --- a/src/addons/qtype/ddmarker/classes/ddmarker.ts +++ b/src/addons/qtype/ddmarker/classes/ddmarker.ts @@ -19,7 +19,7 @@ import { CoreEventObserver } from '@singletons/events'; import { CoreLogger } from '@singletons/logger'; import { AddonQtypeDdMarkerQuestionData } from '../component/ddmarker'; import { AddonQtypeDdMarkerGraphicsApi } from './graphics_api'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreDirectivesRegistry } from '@singletons/directives-registry'; import { CoreExternalContentDirective } from '@directives/external-content'; import { CoreLinkDirective } from '@directives/link'; @@ -715,7 +715,7 @@ export class AddonQtypeDdMarkerQuestion { // Wait for image to be visible, otherwise the calculated positions are wrong. const visiblePromise = CoreDom.waitToBeVisible(bgImg); - await CoreUtils.ignoreErrors(CoreUtils.timeoutPromise(visiblePromise, 500)); + await CorePromiseUtils.ignoreErrors(CorePromiseUtils.timeoutPromise(visiblePromise, 500)); visiblePromise.cancel(); // In case of timeout, cancel the promise. this.redrawDragsAndDrops(); diff --git a/src/addons/qtype/ddmarker/component/ddmarker.ts b/src/addons/qtype/ddmarker/component/ddmarker.ts index e6b22c13e8e..e2cd2ebaa5e 100644 --- a/src/addons/qtype/ddmarker/component/ddmarker.ts +++ b/src/addons/qtype/ddmarker/component/ddmarker.ts @@ -27,7 +27,7 @@ import { AddonQtypeDdMarkerQuestion } from '../classes/ddmarker'; @Component({ selector: 'addon-qtype-ddmarker', templateUrl: 'addon-qtype-ddmarker.html', - styleUrls: ['ddmarker.scss'], + styleUrl: 'ddmarker.scss', }) export class AddonQtypeDdMarkerComponent extends CoreQuestionBaseComponent diff --git a/src/addons/qtype/ddmarker/services/handlers/ddmarker.ts b/src/addons/qtype/ddmarker/services/handlers/ddmarker.ts index 03ce4ff7ac3..899e0619257 100644 --- a/src/addons/qtype/ddmarker/services/handlers/ddmarker.ts +++ b/src/addons/qtype/ddmarker/services/handlers/ddmarker.ts @@ -19,7 +19,6 @@ import { CoreQuestionHandler } from '@features/question/services/question-delega import { CoreQuestionHelper, CoreQuestionQuestion } from '@features/question/services/question-helper'; import { CoreWSFile } from '@services/ws'; import { makeSingleton } from '@singletons'; -import { AddonQtypeDdMarkerComponent } from '../../component/ddmarker'; /** * Handler to support drag-and-drop markers question type. @@ -44,7 +43,9 @@ export class AddonQtypeDdMarkerHandlerService implements CoreQuestionHandler { /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { + const { AddonQtypeDdMarkerComponent } = await import('../../component/ddmarker'); + return AddonQtypeDdMarkerComponent; } diff --git a/src/addons/qtype/ddwtos/services/handlers/ddwtos.ts b/src/addons/qtype/ddwtos/services/handlers/ddwtos.ts index dc05ebf928f..f6766968198 100644 --- a/src/addons/qtype/ddwtos/services/handlers/ddwtos.ts +++ b/src/addons/qtype/ddwtos/services/handlers/ddwtos.ts @@ -17,7 +17,6 @@ import { Injectable, Type } from '@angular/core'; import { CoreQuestion, CoreQuestionQuestionParsed, CoreQuestionsAnswers } from '@features/question/services/question'; import { CoreQuestionHandler } from '@features/question/services/question-delegate'; import { makeSingleton } from '@singletons'; -import { AddonQtypeDdwtosComponent } from '../../component/ddwtos'; /** * Handler to support drag-and-drop words into sentences question type. @@ -42,7 +41,9 @@ export class AddonQtypeDdwtosHandlerService implements CoreQuestionHandler { /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { + const { AddonQtypeDdwtosComponent } = await import('../../component/ddwtos'); + return AddonQtypeDdwtosComponent; } diff --git a/src/addons/qtype/description/services/handlers/description.ts b/src/addons/qtype/description/services/handlers/description.ts index 7a0630ed851..a9775fec876 100644 --- a/src/addons/qtype/description/services/handlers/description.ts +++ b/src/addons/qtype/description/services/handlers/description.ts @@ -16,7 +16,6 @@ import { Injectable, Type } from '@angular/core'; import { CoreQuestionHandler } from '@features/question/services/question-delegate'; import { makeSingleton } from '@singletons'; -import { AddonQtypeDescriptionComponent } from '../../component/description'; /** * Handler to support description question type. @@ -37,7 +36,9 @@ export class AddonQtypeDescriptionHandlerService implements CoreQuestionHandler /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { + const { AddonQtypeDescriptionComponent } = await import('../../component/description'); + return AddonQtypeDescriptionComponent; } diff --git a/src/addons/qtype/essay/services/handlers/essay.ts b/src/addons/qtype/essay/services/handlers/essay.ts index cc816654695..1ded4f9db73 100644 --- a/src/addons/qtype/essay/services/handlers/essay.ts +++ b/src/addons/qtype/essay/services/handlers/essay.ts @@ -24,10 +24,9 @@ import { CoreFileSession } from '@services/file-session'; import { CoreSites } from '@services/sites'; import { convertTextToHTMLElement } from '@/core/utils/create-html-element'; import { CoreText } from '@singletons/text'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreObject } from '@singletons/object'; import { CoreWSFile } from '@services/ws'; import { makeSingleton, Translate } from '@singletons'; -import { AddonQtypeEssayComponent } from '../../component/essay'; import { CoreFileHelper } from '@services/file-helper'; /** @@ -108,7 +107,9 @@ export class AddonQtypeEssayHandlerService implements CoreQuestionHandler { /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { + const { AddonQtypeEssayComponent } = await import('../../component/essay'); + return AddonQtypeEssayComponent; } @@ -266,7 +267,7 @@ export class AddonQtypeEssayHandlerService implements CoreQuestionHandler { // First check the inline text. const answerIsEqual = allowedOptions.text ? - CoreUtils.sameAtKeyMissingIsBlank(prevAnswers, newAnswers, 'answer') : true; + CoreObject.sameAtKeyMissingIsBlank(prevAnswers, newAnswers, 'answer') : true; if (!allowedOptions.attachments || !uploadFilesSupported || !answerIsEqual) { // No need to check attachments. diff --git a/src/addons/qtype/gapselect/component/gapselect.ts b/src/addons/qtype/gapselect/component/gapselect.ts index 5e5d458c5a5..0c0852eb1f0 100644 --- a/src/addons/qtype/gapselect/component/gapselect.ts +++ b/src/addons/qtype/gapselect/component/gapselect.ts @@ -23,7 +23,7 @@ import { CoreQuestionHelper } from '@features/question/services/question-helper' @Component({ selector: 'addon-qtype-gapselect', templateUrl: 'addon-qtype-gapselect.html', - styleUrls: ['gapselect.scss'], + styleUrl: 'gapselect.scss', }) export class AddonQtypeGapSelectComponent extends CoreQuestionBaseComponent { diff --git a/src/addons/qtype/gapselect/services/handlers/gapselect.ts b/src/addons/qtype/gapselect/services/handlers/gapselect.ts index 5337943cefb..3aa587ccd2c 100644 --- a/src/addons/qtype/gapselect/services/handlers/gapselect.ts +++ b/src/addons/qtype/gapselect/services/handlers/gapselect.ts @@ -17,7 +17,6 @@ import { Injectable, Type } from '@angular/core'; import { CoreQuestion, CoreQuestionQuestionParsed, CoreQuestionsAnswers } from '@features/question/services/question'; import { CoreQuestionHandler } from '@features/question/services/question-delegate'; import { makeSingleton } from '@singletons'; -import { AddonQtypeGapSelectComponent } from '../../component/gapselect'; /** * Handler to support gapselect question type. @@ -42,7 +41,9 @@ export class AddonQtypeGapSelectHandlerService implements CoreQuestionHandler { /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { + const { AddonQtypeGapSelectComponent } = await import('../../component/gapselect'); + return AddonQtypeGapSelectComponent; } diff --git a/src/addons/qtype/match/component/match.ts b/src/addons/qtype/match/component/match.ts index 8117b779507..7f0f5738395 100644 --- a/src/addons/qtype/match/component/match.ts +++ b/src/addons/qtype/match/component/match.ts @@ -22,7 +22,7 @@ import { AddonModQuizMatchQuestion, CoreQuestionBaseComponent } from '@features/ @Component({ selector: 'addon-qtype-match', templateUrl: 'addon-qtype-match.html', - styleUrls: ['match.scss'], + styleUrl: 'match.scss', }) export class AddonQtypeMatchComponent extends CoreQuestionBaseComponent { diff --git a/src/addons/qtype/match/services/handlers/match.ts b/src/addons/qtype/match/services/handlers/match.ts index 20784b47a8c..e70ef44fc70 100644 --- a/src/addons/qtype/match/services/handlers/match.ts +++ b/src/addons/qtype/match/services/handlers/match.ts @@ -17,7 +17,6 @@ import { Injectable, Type } from '@angular/core'; import { CoreQuestion, CoreQuestionQuestionParsed, CoreQuestionsAnswers } from '@features/question/services/question'; import { CoreQuestionHandler } from '@features/question/services/question-delegate'; import { makeSingleton } from '@singletons'; -import { AddonQtypeMatchComponent } from '../../component/match'; /** * Handler to support match question type. @@ -42,7 +41,9 @@ export class AddonQtypeMatchHandlerService implements CoreQuestionHandler { /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { + const { AddonQtypeMatchComponent } = await import('../../component/match'); + return AddonQtypeMatchComponent; } diff --git a/src/addons/qtype/multianswer/component/multianswer.ts b/src/addons/qtype/multianswer/component/multianswer.ts index b52ffa63db3..f9c3adc3c13 100644 --- a/src/addons/qtype/multianswer/component/multianswer.ts +++ b/src/addons/qtype/multianswer/component/multianswer.ts @@ -22,7 +22,7 @@ import { CoreQuestionHelper } from '@features/question/services/question-helper' @Component({ selector: 'addon-qtype-multianswer', templateUrl: 'addon-qtype-multianswer.html', - styleUrls: ['multianswer.scss'], + styleUrl: 'multianswer.scss', }) export class AddonQtypeMultiAnswerComponent extends CoreQuestionBaseComponent { diff --git a/src/addons/qtype/multianswer/services/handlers/multianswer.ts b/src/addons/qtype/multianswer/services/handlers/multianswer.ts index a09a50b3ed0..322c75d6954 100644 --- a/src/addons/qtype/multianswer/services/handlers/multianswer.ts +++ b/src/addons/qtype/multianswer/services/handlers/multianswer.ts @@ -18,7 +18,6 @@ import { CoreQuestion, CoreQuestionQuestionParsed, CoreQuestionsAnswers } from ' import { CoreQuestionHandler } from '@features/question/services/question-delegate'; import { CoreQuestionHelper } from '@features/question/services/question-helper'; import { makeSingleton } from '@singletons'; -import { AddonQtypeMultiAnswerComponent } from '../../component/multianswer'; /** * Handler to support multianswer question type. @@ -43,7 +42,9 @@ export class AddonQtypeMultiAnswerHandlerService implements CoreQuestionHandler /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { + const { AddonQtypeMultiAnswerComponent } = await import('../../component/multianswer'); + return AddonQtypeMultiAnswerComponent; } diff --git a/src/addons/qtype/multichoice/component/multichoice.ts b/src/addons/qtype/multichoice/component/multichoice.ts index 0be0f695129..3a615401de3 100644 --- a/src/addons/qtype/multichoice/component/multichoice.ts +++ b/src/addons/qtype/multichoice/component/multichoice.ts @@ -22,7 +22,7 @@ import { AddonModQuizMultichoiceQuestion, CoreQuestionBaseComponent } from '@fea @Component({ selector: 'addon-qtype-multichoice', templateUrl: 'addon-qtype-multichoice.html', - styleUrls: ['multichoice.scss'], + styleUrl: 'multichoice.scss', }) export class AddonQtypeMultichoiceComponent extends CoreQuestionBaseComponent { diff --git a/src/addons/qtype/multichoice/services/handlers/multichoice.ts b/src/addons/qtype/multichoice/services/handlers/multichoice.ts index 0d6e3f6e487..9a02f2e2fd4 100644 --- a/src/addons/qtype/multichoice/services/handlers/multichoice.ts +++ b/src/addons/qtype/multichoice/services/handlers/multichoice.ts @@ -17,9 +17,8 @@ import { Injectable, Type } from '@angular/core'; import { AddonModQuizMultichoiceQuestion } from '@features/question/classes/base-question-component'; import { CoreQuestionQuestionParsed, CoreQuestionsAnswers } from '@features/question/services/question'; import { CoreQuestionHandler } from '@features/question/services/question-delegate'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreObject } from '@singletons/object'; import { makeSingleton } from '@singletons'; -import { AddonQtypeMultichoiceComponent } from '../../component/multichoice'; /** * Handler to support multichoice question type. @@ -33,7 +32,9 @@ export class AddonQtypeMultichoiceHandlerService implements CoreQuestionHandler /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { + const { AddonQtypeMultichoiceComponent } = await import('../../component/multichoice'); + return AddonQtypeMultichoiceComponent; } @@ -119,7 +120,7 @@ export class AddonQtypeMultichoiceHandlerService implements CoreQuestionHandler for (const name in newAnswers) { if (name.indexOf('choice') !== -1) { isSingle = false; - if (!CoreUtils.sameAtKeyMissingIsBlank(prevAnswers, newAnswers, name)) { + if (!CoreObject.sameAtKeyMissingIsBlank(prevAnswers, newAnswers, name)) { isMultiSame = false; break; } @@ -141,7 +142,7 @@ export class AddonQtypeMultichoiceHandlerService implements CoreQuestionHandler * @returns Whether they're the same. */ isSameResponseSingle(prevAnswers: CoreQuestionsAnswers, newAnswers: CoreQuestionsAnswers): boolean { - return CoreUtils.sameAtKeyMissingIsBlank(prevAnswers, newAnswers, 'answer'); + return CoreObject.sameAtKeyMissingIsBlank(prevAnswers, newAnswers, 'answer'); } /** diff --git a/src/addons/qtype/shortanswer/component/shortanswer.ts b/src/addons/qtype/shortanswer/component/shortanswer.ts index 990c195cc40..c955c6bd365 100644 --- a/src/addons/qtype/shortanswer/component/shortanswer.ts +++ b/src/addons/qtype/shortanswer/component/shortanswer.ts @@ -22,7 +22,7 @@ import { AddonModQuizTextQuestion, CoreQuestionBaseComponent } from '@features/q @Component({ selector: 'addon-qtype-shortanswer', templateUrl: 'addon-qtype-shortanswer.html', - styleUrls: ['shortanswer.scss'], + styleUrl: 'shortanswer.scss', }) export class AddonQtypeShortAnswerComponent extends CoreQuestionBaseComponent { diff --git a/src/addons/qtype/shortanswer/services/handlers/shortanswer.ts b/src/addons/qtype/shortanswer/services/handlers/shortanswer.ts index 02b21c7f50a..1f34de4ded5 100644 --- a/src/addons/qtype/shortanswer/services/handlers/shortanswer.ts +++ b/src/addons/qtype/shortanswer/services/handlers/shortanswer.ts @@ -16,9 +16,8 @@ import { Injectable, Type } from '@angular/core'; import { CoreQuestionQuestionParsed, CoreQuestionsAnswers } from '@features/question/services/question'; import { CoreQuestionHandler } from '@features/question/services/question-delegate'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreObject } from '@singletons/object'; import { makeSingleton } from '@singletons'; -import { AddonQtypeShortAnswerComponent } from '../../component/shortanswer'; /** * Handler to support short answer question type. @@ -32,7 +31,9 @@ export class AddonQtypeShortAnswerHandlerService implements CoreQuestionHandler /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { + const { AddonQtypeShortAnswerComponent } = await import('../../component/shortanswer'); + return AddonQtypeShortAnswerComponent; } @@ -71,7 +72,7 @@ export class AddonQtypeShortAnswerHandlerService implements CoreQuestionHandler prevAnswers: CoreQuestionsAnswers, newAnswers: CoreQuestionsAnswers, ): boolean { - return CoreUtils.sameAtKeyMissingIsBlank(prevAnswers, newAnswers, 'answer'); + return CoreObject.sameAtKeyMissingIsBlank(prevAnswers, newAnswers, 'answer'); } } diff --git a/src/addons/qtype/truefalse/services/handlers/truefalse.ts b/src/addons/qtype/truefalse/services/handlers/truefalse.ts index ecec42b1592..7e1b7bd03cd 100644 --- a/src/addons/qtype/truefalse/services/handlers/truefalse.ts +++ b/src/addons/qtype/truefalse/services/handlers/truefalse.ts @@ -14,10 +14,9 @@ import { Injectable, Type } from '@angular/core'; -import { AddonQtypeMultichoiceComponent } from '@addons/qtype/multichoice/component/multichoice'; import { CoreQuestionHandler } from '@features/question/services/question-delegate'; import { CoreQuestionQuestionParsed, CoreQuestionsAnswers } from '@features/question/services/question'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreObject } from '@singletons/object'; import { AddonModQuizMultichoiceQuestion } from '@features/question/classes/base-question-component'; import { makeSingleton } from '@singletons'; @@ -33,8 +32,10 @@ export class AddonQtypeTrueFalseHandlerService implements CoreQuestionHandler { /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { // True/false behaves like a multichoice, use the same component. + const { AddonQtypeMultichoiceComponent } = await import('@addons/qtype/multichoice/component/multichoice'); + return AddonQtypeMultichoiceComponent; } @@ -73,7 +74,7 @@ export class AddonQtypeTrueFalseHandlerService implements CoreQuestionHandler { prevAnswers: CoreQuestionsAnswers, newAnswers: CoreQuestionsAnswers, ): boolean { - return CoreUtils.sameAtKeyMissingIsBlank(prevAnswers, newAnswers, 'answer'); + return CoreObject.sameAtKeyMissingIsBlank(prevAnswers, newAnswers, 'answer'); } /** diff --git a/src/addons/remotethemes/services/remotethemes-handler.ts b/src/addons/remotethemes/services/remotethemes-handler.ts index 16b48f12624..1b6c337c54e 100644 --- a/src/addons/remotethemes/services/remotethemes-handler.ts +++ b/src/addons/remotethemes/services/remotethemes-handler.ts @@ -22,7 +22,7 @@ import { CoreWS } from '@services/ws'; import { makeSingleton } from '@singletons'; import { CoreStyleHandler, CoreStylesService } from '@features/styles/services/styles'; import { CoreLogger } from '@singletons/logger'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; const COMPONENT = 'mmaRemoteStyles'; @@ -89,7 +89,7 @@ export class AddonRemoteThemesHandlerService implements CoreStyleHandler { if (style != '') { // Treat the CSS. - CoreUtils.ignoreErrors( + CorePromiseUtils.ignoreErrors( CoreFilepool.treatCSSCode(siteId, infos.mobilecssurl, style, COMPONENT, 1), ); } diff --git a/src/addons/storagemanager/pages/course-storage/course-storage.ts b/src/addons/storagemanager/pages/course-storage/course-storage.ts index c6431493093..4d1a60ae9ff 100644 --- a/src/addons/storagemanager/pages/course-storage/course-storage.ts +++ b/src/addons/storagemanager/pages/course-storage/course-storage.ts @@ -14,7 +14,8 @@ import { CoreConstants, DownloadStatus } from '@/core/constants'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, OnDestroy, OnInit } from '@angular/core'; -import { CoreCourse, CoreCourseProvider, sectionContentIsModule } from '@features/course/services/course'; +import { CORE_COURSE_ALL_COURSES_CLEARED, CORE_COURSE_ALL_SECTIONS_ID } from '@features/course/constants'; +import { CoreCourse, sectionContentIsModule } from '@features/course/services/course'; import { CoreCourseHelper, CoreCourseModuleData, @@ -181,7 +182,7 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy { // Listen for changes in course status. this.courseStatusObserver = CoreEvents.on(CoreEvents.COURSE_STATUS_CHANGED, (data) => { - if (data.courseId === this.courseId || data.courseId === CoreCourseProvider.ALL_COURSES_CLEARED) { + if (data.courseId === this.courseId || data.courseId === CORE_COURSE_ALL_COURSES_CLEARED) { this.updateCourseStatus(data.status); } }, CoreSites.getCurrentSiteId()); @@ -723,7 +724,7 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy { } await Promise.all(sections.map(async (section) => { - if (section.id === CoreCourseProvider.ALL_SECTIONS_ID) { + if (section.id === CORE_COURSE_ALL_SECTIONS_ID) { return; } diff --git a/src/addons/storagemanager/pages/courses-storage/courses-storage.ts b/src/addons/storagemanager/pages/courses-storage/courses-storage.ts index c09367c1df9..857ac0d2f8d 100644 --- a/src/addons/storagemanager/pages/courses-storage/courses-storage.ts +++ b/src/addons/storagemanager/pages/courses-storage/courses-storage.ts @@ -15,7 +15,8 @@ import { DownloadStatus } from '@/core/constants'; import { Component, OnDestroy, OnInit } from '@angular/core'; import { CoreQueueRunner } from '@classes/queue-runner'; -import { CoreCourse, CoreCourseProvider } from '@features/course/services/course'; +import { CORE_COURSE_ALL_COURSES_CLEARED } from '@features/course/constants'; +import { CoreCourse } from '@features/course/services/course'; import { CoreCourseHelper } from '@features/course/services/course-helper'; import { CoreCourses, CoreEnrolledCourseData } from '@features/courses/services/courses'; import { CoreSettingsHelper, CoreSiteSpaceUsage } from '@features/settings/services/settings-helper'; @@ -34,7 +35,7 @@ import { CoreEventObserver, CoreEvents } from '@singletons/events'; @Component({ selector: 'page-addon-storagemanager-courses-storage', templateUrl: 'courses-storage.html', - styleUrls: ['courses-storage.scss'], + styleUrl: 'courses-storage.scss', }) export class AddonStorageManagerCoursesStoragePage implements OnInit, OnDestroy { @@ -181,7 +182,7 @@ export class AddonStorageManagerCoursesStoragePage implements OnInit, OnDestroy * @param courseId Updated course id. */ private async onCourseUpdated(courseId: number, status: DownloadStatus): Promise { - if (courseId == CoreCourseProvider.ALL_COURSES_CLEARED) { + if (courseId == CORE_COURSE_ALL_COURSES_CLEARED) { await this.downloadedCoursesQueue.run(() => this.setDownloadedCourses([])); return; diff --git a/src/addons/userprofilefield/checkbox/component/checkbox.ts b/src/addons/userprofilefield/checkbox/component/checkbox.ts index be762fc8735..bd8f6d92644 100644 --- a/src/addons/userprofilefield/checkbox/component/checkbox.ts +++ b/src/addons/userprofilefield/checkbox/component/checkbox.ts @@ -17,7 +17,7 @@ import { Validators, FormControl } from '@angular/forms'; import { AuthEmailSignupProfileField } from '@features/login/services/login-helper'; import { CoreUserProfileFieldBaseComponent } from '@features/user/classes/base-profilefield-component'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; /** * Directive to render a checkbox user profile field. @@ -25,7 +25,7 @@ import { CoreUtils } from '@services/utils/utils'; @Component({ selector: 'addon-user-profile-field-checkbox', templateUrl: 'addon-user-profile-field-checkbox.html', - styleUrls: ['./checkbox.scss'], + styleUrl: './checkbox.scss', }) export class AddonUserProfileFieldCheckboxComponent extends CoreUserProfileFieldBaseComponent { diff --git a/src/addons/userprofilefield/checkbox/services/handlers/checkbox.ts b/src/addons/userprofilefield/checkbox/services/handlers/checkbox.ts index 3aa1804c5f6..c3b27005b8d 100644 --- a/src/addons/userprofilefield/checkbox/services/handlers/checkbox.ts +++ b/src/addons/userprofilefield/checkbox/services/handlers/checkbox.ts @@ -19,7 +19,6 @@ import { CoreUserProfileField } from '@features/user/services/user'; import { CoreUserProfileFieldHandler, CoreUserProfileFieldHandlerData } from '@features/user/services/user-profile-field-delegate'; import { CoreFormFields } from '@singletons/form'; import { makeSingleton } from '@singletons'; -import { AddonUserProfileFieldCheckboxComponent } from '../../component/checkbox'; /** * Checkbox user profile field handlers. @@ -66,12 +65,11 @@ export class AddonUserProfileFieldCheckboxHandlerService implements CoreUserProf } /** - * Return the Component to use to display the user profile field. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @returns The component (or promise resolved with component) to use, undefined if not found. + * @inheritdoc */ - getComponent(): Type | Promise> { + async getComponent(): Promise> { + const { AddonUserProfileFieldCheckboxComponent } = await import('../../component/checkbox'); + return AddonUserProfileFieldCheckboxComponent; } diff --git a/src/addons/userprofilefield/datetime/component/datetime.ts b/src/addons/userprofilefield/datetime/component/datetime.ts index af78bef7ee2..4693ea4290e 100644 --- a/src/addons/userprofilefield/datetime/component/datetime.ts +++ b/src/addons/userprofilefield/datetime/component/datetime.ts @@ -16,7 +16,7 @@ import { FormControl, Validators } from '@angular/forms'; import { Component } from '@angular/core'; import { CoreTimeUtils } from '@services/utils/time'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { AuthEmailSignupProfileField } from '@features/login/services/login-helper'; import { CoreUserProfileField } from '@features/user/services/user'; import { CoreUserProfileFieldBaseComponent } from '@features/user/classes/base-profilefield-component'; diff --git a/src/addons/userprofilefield/datetime/services/handlers/datetime.ts b/src/addons/userprofilefield/datetime/services/handlers/datetime.ts index 9c0663f4585..dc349941c5e 100644 --- a/src/addons/userprofilefield/datetime/services/handlers/datetime.ts +++ b/src/addons/userprofilefield/datetime/services/handlers/datetime.ts @@ -19,7 +19,6 @@ import { CoreUserProfileField } from '@features/user/services/user'; import { CoreUserProfileFieldHandler, CoreUserProfileFieldHandlerData } from '@features/user/services/user-profile-field-delegate'; import { CoreFormFields } from '@singletons/form'; import { makeSingleton } from '@singletons'; -import { AddonUserProfileFieldDatetimeComponent } from '../../component/datetime'; import moment from 'moment-timezone'; /** @@ -67,14 +66,13 @@ export class AddonUserProfileFieldDatetimeHandlerService implements CoreUserProf } /** - * Return the Component to use to display the user profile field. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @returns The component (or promise resolved with component) to use, undefined if not found. + * @inheritdoc */ - getComponent(): Type | Promise> { - return AddonUserProfileFieldDatetimeComponent; - } + async getComponent(): Promise> { + const { AddonUserProfileFieldDatetimeComponent } = await import('../../component/datetime'); + + return AddonUserProfileFieldDatetimeComponent; + } } diff --git a/src/addons/userprofilefield/menu/component/menu.ts b/src/addons/userprofilefield/menu/component/menu.ts index 2109277d018..602467823d9 100644 --- a/src/addons/userprofilefield/menu/component/menu.ts +++ b/src/addons/userprofilefield/menu/component/menu.ts @@ -23,7 +23,7 @@ import { CoreUserProfileFieldBaseComponent } from '@features/user/classes/base-p @Component({ selector: 'addon-user-profile-field-menu', templateUrl: 'addon-user-profile-field-menu.html', - styleUrls: ['./menu.scss'], + styleUrl: './menu.scss', }) export class AddonUserProfileFieldMenuComponent extends CoreUserProfileFieldBaseComponent { diff --git a/src/addons/userprofilefield/menu/services/handlers/menu.ts b/src/addons/userprofilefield/menu/services/handlers/menu.ts index bee7848d2b5..658941ddec3 100644 --- a/src/addons/userprofilefield/menu/services/handlers/menu.ts +++ b/src/addons/userprofilefield/menu/services/handlers/menu.ts @@ -19,7 +19,6 @@ import { CoreUserProfileField } from '@features/user/services/user'; import { CoreUserProfileFieldHandler, CoreUserProfileFieldHandlerData } from '@features/user/services/user-profile-field-delegate'; import { CoreFormFields } from '@singletons/form'; import { makeSingleton } from '@singletons'; -import { AddonUserProfileFieldMenuComponent } from '../../component/menu'; /** * Menu user profile field handlers. @@ -66,12 +65,11 @@ export class AddonUserProfileFieldMenuHandlerService implements CoreUserProfileF } /** - * Return the Component to use to display the user profile field. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @returns The component (or promise resolved with component) to use, undefined if not found. + * @inheritdoc */ - getComponent(): Type | Promise> { + async getComponent(): Promise> { + const { AddonUserProfileFieldMenuComponent } = await import('../../component/menu'); + return AddonUserProfileFieldMenuComponent; } diff --git a/src/addons/userprofilefield/social/services/handlers/social.ts b/src/addons/userprofilefield/social/services/handlers/social.ts index 73f837bfef7..6e22289946f 100644 --- a/src/addons/userprofilefield/social/services/handlers/social.ts +++ b/src/addons/userprofilefield/social/services/handlers/social.ts @@ -15,7 +15,6 @@ import { Injectable, Type } from '@angular/core'; import { CoreUserProfileFieldHandler, CoreUserProfileFieldHandlerData } from '@features/user/services/user-profile-field-delegate'; -import { AddonUserProfileFieldSocialComponent } from '../../component/social'; import { CoreText } from '@singletons/text'; import { AuthEmailSignupProfileField } from '@features/login/services/login-helper'; import { CoreUserProfileField } from '@features/user/services/user'; @@ -65,12 +64,11 @@ export class AddonUserProfileFieldSocialHandlerService implements CoreUserProfil } /** - * Return the Component to use to display the user profile field. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @returns The component (or promise resolved with component) to use, undefined if not found. + * @inheritdoc */ - getComponent(): Type | Promise> { + async getComponent(): Promise> { + const { AddonUserProfileFieldSocialComponent } = await import('../../component/social'); + return AddonUserProfileFieldSocialComponent; } diff --git a/src/addons/userprofilefield/text/component/text.ts b/src/addons/userprofilefield/text/component/text.ts index 00b8c487c1c..6dbee965778 100644 --- a/src/addons/userprofilefield/text/component/text.ts +++ b/src/addons/userprofilefield/text/component/text.ts @@ -16,7 +16,7 @@ import { Component } from '@angular/core'; import { AuthEmailSignupProfileField } from '@features/login/services/login-helper'; import { CoreUserProfileFieldBaseComponent } from '@features/user/classes/base-profilefield-component'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; /** * Directive to render a text user profile field. diff --git a/src/addons/userprofilefield/text/services/handlers/text.ts b/src/addons/userprofilefield/text/services/handlers/text.ts index 15e94f1d73a..1543ade400f 100644 --- a/src/addons/userprofilefield/text/services/handlers/text.ts +++ b/src/addons/userprofilefield/text/services/handlers/text.ts @@ -15,7 +15,6 @@ import { Injectable, Type } from '@angular/core'; import { CoreUserProfileFieldHandler, CoreUserProfileFieldHandlerData } from '@features/user/services/user-profile-field-delegate'; -import { AddonUserProfileFieldTextComponent } from '../../component/text'; import { CoreText } from '@singletons/text'; import { AuthEmailSignupProfileField } from '@features/login/services/login-helper'; import { CoreUserProfileField } from '@features/user/services/user'; @@ -65,12 +64,11 @@ export class AddonUserProfileFieldTextHandlerService implements CoreUserProfileF } /** - * Return the Component to use to display the user profile field. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @returns The component (or promise resolved with component) to use, undefined if not found. + * @inheritdoc */ - getComponent(): Type | Promise> { + async getComponent(): Promise> { + const { AddonUserProfileFieldTextComponent } = await import('../../component/text'); + return AddonUserProfileFieldTextComponent; } diff --git a/src/addons/userprofilefield/textarea/services/handlers/textarea.ts b/src/addons/userprofilefield/textarea/services/handlers/textarea.ts index 98a1fb2e790..d16b211d8f4 100644 --- a/src/addons/userprofilefield/textarea/services/handlers/textarea.ts +++ b/src/addons/userprofilefield/textarea/services/handlers/textarea.ts @@ -15,7 +15,6 @@ import { Injectable, Type } from '@angular/core'; import { CoreUserProfileFieldHandler, CoreUserProfileFieldHandlerData } from '@features/user/services/user-profile-field-delegate'; -import { AddonUserProfileFieldTextareaComponent } from '../../component/textarea'; import { CoreText } from '@singletons/text'; import { AuthEmailSignupProfileField } from '@features/login/services/login-helper'; import { CoreUserProfileField } from '@features/user/services/user'; @@ -74,12 +73,11 @@ export class AddonUserProfileFieldTextareaHandlerService implements CoreUserProf } /** - * Return the Component to use to display the user profile field. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @returns The component (or promise resolved with component) to use, undefined if not found. + * @inheritdoc */ - getComponent(): Type | Promise> { + async getComponent(): Promise> { + const { AddonUserProfileFieldTextareaComponent } = await import('../../component/textarea'); + return AddonUserProfileFieldTextareaComponent; } diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index d4b603e63d1..53704f5888f 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { InjectionToken, Injector, ModuleWithProviders, NgModule, Type } from '@angular/core'; +import { InjectionToken, Injector, ModuleWithProviders, NgModule, NgModuleFactory, Type } from '@angular/core'; import { RouterModule, Route, @@ -22,7 +22,9 @@ import { UrlMatchResult, UrlSegment, UrlSegmentGroup, + DefaultExport, } from '@angular/router'; +import { Observable } from 'rxjs'; const modulesRoutes: WeakMap, ModuleRoutes> = new WeakMap(); @@ -97,10 +99,18 @@ function buildConditionalUrlMatcher(pathOrMatcher: string | UrlMatcher, conditio } /** - * Type to declare lazy route modules. + * Type to declare lazy route modules. Extracted from Angular's LoadChildrenCallback type. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any -export type LazyRoutesModule = Type; +export type LazyRoutesModule = Type | + NgModuleFactory | // eslint-disable-line deprecation/deprecation, @typescript-eslint/no-explicit-any + Routes | + Observable | // eslint-disable-line @typescript-eslint/no-explicit-any + Routes | + DefaultExport> | // eslint-disable-line @typescript-eslint/no-explicit-any + DefaultExport> | + // eslint-disable-next-line deprecation/deprecation, @typescript-eslint/no-explicit-any + Promise | Type | Routes | DefaultExport> |DefaultExport>; /** * Build url matcher using a regular expression. diff --git a/src/app/app.component.ts b/src/app/app.component.ts index e821baaeca2..f3d2641444e 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -22,12 +22,12 @@ import { CoreApp } from '@services/app'; import { CoreNavigator } from '@services/navigator'; import { CoreSubscriptions } from '@singletons/subscriptions'; import { CoreWindow } from '@singletons/window'; -import { CoreUtils } from '@services/utils/utils'; import { CorePlatform } from '@services/platform'; import { CoreLogger } from '@singletons/logger'; import { CorePromisedValue } from '@classes/promised-value'; import { register } from 'swiper/element/bundle'; import { CoreWait } from '@singletons/wait'; +import { CoreOpener } from '@singletons/opener'; register(); @@ -51,7 +51,7 @@ export class AppComponent implements OnInit, AfterViewInit { CorePlatform.resume.subscribe(() => { // Wait a second before setting it to false since in iOS there could be some frozen WS calls. setTimeout(() => { - if (CoreLoginHelper.isWaitingForBrowser() && !CoreUtils.isInAppBrowserOpen()) { + if (CoreLoginHelper.isWaitingForBrowser() && !CoreOpener.isInAppBrowserOpen()) { CoreLoginHelper.stopWaitingForBrowser(); CoreLoginHelper.checkLogout(); } diff --git a/src/core/classes/element-controllers/MediaElementController.ts b/src/core/classes/element-controllers/MediaElementController.ts index 3a67a6cebf3..b4b6682ad2c 100644 --- a/src/core/classes/element-controllers/MediaElementController.ts +++ b/src/core/classes/element-controllers/MediaElementController.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { CoreUtils } from '@services/utils/utils'; +import { CoreErrorHelper } from '@services/error-helper'; import { ElementController } from './ElementController'; import { CorePromisedValue } from '@classes/promised-value'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; @@ -70,7 +70,7 @@ export class MediaElementController extends ElementController { this.addPlaybackEventListeners(jsPlayer); } catch (error) { - CoreUtils.logUnhandledError('Error enabling media element', error); + CoreErrorHelper.logUnhandledError('Error enabling media element', error); } } diff --git a/src/core/classes/errors/wserror.ts b/src/core/classes/errors/wserror.ts index f34ac058720..c35d11b9887 100644 --- a/src/core/classes/errors/wserror.ts +++ b/src/core/classes/errors/wserror.ts @@ -39,6 +39,39 @@ export class CoreWSError extends CoreError { this.backtrace = error.backtrace; } + /** + * Given an error returned by a WS call, check if the error is generated by the app or it has been returned by the WebService. + * + * @param error Error to check. + * @returns Whether the error was returned by the WebService. + */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + static isWebServiceError(error: any): boolean { + return !!error && ( + error.warningcode !== undefined || + ( + error.errorcode !== undefined && error.errorcode != 'userdeleted' && error.errorcode != 'upgraderunning' && + error.errorcode != 'forcepasswordchangenotice' && error.errorcode != 'usernotfullysetup' && + error.errorcode != 'sitepolicynotagreed' && error.errorcode != 'sitemaintenance' && + error.errorcode != 'wsaccessusersuspended' && error.errorcode != 'wsaccessuserdeleted' && + !this.isExpiredTokenError(error) + ) || + !!error.status && error.status >= 400 // CoreHttpError, assume status 400 and above are like WebService errors. + ); + } + + /** + * Given an error returned by a WS call, check if the error is a token expired error. + * + * @param error Error to check. + * @returns Whether the error is a token expired error. + */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + static isExpiredTokenError(error: any): boolean { + return error.errorcode === 'invalidtoken' || + (error.errorcode === 'accessexception' && error.message.includes('Invalid token - token expired')); + } + } type CoreWSErrorData = { diff --git a/src/core/classes/interceptor.ts b/src/core/classes/interceptor.ts index 0768dfeefe3..08b4d9ea5de 100644 --- a/src/core/classes/interceptor.ts +++ b/src/core/classes/interceptor.ts @@ -66,7 +66,7 @@ export class CoreInterceptor implements HttpInterceptor { // Add the header and serialize the body if needed. const newReq = req.clone({ headers: req.headers.set('Content-Type', 'application/x-www-form-urlencoded'), - body: typeof req.body == 'object' && String(req.body) != '[object File]' ? + body: typeof req.body === 'object' && String(req.body) != '[object File]' ? CoreInterceptor.serialize(req.body) : req.body, }); diff --git a/src/core/classes/items-management/list-items-manager.ts b/src/core/classes/items-management/list-items-manager.ts index 4761374d7e7..beb8594c490 100644 --- a/src/core/classes/items-management/list-items-manager.ts +++ b/src/core/classes/items-management/list-items-manager.ts @@ -18,12 +18,13 @@ import { Subscription } from 'rxjs'; import { CoreSplitViewComponent } from '@components/split-view/split-view'; import { CoreNavigator } from '@services/navigator'; import { CoreScreen } from '@services/screen'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { CoreRoutedItemsManagerSource } from './routed-items-manager-source'; import { CoreRoutedItemsManager } from './routed-items-manager'; import { CoreDom } from '@singletons/dom'; import { CoreTime } from '@singletons/time'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Helper class to manage the state and routing of a list of items in a page. @@ -45,7 +46,7 @@ export class CoreListItemsManager< this.pageRouteLocator = pageRouteLocator; this.addListener({ onSelectedItemUpdated: debouncedScrollToCurrentElement }); - this.finishSuccessfulFetch = CoreTime.once(() => CoreUtils.ignoreErrors(this.logActivity())); + this.finishSuccessfulFetch = CoreTime.once(() => CorePromiseUtils.ignoreErrors(this.logActivity())); } get items(): Item[] { diff --git a/src/core/classes/page-load-watcher.ts b/src/core/classes/page-load-watcher.ts index 2bc2ef7aaeb..29671b547cf 100644 --- a/src/core/classes/page-load-watcher.ts +++ b/src/core/classes/page-load-watcher.ts @@ -13,7 +13,7 @@ // limitations under the License. import { CoreSitesReadingStrategy } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { Subscription } from 'rxjs'; import { AsyncDirective } from './async-directive'; import { PageLoadsManager } from './page-loads-manager'; @@ -130,7 +130,7 @@ export class PageLoadWatcher { return; } - this.hasChangesPromises.push(CoreUtils.ignoreErrors(hasMeaningfulChanges(firstValue, value), false)); + this.hasChangesPromises.push(CorePromiseUtils.ignoreErrors(hasMeaningfulChanges(firstValue, value), false)); }, error: (error) => { promisedValue.reject(error); diff --git a/src/core/classes/sites/authenticated-site.ts b/src/core/classes/sites/authenticated-site.ts index 422aa755178..3d60d14f2bc 100644 --- a/src/core/classes/sites/authenticated-site.ts +++ b/src/core/classes/sites/authenticated-site.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { CoreApp } from '@services/app'; +import { CoreSSO } from '@singletons/sso'; import { CoreNetwork } from '@services/network'; import { CoreEventData, CoreEvents } from '@singletons/events'; import { @@ -23,8 +23,8 @@ import { } from '@services/ws'; import { CoreToasts, ToastDuration } from '@services/toasts'; import { CoreText } from '@singletons/text'; -import { CoreUtils } from '@services/utils/utils'; -import { CoreConstants } from '@/core/constants'; +import { CoreUtils } from '@singletons/utils'; +import { CoreCacheUpdateFrequency, CoreConstants, MINIMUM_MOODLE_VERSION, MOODLE_RELEASES } from '@/core/constants'; import { CoreError } from '@classes/errors/error'; import { CoreWSError } from '@classes/errors/wserror'; import { CoreLogger } from '@singletons/logger'; @@ -42,6 +42,9 @@ import { Md5 } from 'ts-md5'; import { CoreSiteWSCacheRecord } from '@services/database/sites'; import { CoreErrorLogs } from '@singletons/error-logs'; import { CoreWait } from '@singletons/wait'; +import { CorePromiseUtils } from '@singletons/promise-utils'; +import { CoreObject } from '@singletons/object'; +import { CoreArray } from '@singletons/array'; /** * Class that represents a site (combination of site + user) where the user has authenticated but the site hasn't been validated @@ -51,31 +54,34 @@ export class CoreAuthenticatedSite extends CoreUnauthenticatedSite { static readonly REQUEST_QUEUE_FORCE_WS = false; // Use "tool_mobile_call_external_functions" even for calling a single function. - // Constants for cache update frequency. - static readonly FREQUENCY_USUALLY = 0; - static readonly FREQUENCY_OFTEN = 1; - static readonly FREQUENCY_SOMETIMES = 2; - static readonly FREQUENCY_RARELY = 3; - - static readonly MINIMUM_MOODLE_VERSION = '3.5'; - - // Versions of Moodle releases. - static readonly MOODLE_RELEASES = { - '3.5': 2018051700, - '3.6': 2018120300, - '3.7': 2019052000, - '3.8': 2019111800, - '3.9': 2020061500, - '3.10': 2020110900, - '3.11': 2021051700, - '4.0': 2022041900, - '4.1': 2022112800, - '4.2': 2023042400, - '4.3': 2023100900, - '4.4': 2024042200, - '4.5': 2024100700, - '5.0': 2024100800, // @todo [5.0] replace with right value when released. Using a tmp value to be able to test new things. - }; + /** + * @deprecated 5.0. Use CoreCacheUpdateFrequency.USUALLY instead. + */ + static readonly FREQUENCY_USUALLY = CoreCacheUpdateFrequency.USUALLY; + /** + * @deprecated 5.0. Use CoreCacheUpdateFrequency.OFTEN instead. + */ + static readonly FREQUENCY_OFTEN = CoreCacheUpdateFrequency.OFTEN; + /** + * @deprecated 5.0. Use CoreCacheUpdateFrequency.SOMETIMES instead. + */ + static readonly FREQUENCY_SOMETIMES = CoreCacheUpdateFrequency.SOMETIMES; + /** + * @deprecated 5.0. Use CoreCacheUpdateFrequency.RARELY instead. + */ + static readonly FREQUENCY_RARELY = CoreCacheUpdateFrequency.RARELY; + + /** + * @deprecated 5.0. Use MINIMUM_MOODLE_VERSION from constants.ts. + */ + static readonly MINIMUM_MOODLE_VERSION = MINIMUM_MOODLE_VERSION; + + /** + * Versions of Moodle releases. + * + * @deprecated 5.0. Use MOODLE_RELEASES from constants.ts. + */ + static readonly MOODLE_RELEASES = MOODLE_RELEASES; // Possible cache update frequencies. protected static readonly UPDATE_FREQUENCIES = [ @@ -208,7 +214,7 @@ export class CoreAuthenticatedSite extends CoreUnauthenticatedSite { // Index function by name to speed up wsAvailable method. if (infos?.functions) { - infos.functionsByName = CoreUtils.arrayToObject(infos.functions, 'name'); + infos.functionsByName = CoreArray.toObject(infos.functions, 'name'); } } @@ -638,7 +644,7 @@ export class CoreAuthenticatedSite extends CoreUnauthenticatedSite { } catch (error) { let useSilentError = false; - if (CoreUtils.isExpiredTokenError(error)) { + if (CoreWSError.isExpiredTokenError(error)) { // Session expired, trigger event. this.triggerSiteEvent(CoreEvents.SESSION_EXPIRED, {}); // Change error message. Try to get data from cache, the event will handle the error. @@ -715,9 +721,9 @@ export class CoreAuthenticatedSite extends CoreUnauthenticatedSite { throw new CoreWSError(error); } - if (preSets.deleteCacheIfWSError && CoreUtils.isWebServiceError(error)) { + if (preSets.deleteCacheIfWSError && CoreWSError.isWebServiceError(error)) { // Delete the cache entry and return the entry. Don't block the user with the delete. - CoreUtils.ignoreErrors(this.deleteFromCache(method, data, preSets)); + CorePromiseUtils.ignoreErrors(this.deleteFromCache(method, data, preSets)); throw new CoreWSError(error); } @@ -781,15 +787,15 @@ export class CoreAuthenticatedSite extends CoreUnauthenticatedSite { try { return await this.callOrEnqueueRequest(method, data, preSets, wsPreSets); } catch (error) { - if (CoreUtils.isExpiredTokenError(error)) { + if (CoreWSError.isExpiredTokenError(error)) { if (initialToken !== this.token) { // Token has changed, retry with the new token. wsPreSets.wsToken = this.token ?? ''; return await this.callOrEnqueueRequest(method, data, preSets, wsPreSets); - } else if (CoreApp.isSSOAuthenticationOngoing()) { + } else if (CoreSSO.isSSOAuthenticationOngoing()) { // There's an SSO authentication ongoing, wait for it to finish and try again. - await CoreApp.waitForSSOAuthentication(); + await CoreSSO.waitForSSOAuthentication(); return await this.callOrEnqueueRequest(method, data, preSets, wsPreSets); } @@ -1016,7 +1022,7 @@ export class CoreAuthenticatedSite extends CoreUnauthenticatedSite { */ // eslint-disable-next-line @typescript-eslint/no-explicit-any protected getCacheId(method: string, data: any): string { - return Md5.hashAsciiStr(method + ':' + CoreUtils.sortAndStringify(data)); + return Md5.hashAsciiStr(method + ':' + CoreObject.sortAndStringify(data)); } /** @@ -1145,7 +1151,7 @@ export class CoreAuthenticatedSite extends CoreUnauthenticatedSite { protected async saveToCache(method: string, data: any, response: any, preSets: CoreSiteWSPreSets): Promise { if (preSets.uniqueCacheKey) { // Cache key must be unique, delete all entries with same cache key. - await CoreUtils.ignoreErrors(this.deleteFromCache(method, data, preSets, true)); + await CorePromiseUtils.ignoreErrors(this.deleteFromCache(method, data, preSets, true)); } // Since 3.7, the expiration time contains the time the entry is modified instead of the expiration time. @@ -1484,9 +1490,9 @@ export class CoreAuthenticatedSite extends CoreUnauthenticatedSite { return 0; } - if (CoreAuthenticatedSite.MOODLE_RELEASES[data.major] === undefined) { + if (MOODLE_RELEASES[data.major] === undefined) { // Major version not found. Use the last one. - const major = Object.keys(CoreAuthenticatedSite.MOODLE_RELEASES).pop(); + const major = Object.keys(MOODLE_RELEASES).pop(); if (!major) { return 0; } @@ -1494,7 +1500,7 @@ export class CoreAuthenticatedSite extends CoreUnauthenticatedSite { data.major = major; } - return CoreAuthenticatedSite.MOODLE_RELEASES[data.major] + data.minor; + return MOODLE_RELEASES[data.major] + data.minor; } /** @@ -1524,7 +1530,7 @@ export class CoreAuthenticatedSite extends CoreUnauthenticatedSite { */ protected getNextMajorVersionNumber(version: string): number { const data = this.getMajorAndMinor(version); - const releases = Object.keys(CoreAuthenticatedSite.MOODLE_RELEASES); + const releases = Object.keys(MOODLE_RELEASES); if (!data) { // Invalid version. @@ -1535,10 +1541,10 @@ export class CoreAuthenticatedSite extends CoreUnauthenticatedSite { if (position == -1 || position == releases.length - 1) { // Major version not found or it's the last one. Use the last one. - return CoreAuthenticatedSite.MOODLE_RELEASES[releases[position]]; + return MOODLE_RELEASES[releases[position]]; } - return CoreAuthenticatedSite.MOODLE_RELEASES[releases[position + 1]]; + return MOODLE_RELEASES[releases[position + 1]]; } /** @@ -1547,10 +1553,10 @@ export class CoreAuthenticatedSite extends CoreUnauthenticatedSite { * @param updateFrequency The update frequency of the entry. * @returns Expiration delay. */ - getExpirationDelay(updateFrequency?: number): number { - updateFrequency = updateFrequency || CoreAuthenticatedSite.FREQUENCY_USUALLY; + getExpirationDelay(updateFrequency?: CoreCacheUpdateFrequency): number { + updateFrequency = updateFrequency ?? CoreCacheUpdateFrequency.USUALLY; let expirationDelay = CoreAuthenticatedSite.UPDATE_FREQUENCIES[updateFrequency] || - CoreAuthenticatedSite.UPDATE_FREQUENCIES[CoreAuthenticatedSite.FREQUENCY_USUALLY]; + CoreAuthenticatedSite.UPDATE_FREQUENCIES[CoreCacheUpdateFrequency.USUALLY]; if (CoreNetwork.isNetworkAccessLimited()) { // Not WiFi, increase the expiration delay a 50% to decrease the data usage in this case. @@ -1744,10 +1750,10 @@ export type CoreSiteWSPreSets = { /** * Update frequency. This value determines how often the cached data will be updated. Possible values: - * CoreSite.FREQUENCY_USUALLY, CoreSite.FREQUENCY_OFTEN, CoreSite.FREQUENCY_SOMETIMES, CoreSite.FREQUENCY_RARELY. - * Defaults to CoreSite.FREQUENCY_USUALLY. + * USUALLY, OFTEN, SOMETIMES, RARELY. + * Defaults to USUALLY. */ - updateFrequency?: number; + updateFrequency?: CoreCacheUpdateFrequency; /** * Component name. Optionally included if this request is being made on behalf of a specific diff --git a/src/core/classes/sites/site.ts b/src/core/classes/sites/site.ts index 13863cc4120..2d386468f75 100644 --- a/src/core/classes/sites/site.ts +++ b/src/core/classes/sites/site.ts @@ -27,7 +27,7 @@ import { import { CoreDomUtils } from '@services/utils/dom'; import { CoreTimeUtils } from '@services/utils/time'; import { CoreUrl } from '@singletons/url'; -import { CoreUtils, CoreUtilsOpenInBrowserOptions } from '@services/utils/utils'; +import { CoreOpener, CoreOpenerOpenInBrowserOptions } from '@singletons/opener'; import { CoreConstants } from '@/core/constants'; import { SQLiteDB } from '@classes/sqlitedb'; import { CoreError } from '@classes/errors/error'; @@ -55,6 +55,7 @@ import { CoreAuthenticatedSite, CoreAuthenticatedSiteOptionalData, CoreSiteWSPre import { firstValueFrom } from 'rxjs'; import { CorePlatform } from '@services/platform'; import { CoreLoadings } from '@services/loadings'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Class that represents a site (combination of site + user). @@ -405,7 +406,7 @@ export class CoreSite extends CoreAuthenticatedSite { const siteFolder = CoreFile.getSiteFolder(this.id); // Ignore any errors, removeDir fails if folder doesn't exists. - await CoreUtils.ignoreErrors(CoreFile.removeDir(siteFolder)); + await CorePromiseUtils.ignoreErrors(CoreFile.removeDir(siteFolder)); } /** @@ -413,14 +414,14 @@ export class CoreSite extends CoreAuthenticatedSite { * * @returns Promise resolved with the site space usage (size). */ - getSpaceUsage(): Promise { + async getSpaceUsage(): Promise { if (CoreFile.isAvailable() && this.id) { const siteFolderPath = CoreFile.getSiteFolder(this.id); return CoreFile.getDirectorySize(siteFolderPath).catch(() => 0); - } else { - return Promise.resolve(0); } + + return 0; } /** @@ -470,7 +471,7 @@ export class CoreSite extends CoreAuthenticatedSite { async openInBrowserWithAutoLogin( url: string, alertMessage?: string, - options: CoreUtilsOpenInBrowserOptions = {}, + options: CoreOpenerOpenInBrowserOptions = {}, ): Promise { await this.openWithAutoLogin(false, url, options, alertMessage); } @@ -501,7 +502,7 @@ export class CoreSite extends CoreAuthenticatedSite { async openWithAutoLogin( inApp: boolean, url: string, - options: InAppBrowserOptions & CoreUtilsOpenInBrowserOptions = {}, + options: InAppBrowserOptions & CoreOpenerOpenInBrowserOptions = {}, alertMessage?: string, ): Promise { // Get the URL to open. @@ -535,9 +536,9 @@ export class CoreSite extends CoreAuthenticatedSite { options.clearsessioncache = 'yes'; } - return CoreUtils.openInApp(autoLoginUrl, options); + return CoreOpener.openInApp(autoLoginUrl, options); } else { - return CoreUtils.openInBrowser(autoLoginUrl, options); + return CoreOpener.openInBrowser(autoLoginUrl, options); } } @@ -884,7 +885,7 @@ export class CoreSite extends CoreAuthenticatedSite { * @returns Time between requests. */ async getAutoLoginMinTimeBetweenRequests(): Promise { - const timeBetweenRequests = await CoreUtils.ignoreErrors( + const timeBetweenRequests = await CorePromiseUtils.ignoreErrors( this.getConfig('tool_mobile_autologinmintimebetweenreq'), CoreConstants.SECONDS_MINUTE * 6, ); diff --git a/src/core/components/attachments/attachments.ts b/src/core/components/attachments/attachments.ts index 54895fa5588..1996d62ec99 100644 --- a/src/core/components/attachments/attachments.ts +++ b/src/core/components/attachments/attachments.ts @@ -24,7 +24,7 @@ import { CoreDomUtils } from '@services/utils/dom'; import { CoreFileUploaderHelper } from '@features/fileuploader/services/fileuploader-helper'; import { CoreFileEntry } from '@services/file-helper'; import { CoreCourses } from '@features/courses/services/courses'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { toBoolean } from '@/core/transforms/boolean'; /** @@ -43,7 +43,7 @@ import { toBoolean } from '@/core/transforms/boolean'; @Component({ selector: 'core-attachments', templateUrl: 'core-attachments.html', - styleUrls: ['attachments.scss'], + styleUrl: 'attachments.scss', }) export class CoreAttachmentsComponent implements OnInit { @@ -106,7 +106,7 @@ export class CoreAttachmentsComponent implements OnInit { protected async getMaxSizeOfArea(): Promise { if (this.courseId) { // Check course max size. - const course = await CoreUtils.ignoreErrors(CoreCourses.getCourseByField('id', this.courseId)); + const course = await CorePromiseUtils.ignoreErrors(CoreCourses.getCourseByField('id', this.courseId)); if (course?.maxbytes) { this.maxSize = course.maxbytes; diff --git a/src/core/components/button-with-spinner/button-with-spinner.ts b/src/core/components/button-with-spinner/button-with-spinner.ts index ab6b611e782..8187042325a 100644 --- a/src/core/components/button-with-spinner/button-with-spinner.ts +++ b/src/core/components/button-with-spinner/button-with-spinner.ts @@ -27,7 +27,7 @@ import { CoreAnimations } from '@components/animations'; @Component({ selector: 'core-button-with-spinner', templateUrl: 'core-button-with-spinner.html', - styleUrls: ['button-with-spinner.scss'], + styleUrl: 'button-with-spinner.scss', animations: [CoreAnimations.SHOW_HIDE], }) export class CoreButtonWithSpinnerComponent { diff --git a/src/core/components/chart/chart.ts b/src/core/components/chart/chart.ts index 5f5affbd38b..e71cd860019 100644 --- a/src/core/components/chart/chart.ts +++ b/src/core/components/chart/chart.ts @@ -30,7 +30,7 @@ import { ChartLegendLabelItem, ChartLegendOptions } from 'chart.js'; @Component({ selector: 'core-chart', templateUrl: 'core-chart.html', - styleUrls: ['chart.scss'], + styleUrl: 'chart.scss', }) export class CoreChartComponent implements OnDestroy, OnInit, OnChanges { diff --git a/src/core/components/combobox/combobox.ts b/src/core/components/combobox/combobox.ts index 51a8ee8a339..8602dec8d50 100644 --- a/src/core/components/combobox/combobox.ts +++ b/src/core/components/combobox/combobox.ts @@ -40,7 +40,7 @@ import { toBoolean } from '@/core/transforms/boolean'; @Component({ selector: 'core-combobox', templateUrl: 'core-combobox.html', - styleUrls: ['combobox.scss'], + styleUrl: 'combobox.scss', providers: [ { provide: NG_VALUE_ACCESSOR, diff --git a/src/core/components/context-menu/context-menu.ts b/src/core/components/context-menu/context-menu.ts index 9d3f03900fa..44f1d470e74 100644 --- a/src/core/components/context-menu/context-menu.ts +++ b/src/core/components/context-menu/context-menu.ts @@ -16,7 +16,7 @@ import { Component, Input, OnInit, OnDestroy, ElementRef, ChangeDetectorRef } fr import { Subject, Subscription } from 'rxjs'; import { auditTime } from 'rxjs/operators'; import { CorePopovers } from '@services/popovers'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { Translate } from '@singletons'; import { CoreContextMenuItemComponent } from './context-menu-item'; import { CoreDirectivesRegistry } from '@singletons/directives-registry'; diff --git a/src/core/components/course-image/course-image.ts b/src/core/components/course-image/course-image.ts index a6b55987399..d8459c8c069 100644 --- a/src/core/components/course-image/course-image.ts +++ b/src/core/components/course-image/course-image.ts @@ -21,7 +21,7 @@ import { CoreColors } from '@singletons/colors'; @Component({ selector: 'core-course-image', templateUrl: 'course-image.html', - styleUrls: ['./course-image.scss'], + styleUrl: './course-image.scss', }) export class CoreCourseImageComponent { diff --git a/src/core/components/download-refresh/download-refresh.ts b/src/core/components/download-refresh/download-refresh.ts index a4f491726b9..9093b127265 100644 --- a/src/core/components/download-refresh/download-refresh.ts +++ b/src/core/components/download-refresh/download-refresh.ts @@ -27,7 +27,7 @@ import { toBoolean } from '@/core/transforms/boolean'; @Component({ selector: 'core-download-refresh', templateUrl: 'core-download-refresh.html', - styleUrls: ['download-refresh.scss'], + styleUrl: 'download-refresh.scss', animations: [CoreAnimations.SHOW_HIDE], }) export class CoreDownloadRefreshComponent implements OnInit { diff --git a/src/core/components/file/file.ts b/src/core/components/file/file.ts index 574a138e2ef..260fe3c88e7 100644 --- a/src/core/components/file/file.ts +++ b/src/core/components/file/file.ts @@ -21,13 +21,14 @@ import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreMimetypeUtils } from '@services/utils/mimetype'; import { CoreUrl } from '@singletons/url'; -import { CoreUtils, CoreUtilsOpenFileOptions, OpenFileAction } from '@services/utils/utils'; import { CoreText } from '@singletons/text'; import { DownloadStatus } from '@/core/constants'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreWSFile } from '@services/ws'; import { CorePlatform } from '@services/platform'; import { toBoolean } from '@/core/transforms/boolean'; +import { CorePromiseUtils } from '@singletons/promise-utils'; +import { CoreOpener, CoreOpenerOpenFileOptions, OpenFileAction } from '@singletons/opener'; /** * Component to handle a remote file. Shows the file name, icon (depending on mimetype) and a button @@ -154,7 +155,7 @@ export class CoreFileComponent implements OnInit, OnDestroy { return; } - const options: CoreUtilsOpenFileOptions = {}; + const options: CoreOpenerOpenFileOptions = {}; if (isOpenButton) { // Use the non-default method. options.iOSOpenFileAction = this.defaultIsOpenWithPicker ? OpenFileAction.OPEN : OpenFileAction.OPEN_WITH; @@ -193,9 +194,9 @@ export class CoreFileComponent implements OnInit, OnDestroy { if (!this.canDownload || !this.state || this.state === DownloadStatus.NOT_DOWNLOADABLE) { // File cannot be downloaded, just open it. if (CoreUrl.isLocalFileUrl(this.fileUrl)) { - CoreUtils.openFile(this.fileUrl); + CoreOpener.openFile(this.fileUrl); } else { - CoreUtils.openOnlineFile(CoreUrl.unfixPluginfileURL(this.fileUrl)); + CoreOpener.openOnlineFile(CoreUrl.unfixPluginfileURL(this.fileUrl)); } return; @@ -226,7 +227,7 @@ export class CoreFileComponent implements OnInit, OnDestroy { // User confirmed, add the file to queue. // @todo Is the invalidate really needed? - await CoreUtils.ignoreErrors(CoreFilepool.invalidateFileByUrl(this.siteId, this.fileUrl)); + await CorePromiseUtils.ignoreErrors(CoreFilepool.invalidateFileByUrl(this.siteId, this.fileUrl)); this.isDownloading = true; diff --git a/src/core/components/horizontal-scroll-controls/horizontal-scroll-controls.ts b/src/core/components/horizontal-scroll-controls/horizontal-scroll-controls.ts index e0ea431e05f..7c94cfda2b1 100644 --- a/src/core/components/horizontal-scroll-controls/horizontal-scroll-controls.ts +++ b/src/core/components/horizontal-scroll-controls/horizontal-scroll-controls.ts @@ -25,7 +25,7 @@ const enum ScrollPosition { @Component({ selector: 'core-horizontal-scroll-controls', templateUrl: 'core-horizontal-scroll-controls.html', - styleUrls: ['./horizontal-scroll-controls.scss'], + styleUrl: './horizontal-scroll-controls.scss', }) export class CoreHorizontalScrollControlsComponent { diff --git a/src/core/components/iframe/iframe.ts b/src/core/components/iframe/iframe.ts index 1fca966a9b7..af12e883bb8 100644 --- a/src/core/components/iframe/iframe.ts +++ b/src/core/components/iframe/iframe.ts @@ -33,7 +33,7 @@ import { toBoolean } from '@/core/transforms/boolean'; @Component({ selector: 'core-iframe', templateUrl: 'core-iframe.html', - styleUrls: ['iframe.scss'], + styleUrl: 'iframe.scss', }) export class CoreIframeComponent implements OnChanges, OnDestroy { diff --git a/src/core/components/input-errors/input-errors.ts b/src/core/components/input-errors/input-errors.ts index 61cb6b7916d..df2c2eb7fae 100644 --- a/src/core/components/input-errors/input-errors.ts +++ b/src/core/components/input-errors/input-errors.ts @@ -36,7 +36,7 @@ import { FormControl } from '@angular/forms'; @Component({ selector: 'core-input-errors', templateUrl: 'core-input-errors.html', - styleUrls: ['input-errors.scss'], + styleUrl: 'input-errors.scss', }) export class CoreInputErrorsComponent implements OnInit, OnChanges { diff --git a/src/core/components/loading/loading.ts b/src/core/components/loading/loading.ts index bba7bb8730e..fc38bea48f0 100644 --- a/src/core/components/loading/loading.ts +++ b/src/core/components/loading/loading.ts @@ -14,7 +14,7 @@ import { Component, Input, OnInit, OnChanges, SimpleChange, ElementRef, AfterViewInit, OnDestroy } from '@angular/core'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { CoreAnimations } from '@components/animations'; import { Translate } from '@singletons'; import { CoreDirectivesRegistry } from '@singletons/directives-registry'; @@ -47,7 +47,7 @@ import { toBoolean } from '@/core/transforms/boolean'; @Component({ selector: 'core-loading', templateUrl: 'core-loading.html', - styleUrls: ['loading.scss'], + styleUrl: 'loading.scss', animations: [CoreAnimations.SHOW_HIDE], }) export class CoreLoadingComponent implements OnInit, OnChanges, AfterViewInit, AsyncDirective, OnDestroy { diff --git a/src/core/components/local-file/local-file.ts b/src/core/components/local-file/local-file.ts index 89cc829fea8..e05d3865e6a 100644 --- a/src/core/components/local-file/local-file.ts +++ b/src/core/components/local-file/local-file.ts @@ -23,12 +23,13 @@ import { CoreDomUtils } from '@services/utils/dom'; import { CoreMimetypeUtils } from '@services/utils/mimetype'; import { CoreText } from '@singletons/text'; import { CoreTimeUtils } from '@services/utils/time'; -import { CoreUtils, CoreUtilsOpenFileOptions, OpenFileAction } from '@services/utils/utils'; +import { CoreOpener, CoreOpenerOpenFileOptions, OpenFileAction } from '@singletons/opener'; import { CoreForms } from '@singletons/form'; import { CorePath } from '@singletons/path'; import { CorePlatform } from '@services/platform'; import { toBoolean } from '@/core/transforms/boolean'; import { CoreLoadings } from '@services/loadings'; +import { CoreFileUtils } from '@singletons/file-utils'; /** * Component to handle a local file. Only files inside the app folder can be managed. @@ -133,13 +134,13 @@ export class CoreLocalFileComponent implements OnInit { } } - const options: CoreUtilsOpenFileOptions = {}; + const options: CoreOpenerOpenFileOptions = {}; if (isOpenButton) { // Use the non-default method. options.iOSOpenFileAction = this.defaultIsOpenWithPicker ? OpenFileAction.OPEN : OpenFileAction.OPEN_WITH; } - CoreUtils.openFile(CoreFile.getFileEntryURL(this.file), options); + CoreOpener.openFile(CoreFile.getFileEntryURL(this.file), options); } /** @@ -182,7 +183,7 @@ export class CoreLocalFileComponent implements OnInit { } const modal = await CoreLoadings.show(); - const fileAndDir = CoreFile.getFileAndDirectoryFromPath(this.relativePath); + const fileAndDir = CoreFileUtils.getFileAndDirectoryFromPath(this.relativePath); const newPath = CorePath.concatenatePaths(fileAndDir.directory, newName); try { diff --git a/src/core/components/mark-required/mark-required.ts b/src/core/components/mark-required/mark-required.ts index da40c2940bf..c271a5e41d6 100644 --- a/src/core/components/mark-required/mark-required.ts +++ b/src/core/components/mark-required/mark-required.ts @@ -31,7 +31,7 @@ import { Translate } from '@singletons'; @Component({ selector: '[core-mark-required]', templateUrl: 'core-mark-required.html', - styleUrls: ['mark-required.scss'], + styleUrl: 'mark-required.scss', }) export class CoreMarkRequiredComponent implements AfterViewInit { diff --git a/src/core/components/message/message.ts b/src/core/components/message/message.ts index aeb18b6ae82..613361250cb 100644 --- a/src/core/components/message/message.ts +++ b/src/core/components/message/message.ts @@ -26,7 +26,7 @@ import { toBoolean } from '@/core/transforms/boolean'; @Component({ selector: 'core-message', templateUrl: 'message.html', - styleUrls: ['message.scss'], + styleUrl: 'message.scss', animations: [CoreAnimations.SLIDE_IN_OUT], }) export class CoreMessageComponent implements OnInit { diff --git a/src/core/components/mod-icon/mod-icon.ts b/src/core/components/mod-icon/mod-icon.ts index 3c10b5eb248..d6cdd96a2f1 100644 --- a/src/core/components/mod-icon/mod-icon.ts +++ b/src/core/components/mod-icon/mod-icon.ts @@ -46,7 +46,7 @@ const enum IconVersion { @Component({ selector: 'core-mod-icon', templateUrl: 'mod-icon.html', - styleUrls: ['mod-icon.scss'], + styleUrl: 'mod-icon.scss', changeDetection: ChangeDetectionStrategy.OnPush, }) export class CoreModIconComponent implements OnInit, OnChanges { diff --git a/src/core/components/navbar-buttons/navbar-buttons.ts b/src/core/components/navbar-buttons/navbar-buttons.ts index 63173234747..e06a8843591 100644 --- a/src/core/components/navbar-buttons/navbar-buttons.ts +++ b/src/core/components/navbar-buttons/navbar-buttons.ts @@ -53,7 +53,7 @@ const BUTTON_HIDDEN_CLASS = 'core-navbar-button-hidden'; @Component({ selector: 'core-navbar-buttons', template: '', - styleUrls: ['navbar-buttons.scss'], + styleUrl: 'navbar-buttons.scss', }) export class CoreNavBarButtonsComponent implements OnInit, OnDestroy { diff --git a/src/core/components/navigation-bar/navigation-bar.ts b/src/core/components/navigation-bar/navigation-bar.ts index 5c1d5473793..9f033e68924 100644 --- a/src/core/components/navigation-bar/navigation-bar.ts +++ b/src/core/components/navigation-bar/navigation-bar.ts @@ -28,7 +28,7 @@ import { Translate } from '@singletons'; @Component({ selector: 'core-navigation-bar', templateUrl: 'core-navigation-bar.html', - styleUrls: ['navigation-bar.scss'], + styleUrl: 'navigation-bar.scss', }) export class CoreNavigationBarComponent implements OnChanges { diff --git a/src/core/components/progress-bar/progress-bar.ts b/src/core/components/progress-bar/progress-bar.ts index 1d30c770c08..970aa5885d0 100644 --- a/src/core/components/progress-bar/progress-bar.ts +++ b/src/core/components/progress-bar/progress-bar.ts @@ -25,7 +25,7 @@ import { DomSanitizer, Translate } from '@singletons'; @Component({ selector: 'core-progress-bar', templateUrl: 'core-progress-bar.html', - styleUrls: ['progress-bar.scss'], + styleUrl: 'progress-bar.scss', changeDetection: ChangeDetectionStrategy.OnPush, }) export class CoreProgressBarComponent implements OnChanges { diff --git a/src/core/components/recaptcha/recaptcha.ts b/src/core/components/recaptcha/recaptcha.ts index f37662f3c18..23428bb237e 100644 --- a/src/core/components/recaptcha/recaptcha.ts +++ b/src/core/components/recaptcha/recaptcha.ts @@ -17,7 +17,7 @@ import { Component, Input, OnInit } from '@angular/core'; import { CoreLang, CoreLangFormat } from '@services/lang'; import { CoreSites } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreOpener } from '@singletons/opener'; import { CorePath } from '@singletons/path'; /** @@ -66,7 +66,7 @@ export class CoreRecaptchaComponent implements OnInit { // The app cannot render the recaptcha directly because it has problems with the local protocols and domains. const src = CorePath.concatenatePaths(this.siteUrl, 'webservice/recaptcha.php?lang=' + this.lang); - const inAppBrowserWindow = CoreUtils.openInApp(src); + const inAppBrowserWindow = CoreOpener.openInApp(src); if (!inAppBrowserWindow) { return; } diff --git a/src/core/components/send-message-form/send-message-form.ts b/src/core/components/send-message-form/send-message-form.ts index 63e0cde3772..dd1cbde301a 100644 --- a/src/core/components/send-message-form/send-message-form.ts +++ b/src/core/components/send-message-form/send-message-form.ts @@ -35,7 +35,7 @@ import { toBoolean } from '@/core/transforms/boolean'; @Component({ selector: 'core-send-message-form', templateUrl: 'core-send-message-form.html', - styleUrls: ['send-message-form.scss'], + styleUrl: 'send-message-form.scss', }) export class CoreSendMessageFormComponent { diff --git a/src/core/components/sheet-modal/sheet-modal.ts b/src/core/components/sheet-modal/sheet-modal.ts index af8ce0f1777..9870071d293 100644 --- a/src/core/components/sheet-modal/sheet-modal.ts +++ b/src/core/components/sheet-modal/sheet-modal.ts @@ -24,7 +24,7 @@ import { CoreDirectivesRegistry } from '@singletons/directives-registry'; @Component({ selector: 'core-sheet-modal', templateUrl: 'sheet-modal.html', - styleUrls: ['sheet-modal.scss'], + styleUrl: 'sheet-modal.scss', }) export class CoreSheetModalComponent implements AfterViewInit { diff --git a/src/core/components/show-password/show-password.ts b/src/core/components/show-password/show-password.ts index 12ca321cad8..b36e0bf048f 100644 --- a/src/core/components/show-password/show-password.ts +++ b/src/core/components/show-password/show-password.ts @@ -16,7 +16,7 @@ import { Component, AfterViewInit, Input, ContentChild, ViewEncapsulation } from import { IonInput } from '@ionic/angular'; import { convertTextToHTMLElement } from '@/core/utils/create-html-element'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreLogger } from '@singletons/logger'; /** @@ -79,7 +79,7 @@ export class CoreShowPasswordComponent implements AfterViewInit { } // eslint-disable-next-line deprecation/deprecation - const input = await CoreUtils.ignoreErrors(this.ionInput.getInputElement()); + const input = await CorePromiseUtils.ignoreErrors(this.ionInput.getInputElement()); if (!input) { return; } diff --git a/src/core/components/site-picker/site-picker.ts b/src/core/components/site-picker/site-picker.ts index 3bcb7d08e34..7e644b2fabe 100644 --- a/src/core/components/site-picker/site-picker.ts +++ b/src/core/components/site-picker/site-picker.ts @@ -16,7 +16,7 @@ import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core'; import { CoreFilter } from '@features/filter/services/filter'; import { CoreSiteBasicInfo, CoreSites } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { Translate } from '@singletons'; /** @@ -56,7 +56,7 @@ export class CoreSitePickerComponent implements OnInit { await Promise.all(sites.map(async (site: SiteInfo) => { // Format the site name. const options = { clean: true, singleLine: true, filter: false }; - const siteName = await CoreUtils.ignoreErrors( + const siteName = await CorePromiseUtils.ignoreErrors( CoreFilter.formatText(site.siteName || '', options, [], site.id), site.siteName || '', ); diff --git a/src/core/components/sites-list/sites-list.ts b/src/core/components/sites-list/sites-list.ts index 78d83c89fb3..4018d83ff08 100644 --- a/src/core/components/sites-list/sites-list.ts +++ b/src/core/components/sites-list/sites-list.ts @@ -39,7 +39,7 @@ import { toBoolean } from '@/core/transforms/boolean'; @Component({ selector: 'core-sites-list', templateUrl: 'sites-list.html', - styleUrls: ['sites-list.scss'], + styleUrl: 'sites-list.scss', }) export class CoreSitesListComponent { diff --git a/src/core/components/split-view/split-view.ts b/src/core/components/split-view/split-view.ts index e7d8fc6af91..4bf6534cdfd 100644 --- a/src/core/components/split-view/split-view.ts +++ b/src/core/components/split-view/split-view.ts @@ -29,7 +29,7 @@ const disabledScrollClass = 'disable-scroll-y'; @Component({ selector: 'core-split-view', templateUrl: 'split-view.html', - styleUrls: ['split-view.scss'], + styleUrl: 'split-view.scss', }) export class CoreSplitViewComponent implements AfterViewInit, OnDestroy { diff --git a/src/core/components/swipe-navigation-tour/swipe-navigation-tour.ts b/src/core/components/swipe-navigation-tour/swipe-navigation-tour.ts index fb5c590857a..5919c4ebc74 100644 --- a/src/core/components/swipe-navigation-tour/swipe-navigation-tour.ts +++ b/src/core/components/swipe-navigation-tour/swipe-navigation-tour.ts @@ -21,7 +21,7 @@ import { CoreUserTours } from '@features/usertours/services/user-tours'; @Component({ selector: 'core-swipe-navigation-tour', templateUrl: 'core-swipe-navigation-tour.html', - styleUrls: ['swipe-navigation-tour.scss'], + styleUrl: 'swipe-navigation-tour.scss', }) export class CoreSwipeNavigationTourComponent { diff --git a/src/core/components/swipe-slides/swipe-slides.ts b/src/core/components/swipe-slides/swipe-slides.ts index 182fdc890f3..4830ee1e835 100644 --- a/src/core/components/swipe-slides/swipe-slides.ts +++ b/src/core/components/swipe-slides/swipe-slides.ts @@ -34,7 +34,7 @@ import { SwiperOptions } from 'swiper/types'; @Component({ selector: 'core-swipe-slides', templateUrl: 'swipe-slides.html', - styleUrls: ['swipe-slides.scss'], + styleUrl: 'swipe-slides.scss', }) export class CoreSwipeSlidesComponent implements OnChanges, OnDestroy, AsyncDirective { diff --git a/src/core/components/tabs-outlet/tabs-outlet.ts b/src/core/components/tabs-outlet/tabs-outlet.ts index 076e3963ff8..fd9dce45de7 100644 --- a/src/core/components/tabs-outlet/tabs-outlet.ts +++ b/src/core/components/tabs-outlet/tabs-outlet.ts @@ -23,7 +23,7 @@ import { } from '@angular/core'; import { IonRouterOutlet, IonTabs, ViewDidEnter, ViewDidLeave } from '@ionic/angular'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { Params } from '@angular/router'; import { CoreNavBarButtonsComponent } from '../navbar-buttons/navbar-buttons'; import { StackDidChangeEvent } from '@ionic/angular/common/directives/navigation/stack-utils'; @@ -49,7 +49,7 @@ import { CorePath } from '@singletons/path'; @Component({ selector: 'core-tabs-outlet', templateUrl: 'core-tabs-outlet.html', - styleUrls: ['../tabs/tabs.scss'], + styleUrl: '../tabs/tabs.scss', }) export class CoreTabsOutletComponent extends CoreTabsBaseComponent implements AfterViewInit, OnChanges, OnDestroy { diff --git a/src/core/components/tabs/tab.ts b/src/core/components/tabs/tab.ts index a7294e67f5a..b88dd6169cc 100644 --- a/src/core/components/tabs/tab.ts +++ b/src/core/components/tabs/tab.ts @@ -15,7 +15,7 @@ import { Component, Input, Output, OnInit, OnDestroy, ElementRef, EventEmitter, ContentChild, TemplateRef } from '@angular/core'; import { CoreTabBase } from '@classes/tabs'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { CoreDirectivesRegistry } from '@singletons/directives-registry'; import { CoreNavBarButtonsComponent } from '../navbar-buttons/navbar-buttons'; import { CoreTabsComponent } from './tabs'; diff --git a/src/core/components/tabs/tabs.ts b/src/core/components/tabs/tabs.ts index a84bf2e4d35..31f6343a201 100644 --- a/src/core/components/tabs/tabs.ts +++ b/src/core/components/tabs/tabs.ts @@ -41,7 +41,7 @@ import { toBoolean } from '@/core/transforms/boolean'; @Component({ selector: 'core-tabs', templateUrl: 'core-tabs.html', - styleUrls: ['tabs.scss'], + styleUrl: 'tabs.scss', }) export class CoreTabsComponent extends CoreTabsBaseComponent implements AfterViewInit { diff --git a/src/core/components/timer/timer.ts b/src/core/components/timer/timer.ts index 15ba62e7b3f..5f1b660ca06 100644 --- a/src/core/components/timer/timer.ts +++ b/src/core/components/timer/timer.ts @@ -27,7 +27,7 @@ import { CoreTimeUtils } from '@services/utils/time'; @Component({ selector: 'core-timer', templateUrl: 'core-timer.html', - styleUrls: ['timer.scss'], + styleUrl: 'timer.scss', }) export class CoreTimerComponent implements OnInit, OnDestroy { diff --git a/src/core/components/user-avatar/user-avatar.ts b/src/core/components/user-avatar/user-avatar.ts index de961ba4b8d..7cb3b866ecd 100644 --- a/src/core/components/user-avatar/user-avatar.ts +++ b/src/core/components/user-avatar/user-avatar.ts @@ -15,7 +15,7 @@ import { Component, Input, OnInit, OnChanges, OnDestroy, SimpleChange } from '@angular/core'; import { CoreSiteBasicInfo, CoreSites } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { USER_PROFILE_PICTURE_UPDATED, CoreUserBasicData } from '@features/user/services/user'; import { CoreNavigator } from '@services/navigator'; @@ -33,7 +33,7 @@ import { toBoolean } from '@/core/transforms/boolean'; @Component({ selector: 'core-user-avatar', templateUrl: 'core-user-avatar.html', - styleUrls: ['user-avatar.scss'], + styleUrl: 'user-avatar.scss', }) export class CoreUserAvatarComponent implements OnInit, OnChanges, OnDestroy { diff --git a/src/core/constants.ts b/src/core/constants.ts index 96706ea39a1..f14df0ecf28 100644 --- a/src/core/constants.ts +++ b/src/core/constants.ts @@ -76,6 +76,36 @@ export const DownloadStatus = { export type DownloadStatus = typeof DownloadStatus[keyof typeof DownloadStatus]; /* eslint-enable @typescript-eslint/naming-convention, @typescript-eslint/no-redeclare */ +// Constants for cache update frequency. +export const CoreCacheUpdateFrequency = { + USUALLY: 0, // eslint-disable-line @typescript-eslint/naming-convention + OFTEN: 1, // eslint-disable-line @typescript-eslint/naming-convention + SOMETIMES: 2, // eslint-disable-line @typescript-eslint/naming-convention + RARELY: 3, // eslint-disable-line @typescript-eslint/naming-convention +} as const; +// eslint-disable-next-line @typescript-eslint/no-redeclare +export type CoreCacheUpdateFrequency = typeof CoreCacheUpdateFrequency[keyof typeof CoreCacheUpdateFrequency]; + +export const MINIMUM_MOODLE_VERSION = '3.5'; + +// Versions of Moodle releases. +export const MOODLE_RELEASES = { + '3.5': 2018051700, + '3.6': 2018120300, + '3.7': 2019052000, + '3.8': 2019111800, + '3.9': 2020061500, + '3.10': 2020110900, + '3.11': 2021051700, + '4.0': 2022041900, + '4.1': 2022112800, + '4.2': 2023042400, + '4.3': 2023100900, + '4.4': 2024042200, + '4.5': 2024100700, + '5.0': 2024100800, // @todo [5.0] replace with right value when released. Using a tmp value to be able to test new things. +}; + /** * Static class to contain all the core constants. */ diff --git a/src/core/core.module.ts b/src/core/core.module.ts index 554e088fc47..fee9c1c4ff7 100644 --- a/src/core/core.module.ts +++ b/src/core/core.module.ts @@ -29,6 +29,7 @@ import { getInitializerProviders } from './initializers'; export async function getCoreServices(): Promise[]> { const { CoreAppProvider } = await import('@services/app'); + const { CoreAppDBService } = await import('@services/app-db'); const { CoreConfigProvider } = await import('@services/config'); const { CoreCronDelegateService } = await import('@services/cron'); const { CoreCustomURLSchemesProvider } = await import('@services/urlschemes'); @@ -65,6 +66,7 @@ export async function getCoreServices(): Promise[]> { return [ CoreAppProvider, + CoreAppDBService, CoreConfigProvider, CoreCronDelegateService, CoreCustomURLSchemesProvider, @@ -100,6 +102,32 @@ export async function getCoreServices(): Promise[]> { ]; } +/** + * Get core exported objects. + * + * @returns Core exported objects. + */ +export async function getCoreExportedObjects(): Promise> { + const { + CoreConstants, + CoreCacheUpdateFrequency, + DownloadStatus, + MINIMUM_MOODLE_VERSION, + MOODLE_RELEASES, + } = await import('@/core/constants'); + + /* eslint-disable @typescript-eslint/naming-convention */ + return { + CoreConstants, + CoreConfigConstants: CoreConstants.CONFIG, + CoreCacheUpdateFrequency, + DownloadStatus, + MINIMUM_MOODLE_VERSION, + MOODLE_RELEASES, + }; + /* eslint-enable @typescript-eslint/naming-convention */ +} + @NgModule({ imports: [ CoreFeaturesModule, diff --git a/src/core/directives/collapsible-footer.ts b/src/core/directives/collapsible-footer.ts index 7c87d145063..d58f14a1d04 100644 --- a/src/core/directives/collapsible-footer.ts +++ b/src/core/directives/collapsible-footer.ts @@ -15,7 +15,7 @@ import { Directive, ElementRef, Input, OnDestroy, OnInit } from '@angular/core'; import { ScrollDetail } from '@ionic/core'; import { IonContent } from '@ionic/angular'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { CoreMath } from '@singletons/math'; import { CoreDirectivesRegistry } from '@singletons/directives-registry'; import { CoreFormatTextDirective } from './format-text'; diff --git a/src/core/directives/collapsible-item.ts b/src/core/directives/collapsible-item.ts index a845e0a1428..642169c3b80 100644 --- a/src/core/directives/collapsible-item.ts +++ b/src/core/directives/collapsible-item.ts @@ -16,7 +16,7 @@ import { Directive, ElementRef, Input, OnDestroy, OnInit } from '@angular/core'; import { CoreCancellablePromise } from '@classes/cancellable-promise'; import { CoreLoadingComponent } from '@components/loading/loading'; import { CoreSettingsHelper } from '@features/settings/services/settings-helper'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { Translate } from '@singletons'; import { CoreColors } from '@singletons/colors'; import { CoreDirectivesRegistry } from '@singletons/directives-registry'; diff --git a/src/core/directives/external-content.ts b/src/core/directives/external-content.ts index 4ed81c8a48a..4ed754f7248 100644 --- a/src/core/directives/external-content.ts +++ b/src/core/directives/external-content.ts @@ -27,7 +27,7 @@ import { CoreFile, CoreFileProvider } from '@services/file'; import { CoreFilepool, CoreFilepoolFileActions, CoreFilepoolFileEventData } from '@services/filepool'; import { CoreSites } from '@services/sites'; import { CoreUrl } from '@singletons/url'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreLogger } from '@singletons/logger'; import { CoreError } from '@classes/errors/error'; import { CoreSite } from '@classes/sites/site'; @@ -203,7 +203,7 @@ export class CoreExternalContentDirective implements AfterViewInit, OnChanges, O this.handleVideoSubtitles( this.element); } - const site = await CoreUtils.ignoreErrors(CoreSites.getSite(this.siteId)); + const site = await CorePromiseUtils.ignoreErrors(CoreSites.getSite(this.siteId)); const isSiteFile = site?.isSitePluginFileUrl(url); if (!url || !url.match(/^https?:\/\//i) || CoreUrl.isLocalFileUrl(url) || @@ -305,7 +305,7 @@ export class CoreExternalContentDirective implements AfterViewInit, OnChanges, O }); try { - await CoreUtils.allPromises(promises); + await CorePromiseUtils.allPromises(promises); this.element.setAttribute('style', inlineStyles); } catch (error) { diff --git a/src/core/directives/format-text.ts b/src/core/directives/format-text.ts index e5086623cab..7257beb714a 100644 --- a/src/core/directives/format-text.ts +++ b/src/core/directives/format-text.ts @@ -32,7 +32,7 @@ import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreIframeUtils, CoreIframeUtilsProvider } from '@services/utils/iframe'; import { CoreText } from '@singletons/text'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreErrorHelper } from '@services/error-helper'; import { CoreSite } from '@classes/sites/site'; import { NgZone, Translate } from '@singletons'; import { CoreExternalContentDirective } from './external-content'; @@ -58,6 +58,7 @@ import { ContextLevel } from '../constants'; import { CoreWait } from '@singletons/wait'; import { toBoolean } from '../transforms/boolean'; import { CoreViewer } from '@features/viewer/services/viewer'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Directive to format text rendered. It renders the HTML and treats all links and media, using CoreLinkDirective @@ -422,7 +423,7 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy, AsyncDirec */ protected async formatContents(): Promise { // Retrieve the site since it might be needed later. - const site = await CoreUtils.ignoreErrors(CoreSites.getSite(this.siteId)); + const site = await CorePromiseUtils.ignoreErrors(CoreSites.getSite(this.siteId)); const siteId = site?.getId(); @@ -612,7 +613,7 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy, AsyncDirec if (externalImages.length) { // Wait for images to load. - const promise = CoreUtils.allPromises(externalImages.map((externalImage) => { + const promise = CorePromiseUtils.allPromises(externalImages.map((externalImage) => { if (externalImage.loaded) { // Image has already been loaded, no need to wait. return Promise.resolve(); @@ -622,11 +623,11 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy, AsyncDirec })); // Automatically reject the promise after 5 seconds to prevent blocking the user forever. - promises.push(CoreUtils.ignoreErrors(CoreUtils.timeoutPromise(promise, 5000))); + promises.push(CorePromiseUtils.ignoreErrors(CorePromiseUtils.timeoutPromise(promise, 5000))); } // Run asynchronous operations in the background to avoid blocking rendering. - Promise.all(promises).catch(error => CoreUtils.logUnhandledError('Error treating format-text elements', error)); + Promise.all(promises).catch(error => CoreErrorHelper.logUnhandledError('Error treating format-text elements', error)); return [ ...videoControllers, diff --git a/src/core/directives/link.ts b/src/core/directives/link.ts index 3bb1f30d398..0f38c837507 100644 --- a/src/core/directives/link.ts +++ b/src/core/directives/link.ts @@ -19,7 +19,7 @@ import { CoreFileHelper } from '@services/file-helper'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreUrl } from '@singletons/url'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreOpener } from '@singletons/opener'; import { CoreConstants } from '@/core/constants'; import { CoreContentLinksHelper } from '@features/contentlinks/services/contentlinks-helper'; import { CoreCustomURLSchemes } from '@services/urlschemes'; @@ -163,7 +163,7 @@ export class CoreLinkDirective implements OnInit { } try { - await CoreUtils.openFile(path); + await CoreOpener.openFile(path); } catch (error) { CoreDomUtils.showErrorModal(error); } @@ -186,9 +186,9 @@ export class CoreLinkDirective implements OnInit { if (!CoreSites.isLoggedIn()) { // Not logged in, cannot auto-login. if (openInApp) { - CoreUtils.openInApp(href); + CoreOpener.openInApp(href); } else { - CoreUtils.openInBrowser(href, { showBrowserWarning: this.showBrowserWarning }); + CoreOpener.openInBrowser(href, { showBrowserWarning: this.showBrowserWarning }); } return; @@ -227,9 +227,9 @@ export class CoreLinkDirective implements OnInit { } } else { if (openInApp) { - CoreUtils.openInApp(href); + CoreOpener.openInApp(href); } else { - CoreUtils.openInBrowser(href, { showBrowserWarning: this.showBrowserWarning }); + CoreOpener.openInBrowser(href, { showBrowserWarning: this.showBrowserWarning }); } } } diff --git a/src/core/directives/on-resize.ts b/src/core/directives/on-resize.ts index 8660008f05c..e12adcbf3b5 100644 --- a/src/core/directives/on-resize.ts +++ b/src/core/directives/on-resize.ts @@ -13,7 +13,7 @@ // limitations under the License. import { Directive, ElementRef, OnInit, Output, EventEmitter, OnDestroy } from '@angular/core'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; /** * Directive to listen for element resize events. diff --git a/src/core/directives/tests/format-text.test.ts b/src/core/directives/tests/format-text.test.ts index e041ea7ceb1..1c1f3455017 100644 --- a/src/core/directives/tests/format-text.test.ts +++ b/src/core/directives/tests/format-text.test.ts @@ -23,7 +23,7 @@ import { CoreFilterHelper } from '@features/filter/services/filter-helper'; import { CoreFormatTextDirective } from '@directives/format-text'; import { CoreSite } from '@classes/sites/site'; import { CoreSites } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { mock, mockSingleton, RenderConfig, renderTemplate, renderWrapperComponent } from '@/testing/utils'; import { ContextLevel } from '@/core/constants'; @@ -135,7 +135,7 @@ describe('CoreFormatTextDirective', () => { }); // @todo this is done because we cannot mock image being loaded, we should find an alternative... - CoreUtils.instance.timeoutPromise = () => Promise.resolve(null as unknown as T); + CorePromiseUtils.timeoutPromise = () => Promise.resolve(null as unknown as T); mockSingleton(CoreFilepool, { getSrcByUrl: () => Promise.resolve('file://local-path') }); mockSingleton(CoreSites, { diff --git a/src/core/features/block/classes/base-block-component.ts b/src/core/features/block/classes/base-block-component.ts index 86f74c90b9b..1d6aa2f358d 100644 --- a/src/core/features/block/classes/base-block-component.ts +++ b/src/core/features/block/classes/base-block-component.ts @@ -15,7 +15,7 @@ import { OnInit, Input, Component, Optional, Inject, OnChanges, SimpleChanges } from '@angular/core'; import { CoreLogger } from '@singletons/logger'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreArray } from '@singletons/array'; import { CoreText } from '@singletons/text'; import { CoreCourseBlock } from '../../course/services/course'; import { Params } from '@angular/router'; @@ -78,7 +78,7 @@ export abstract class CoreBlockBaseComponent implements OnInit, OnChanges, ICore config.value = CoreText.parseJSON(config.value); }); - this.block.configsRecord = CoreUtils.arrayToObject(this.block.configs, 'name'); + this.block.configsRecord = CoreArray.toObject(this.block.configs, 'name'); } /** diff --git a/src/core/features/block/components/block/block.ts b/src/core/features/block/components/block/block.ts index 684e5c7cdfd..46308a7b8a7 100644 --- a/src/core/features/block/components/block/block.ts +++ b/src/core/features/block/components/block/block.ts @@ -26,7 +26,7 @@ import { ContextLevel } from '@/core/constants'; @Component({ selector: 'core-block', templateUrl: 'core-block.html', - styleUrls: ['block.scss'], + styleUrl: 'block.scss', }) export class CoreBlockComponent implements OnChanges, OnDestroy { diff --git a/src/core/features/block/components/only-title-block/only-title-block.ts b/src/core/features/block/components/only-title-block/only-title-block.ts index cac191d0cbd..7affb4388f8 100644 --- a/src/core/features/block/components/only-title-block/only-title-block.ts +++ b/src/core/features/block/components/only-title-block/only-title-block.ts @@ -22,7 +22,7 @@ import { CoreNavigator } from '@services/navigator'; @Component({ selector: 'core-block-only-title', templateUrl: 'core-block-only-title.html', - styleUrls: ['only-title-block.scss'], + styleUrl: 'only-title-block.scss', }) export class CoreBlockOnlyTitleComponent extends CoreBlockBaseComponent implements OnInit { diff --git a/src/core/features/block/components/side-blocks-button/side-blocks-button.ts b/src/core/features/block/components/side-blocks-button/side-blocks-button.ts index cf159c64144..67cc3ceb45e 100644 --- a/src/core/features/block/components/side-blocks-button/side-blocks-button.ts +++ b/src/core/features/block/components/side-blocks-button/side-blocks-button.ts @@ -27,7 +27,7 @@ import { ContextLevel } from '@/core/constants'; @Component({ selector: 'core-block-side-blocks-button', templateUrl: 'side-blocks-button.html', - styleUrls: ['side-blocks-button.scss'], + styleUrl: 'side-blocks-button.scss', }) export class CoreBlockSideBlocksButtonComponent implements OnInit, OnDestroy { diff --git a/src/core/features/block/components/side-blocks-tour/side-blocks-tour.ts b/src/core/features/block/components/side-blocks-tour/side-blocks-tour.ts index 2f5c2ada40b..546ebc68b48 100644 --- a/src/core/features/block/components/side-blocks-tour/side-blocks-tour.ts +++ b/src/core/features/block/components/side-blocks-tour/side-blocks-tour.ts @@ -21,7 +21,7 @@ import { CoreUserTours } from '@features/usertours/services/user-tours'; @Component({ selector: 'core-block-side-blocks-tour', templateUrl: 'side-blocks-tour.html', - styleUrls: ['side-blocks-tour.scss'], + styleUrl: 'side-blocks-tour.scss', }) export class CoreBlockSideBlocksTourComponent { diff --git a/src/core/features/block/components/side-blocks/side-blocks.ts b/src/core/features/block/components/side-blocks/side-blocks.ts index 92b20b1b840..eabb1e33fd2 100644 --- a/src/core/features/block/components/side-blocks/side-blocks.ts +++ b/src/core/features/block/components/side-blocks/side-blocks.ts @@ -18,7 +18,7 @@ import { CoreDomUtils } from '@services/utils/dom'; import { CoreCourse, CoreCourseBlock } from '@features/course/services/course'; import { CoreBlockHelper } from '../../services/block-helper'; import { CoreBlockComponent } from '../block/block'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreCoursesDashboard } from '@features/courses/services/dashboard'; import { CoreDom } from '@singletons/dom'; import { ContextLevel } from '@/core/constants'; @@ -118,7 +118,7 @@ export class CoreBlockSideBlocksComponent implements OnInit { * @param refresher Refresher. */ async doRefresh(refresher?: HTMLIonRefresherElement): Promise { - await CoreUtils.ignoreErrors(this.invalidateBlocks()); + await CorePromiseUtils.ignoreErrors(this.invalidateBlocks()); await this.loadContent().finally(() => { refresher?.complete(); diff --git a/src/core/features/comments/comments-lazy.module.ts b/src/core/features/comments/comments-lazy.module.ts index efce272c0ae..36e60000487 100644 --- a/src/core/features/comments/comments-lazy.module.ts +++ b/src/core/features/comments/comments-lazy.module.ts @@ -33,4 +33,4 @@ const routes: Routes = [ CoreCommentsViewerPage, ], }) -export class CoreCommentsLazyModule {} +export default class CoreCommentsLazyModule {} diff --git a/src/core/features/comments/comments.module.ts b/src/core/features/comments/comments.module.ts index 7d2c29f13ae..279fa33af2a 100644 --- a/src/core/features/comments/comments.module.ts +++ b/src/core/features/comments/comments.module.ts @@ -42,7 +42,7 @@ export async function getCommentsServices(): Promise[]> { const routes: Routes = [ { path: 'comments', - loadChildren: () => import('@features/comments/comments-lazy.module').then(m => m.CoreCommentsLazyModule), + loadChildren: () => import('@features/comments/comments-lazy.module'), }, ]; diff --git a/src/core/features/comments/components/comments/comments.ts b/src/core/features/comments/components/comments/comments.ts index 9e9f44828fa..b15171df757 100644 --- a/src/core/features/comments/components/comments/comments.ts +++ b/src/core/features/comments/components/comments/comments.ts @@ -20,7 +20,7 @@ import { import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreSites } from '@services/sites'; import { CoreNavigator } from '@services/navigator'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { ContextLevel } from '@/core/constants'; import { toBoolean } from '@/core/transforms/boolean'; @@ -76,7 +76,7 @@ export class CoreCommentsCommentsComponent implements OnInit, OnChanges, OnDestr this.undefinedOrEqual(data, 'component') && this.undefinedOrEqual(data, 'itemId') && this.undefinedOrEqual(data, 'area')) { - CoreUtils.ignoreErrors(this.doRefresh()); + CorePromiseUtils.ignoreErrors(this.doRefresh()); } }, CoreSites.getCurrentSiteId(), diff --git a/src/core/features/comments/constants.ts b/src/core/features/comments/constants.ts new file mode 100644 index 00000000000..65a5da36c42 --- /dev/null +++ b/src/core/features/comments/constants.ts @@ -0,0 +1,15 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +export const CORE_COMMENTS_AUTO_SYNCED = 'core_comments_autom_synced'; diff --git a/src/core/features/comments/pages/viewer/viewer.ts b/src/core/features/comments/pages/viewer/viewer.ts index 688fa4bf019..e063f3678eb 100644 --- a/src/core/features/comments/pages/viewer/viewer.ts +++ b/src/core/features/comments/pages/viewer/viewer.ts @@ -24,13 +24,12 @@ import { } from '@features/comments/services/comments'; import { CoreCommentsSync, - CoreCommentsSyncProvider, } from '@features/comments/services/comments-sync'; import { IonContent } from '@ionic/angular'; import { ContextLevel, CoreConstants } from '@/core/constants'; import { CoreNavigator } from '@services/navigator'; import { NgZone, Translate } from '@singletons'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreUser } from '@features/user/services/user'; import { CoreText } from '@singletons/text'; @@ -45,6 +44,7 @@ import { CoreAnimations } from '@components/animations'; import { CoreKeyboard } from '@singletons/keyboard'; import { CoreToasts, ToastDuration } from '@services/toasts'; import { CoreLoadings } from '@services/loadings'; +import { CORE_COMMENTS_AUTO_SYNCED } from '@features/comments/constants'; /** * Page that displays comments. @@ -94,7 +94,7 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { this.currentUserId = CoreSites.getCurrentSiteUserId(); // Refresh data if comments are synchronized automatically. - this.syncObserver = CoreEvents.on(CoreCommentsSyncProvider.AUTO_SYNCED, (data) => { + this.syncObserver = CoreEvents.on(CORE_COMMENTS_AUTO_SYNCED, (data) => { if (data.contextLevel == this.contextLevel && data.instanceId == this.instanceId && data.componentName == this.componentName && data.itemId == this.itemId && data.area == this.area) { // Show the sync warnings. @@ -161,7 +161,7 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { this.loadMoreError = false; if (sync) { - await CoreUtils.ignoreErrors(this.syncComments(showErrors)); + await CorePromiseUtils.ignoreErrors(this.syncComments(showErrors)); } try { @@ -256,7 +256,7 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { this.refreshIcon = CoreConstants.ICON_LOADING; this.syncIcon = CoreConstants.ICON_LOADING; - await CoreUtils.ignoreErrors(this.invalidateComments()); + await CorePromiseUtils.ignoreErrors(this.invalidateComments()); this.page = 0; this.comments = []; @@ -623,7 +623,7 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { * Refresh cached data in background. */ protected async refreshInBackground(): Promise { - await CoreUtils.ignoreErrors(this.invalidateComments()); + await CorePromiseUtils.ignoreErrors(this.invalidateComments()); const promises: Promise[] = []; diff --git a/src/core/features/comments/services/comments-sync.ts b/src/core/features/comments/services/comments-sync.ts index 2aa32d8331d..d6f93369408 100644 --- a/src/core/features/comments/services/comments-sync.ts +++ b/src/core/features/comments/services/comments-sync.ts @@ -20,11 +20,13 @@ import { makeSingleton, Translate } from '@singletons'; import { CoreCommentsOffline } from './comments-offline'; import { CoreSites } from '@services/sites'; import { CoreNetwork } from '@services/network'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreWSError } from '@classes/errors/wserror'; import { CoreNetworkError } from '@classes/errors/network-error'; import { CoreCommentsDBRecord, CoreCommentsDeletedDBRecord } from './database/comments'; import { CoreSyncResult } from '@services/sync'; import { ContextLevel } from '@/core/constants'; +import { CORE_COMMENTS_AUTO_SYNCED } from '../constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Service to sync omments. @@ -32,8 +34,6 @@ import { ContextLevel } from '@/core/constants'; @Injectable( { providedIn: 'root' }) export class CoreCommentsSyncProvider extends CoreSyncBaseProvider { - static readonly AUTO_SYNCED = 'core_comments_autom_synced'; - constructor() { super('CoreCommentsSync'); } @@ -96,7 +96,7 @@ export class CoreCommentsSyncProvider extends CoreSyncBaseProvider('core_comment_get_comments', params, preSets); @@ -524,7 +523,7 @@ export class CoreCommentsProvider { ): Promise { const site = await CoreSites.getSite(siteId); - await CoreUtils.allPromises([ + await CorePromiseUtils.allPromises([ // This is done with starting with to avoid conflicts with previous keys that were including page. site.invalidateWsCacheForKeyStartingWith(this.getCommentsCacheKey( contextLevel, diff --git a/src/core/features/compile/components/compile-html/compile-html.ts b/src/core/features/compile/components/compile-html/compile-html.ts index c55156abe1c..9176d4990b4 100644 --- a/src/core/features/compile/components/compile-html/compile-html.ts +++ b/src/core/features/compile/components/compile-html/compile-html.ts @@ -43,7 +43,7 @@ import { CorePromisedValue } from '@classes/promised-value'; import { CoreCompile } from '@features/compile/services/compile'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreWS } from '@services/ws'; import { CoreDom } from '@singletons/dom'; @@ -192,7 +192,7 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy, DoCheck { } if (this.stylesPath && !this.cssCode) { - this.cssCode = await CoreUtils.ignoreErrors(CoreWS.getText(this.stylesPath)); + this.cssCode = await CorePromiseUtils.ignoreErrors(CoreWS.getText(this.stylesPath)); } // Prepend all CSS rules with :host to avoid conflicts. diff --git a/src/core/features/compile/services/compile.ts b/src/core/features/compile/services/compile.ts index d142682cff7..688c7557696 100644 --- a/src/core/features/compile/services/compile.ts +++ b/src/core/features/compile/services/compile.ts @@ -42,12 +42,12 @@ import { makeSingleton } from '@singletons'; import { effectWithInjectionContext, modelWithInjectionContext } from '@/core/utils/signals'; // Import core services. -import { getCoreServices } from '@/core/core.module'; +import { getCoreExportedObjects, getCoreServices } from '@/core/core.module'; import { getBlockServices } from '@features/block/block.module'; import { getCommentsServices } from '@features/comments/comments.module'; import { getContentLinksExportedObjects, getContentLinksServices } from '@features/contentlinks/contentlinks.module'; import { getCourseExportedObjects, getCourseServices, getCourseStandaloneComponents } from '@features/course/course.module'; -import { getCoursesServices } from '@features/courses/courses.module'; +import { getCoursesExportedObjects, getCoursesServices } from '@features/courses/courses.module'; import { getEditorServices } from '@features/editor/editor.module'; import { getEnrolServices } from '@features/enrol/enrol.module'; import { getFileUploadedServices } from '@features/fileuploader/fileuploader.module'; @@ -55,7 +55,7 @@ import { getFilterServices } from '@features/filter/filter.module'; import { getGradesServices } from '@features/grades/grades.module'; import { getH5PServices } from '@features/h5p/h5p.module'; import { getLoginServices } from '@features/login/login.module'; -import { getMainMenuServices } from '@features/mainmenu/mainmenu.module'; +import { getMainMenuExportedObjects, getMainMenuServices } from '@features/mainmenu/mainmenu.module'; import { getNativeServices } from '@features/native/native.module'; import { getPushNotificationsServices } from '@features/pushnotifications/pushnotifications.module'; import { getQuestionServices } from '@features/question/question.module'; @@ -73,37 +73,42 @@ import { getXAPIServices } from '@features/xapi/xapi.module'; import { DomSanitizer } from '@angular/platform-browser'; import { FormBuilder, Validators } from '@angular/forms'; import { HttpClient } from '@angular/common/http'; -import { CoreConstants, DownloadStatus } from '@/core/constants'; import moment from 'moment-timezone'; import { Md5 } from 'ts-md5/dist/md5'; // Import core classes that can be useful for site plugins. import { CoreSyncBaseProvider } from '@classes/base-sync'; import { CoreArray } from '@singletons/array'; +import { CoreCache } from '@classes/cache'; import { CoreColors } from '@singletons/colors'; +import { CoreCountries } from '@singletons/countries'; +import { CoreDelegate } from '@classes/delegate'; import { CoreDirectivesRegistry } from '@singletons/directives-registry'; import { CoreDom } from '@singletons/dom'; +import { CoreFileUtils } from '@singletons/file-utils'; import { CoreForms } from '@singletons/form'; +import { CoreGeolocationError, CoreGeolocationErrorReason } from '@services/geolocation'; import { CoreKeyboard } from '@singletons/keyboard'; +import { CoreMedia } from '@singletons/media'; +import { CoreNetwork } from '@services/network'; import { CoreObject } from '@singletons/object'; +import { CoreOpener } from '@singletons/opener'; import { CorePath } from '@singletons/path'; +import { CorePromiseUtils } from '@singletons/promise-utils'; +import { CoreSSO } from '@singletons/sso'; import { CoreText } from '@singletons/text'; import { CoreTime } from '@singletons/time'; import { CoreUrl } from '@singletons/url'; +import { CoreUtils } from '@singletons/utils'; import { CoreWait } from '@singletons/wait'; import { CoreWindow } from '@singletons/window'; -import { CoreCache } from '@classes/cache'; -import { CoreDelegate } from '@classes/delegate'; -import { CoreGeolocationError, CoreGeolocationErrorReason } from '@services/geolocation'; import { getCoreErrorsExportedObjects } from '@classes/errors/errors'; -import { CoreNetwork } from '@services/network'; // Import all core modules that define components, directives and pipes. import { CoreSharedModule } from '@/core/shared.module'; import { CoreCourseComponentsModule } from '@features/course/components/components.module'; import { CoreCourseDirectivesModule } from '@features/course/directives/directives.module'; import { CoreCoursesComponentsModule } from '@features/courses/components/components.module'; -import { CoreSitePluginsDirectivesModule } from '@features/siteplugins/directives/directives.module'; import { CoreUserComponentsModule } from '@features/user/components/components.module'; import { CoreQuestionComponentsModule } from '@features/question/components/components.module'; import { CoreBlockComponentsModule } from '@features/block/components/components.module'; @@ -132,8 +137,11 @@ import { CorePromisedValue } from '@classes/promised-value'; import { CorePlatform } from '@services/platform'; import { CoreAutoLogoutService } from '@features/autologout/services/autologout'; -import { CoreSitePluginsProvider } from '@features/siteplugins/services/siteplugins'; -import { getSitePluginsExportedObjects } from '@features/siteplugins/siteplugins.module'; +import { + getSitePluginsDirectives, + getSitePluginsExportedObjects, + getSitePluginsServices, +} from '@features/siteplugins/siteplugins.module'; import { CoreError } from '@classes/errors/error'; /** @@ -161,7 +169,6 @@ export class CoreCompileProvider { CoreBlockComponentsModule, CoreEditorComponentsModule, CoreSearchComponentsModule, - CoreSitePluginsDirectivesModule, ]; protected readonly LAZY_IMPORTS = [ @@ -170,6 +177,7 @@ export class CoreCompileProvider { getModWorkshopComponentModules, getCoreStandaloneComponents, getCourseStandaloneComponents, + getSitePluginsDirectives, ]; protected componentId = 0; @@ -295,9 +303,6 @@ export class CoreCompileProvider { // Add some final classes. instance['injector'] = injector; instance['Validators'] = Validators; - instance['CoreConstants'] = CoreConstants; - instance['DownloadStatus'] = DownloadStatus; - instance['CoreConfigConstants'] = CoreConstants.CONFIG; instance['CoreEventsProvider'] = CoreEvents; instance['CoreLoggerProvider'] = CoreLogger; instance['moment'] = moment; @@ -314,19 +319,26 @@ export class CoreCompileProvider { */ instance['Network'] = CoreNetwork.instance; instance['CoreNetwork'] = CoreNetwork.instance; - instance['CorePlatform'] = CorePlatform.instance; - instance['CoreSyncBaseProvider'] = CoreSyncBaseProvider; instance['CoreArray'] = CoreArray; instance['CoreColors'] = CoreColors; + instance['CoreCountries'] = CoreCountries; instance['CoreDirectivesRegistry'] = CoreDirectivesRegistry; instance['CoreDom'] = CoreDom; + instance['CoreFileUtils'] = CoreFileUtils; instance['CoreForms'] = CoreForms; instance['CoreKeyboard'] = CoreKeyboard; + instance['CoreMedia'] = CoreMedia; instance['CoreObject'] = CoreObject; + instance['CoreOpener'] = CoreOpener; instance['CorePath'] = CorePath; + instance['CorePlatform'] = CorePlatform.instance; + instance['CorePromiseUtils'] = CorePromiseUtils; + instance['CoreSSO'] = CoreSSO; + instance['CoreSyncBaseProvider'] = CoreSyncBaseProvider; instance['CoreText'] = CoreText; instance['CoreTime'] = CoreTime; instance['CoreUrl'] = CoreUrl; + instance['CoreUtils'] = CoreUtils; instance['CoreWait'] = CoreWait; instance['CoreWindow'] = CoreWindow; instance['CoreCache'] = CoreCache; // @deprecated since 4.4, plugins should use plain objects instead. @@ -399,6 +411,7 @@ export class CoreCompileProvider { getNotesServices(), getNotificationsServices(), getPrivateFilesServices(), + getSitePluginsServices(), ]); const lazyLibraries = services.flat(); @@ -406,7 +419,6 @@ export class CoreCompileProvider { return [ ...lazyLibraries, CoreAutoLogoutService, - CoreSitePluginsProvider, ...this.OTHER_SERVICES, ]; } @@ -418,8 +430,11 @@ export class CoreCompileProvider { */ protected async getExportedObjects(): Promise> { const objects = await Promise.all([ + getCoreExportedObjects(), getCoreErrorsExportedObjects(), getCourseExportedObjects(), + getCoursesExportedObjects(), + getMainMenuExportedObjects(), getContentLinksExportedObjects(), getSitePluginsExportedObjects(), ]); diff --git a/src/core/features/contentlinks/services/contentlinks-delegate.ts b/src/core/features/contentlinks/services/contentlinks-delegate.ts index c765272434a..2b3e1a93c3b 100644 --- a/src/core/features/contentlinks/services/contentlinks-delegate.ts +++ b/src/core/features/contentlinks/services/contentlinks-delegate.ts @@ -16,9 +16,9 @@ import { Injectable } from '@angular/core'; import { CoreLogger } from '@singletons/logger'; import { CoreSites } from '@services/sites'; import { CoreUrl } from '@singletons/url'; -import { CoreUtils } from '@services/utils/utils'; import { makeSingleton } from '@singletons'; import { CoreText } from '@singletons/text'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Interface that all handlers must implement. @@ -187,13 +187,13 @@ export class CoreContentLinksDelegateService { } // Filter the site IDs using the isEnabled function. - promises.push(CoreUtils.filterEnabledSites(siteIds, isEnabledFn, checkAll).then(async (siteIds) => { + promises.push(CoreSites.filterEnabledSites(siteIds, isEnabledFn, checkAll).then(async (siteIds) => { if (!siteIds.length) { // No sites supported, no actions. return; } - const actions = await CoreUtils.ignoreErrors( + const actions = await CorePromiseUtils.ignoreErrors( Promise.resolve(handler.getActions(siteIds, relativeUrl, params, courseId, data)), [], ); @@ -240,7 +240,7 @@ export class CoreContentLinksDelegateService { })); } try { - await CoreUtils.allPromises(promises); + await CorePromiseUtils.allPromises(promises); } catch { // Ignore errors. } diff --git a/src/core/features/course/classes/main-activity-component.ts b/src/core/features/course/classes/main-activity-component.ts index 02f2e812733..e24f336a973 100644 --- a/src/core/features/course/classes/main-activity-component.ts +++ b/src/core/features/course/classes/main-activity-component.ts @@ -18,7 +18,7 @@ import { IonContent } from '@ionic/angular'; import { CoreCourseModuleMainResourceComponent } from './main-resource-component'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreCourse } from '../services/course'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreCourseContentsPage } from '../pages/contents/contents'; import { CoreSites } from '@services/sites'; @@ -100,7 +100,7 @@ export class CoreCourseModuleMainActivityComponent extends CoreCourseModuleMainR return; } - await CoreUtils.ignoreErrors(Promise.all([ + await CorePromiseUtils.ignoreErrors(Promise.all([ this.invalidateContent(), this.showCompletion ? CoreCourse.invalidateModule(this.module.id) : undefined, ])); @@ -165,7 +165,7 @@ export class CoreCourseModuleMainActivityComponent extends CoreCourseModuleMainR try { if (refresh && this.showCompletion) { - await CoreUtils.ignoreErrors(this.fetchModule()); + await CorePromiseUtils.ignoreErrors(this.fetchModule()); } await this.fetchContent(refresh, sync, showErrors); diff --git a/src/core/features/course/classes/main-resource-component.ts b/src/core/features/course/classes/main-resource-component.ts index 6e67b5d1e6a..2003c1e5b06 100644 --- a/src/core/features/course/classes/main-resource-component.ts +++ b/src/core/features/course/classes/main-resource-component.ts @@ -19,7 +19,7 @@ import { CoreNetwork } from '@services/network'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { Translate } from '@singletons'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreLogger } from '@singletons/logger'; @@ -35,6 +35,7 @@ import { CoreTime } from '@singletons/time'; import { CoreText } from '@singletons/text'; import { CoreModals } from '@services/modals'; import { CoreErrorHelper, CoreErrorObject } from '@services/error-helper'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Result of a resource download. @@ -131,10 +132,10 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, // If it's a single activity course and the refresher is displayed within the component, // call doRefresh on the section page to refresh the course data. if (this.courseContentsPage && !CoreCourseModuleDelegate.displayRefresherInSingleActivity(this.module.modname)) { - await CoreUtils.ignoreErrors(this.courseContentsPage.doRefresh()); + await CorePromiseUtils.ignoreErrors(this.courseContentsPage.doRefresh()); } - await CoreUtils.ignoreErrors(this.refreshContent(true, showErrors)); + await CorePromiseUtils.ignoreErrors(this.refreshContent(true, showErrors)); refresher?.complete(); } @@ -153,7 +154,7 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, return; } - await CoreUtils.ignoreErrors(Promise.all([ + await CorePromiseUtils.ignoreErrors(Promise.all([ this.invalidateContent(), this.showCompletion ? CoreCourse.invalidateModule(this.module.id) : undefined, ])); @@ -315,7 +316,7 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, } if (refresh) { - await CoreUtils.ignoreErrors(CoreCourseModulePrefetchDelegate.invalidateCourseUpdates(this.courseId)); + await CorePromiseUtils.ignoreErrors(CoreCourseModulePrefetchDelegate.invalidateCourseUpdates(this.courseId)); } // Also, get the current status. @@ -420,7 +421,7 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, // @todo: Temporary fix to update course page completion. This should be refactored in MOBILE-4326. if (previousCompletion && module.completiondata && previousCompletion.state !== module.completiondata.state) { - await CoreUtils.ignoreErrors(CoreCourse.invalidateSections(this.courseId)); + await CorePromiseUtils.ignoreErrors(CoreCourse.invalidateSections(this.courseId)); CoreEvents.trigger(CoreEvents.COMPLETION_MODULE_VIEWED, { courseId: this.courseId, diff --git a/src/core/features/course/components/course-format/course-format.ts b/src/core/features/course/components/course-format/course-format.ts index 65ddf72f1bc..d0ef9749ff4 100644 --- a/src/core/features/course/components/course-format/course-format.ts +++ b/src/core/features/course/components/course-format/course-format.ts @@ -30,7 +30,6 @@ import { CoreDynamicComponent } from '@components/dynamic-component/dynamic-comp import { CoreCourseAnyCourseData } from '@features/courses/services/courses'; import { CoreCourse, - CoreCourseProvider, sectionContentIsModule, } from '@features/course/services/course'; import { @@ -41,7 +40,7 @@ import { import { CoreCourseFormatDelegate } from '@features/course/services/format-delegate'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { AccordionGroupChangeEventDetail, IonContent } from '@ionic/angular'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreCourseIndexSectionWithModule } from '../course-index/course-index'; import { CoreBlockHelper } from '@features/block/services/block-helper'; import { CoreNavigator } from '@services/navigator'; @@ -57,7 +56,12 @@ import { CoreModals } from '@services/modals'; import { CoreSharedModule } from '@/core/shared.module'; import { CoreBlockComponentsModule } from '@features/block/components/components.module'; import { CoreSites } from '@services/sites'; -import { COURSE_ALL_SECTIONS_PREFERRED_PREFIX, COURSE_EXPANDED_SECTIONS_PREFIX } from '@features/course/constants'; +import { + CORE_COURSE_ALL_SECTIONS_ID, + CORE_COURSE_ALL_SECTIONS_PREFERRED_PREFIX, + CORE_COURSE_EXPANDED_SECTIONS_PREFIX, + CORE_COURSE_STEALTH_MODULES_SECTION_ID, +} from '@features/course/constants'; import { toBoolean } from '@/core/transforms/boolean'; import { CoreInfiniteLoadingComponent } from '@components/infinite-loading/infinite-loading'; import { CoreSite } from '@classes/sites/site'; @@ -129,8 +133,8 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { selectedSection?: CoreCourseSectionToDisplay; previousSection?: CoreCourseSectionToDisplay; nextSection?: CoreCourseSectionToDisplay; - allSectionsId = CoreCourseProvider.ALL_SECTIONS_ID; - stealthModulesSectionId = CoreCourseProvider.STEALTH_MODULES_SECTION_ID; + allSectionsId = CORE_COURSE_ALL_SECTIONS_ID; + stealthModulesSectionId = CORE_COURSE_STEALTH_MODULES_SECTION_ID; loaded = false; lastModuleViewed?: CoreCourseViewedModulesDBRecord; viewedModules: Record = {}; @@ -299,7 +303,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { * @param sections Sections to treat. */ protected async treatSections(sections: CoreCourseSectionToDisplay[]): Promise { - const hasAllSections = sections[0].id === CoreCourseProvider.ALL_SECTIONS_ID; + const hasAllSections = sections[0].id === CORE_COURSE_ALL_SECTIONS_ID; const hasSeveralSections = sections.length > 2 || (sections.length === 2 && !hasAllSections); await this.initializeViewedModules(); @@ -735,7 +739,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { * @param firstLoad Whether it's the first load when opening the course. */ async logView(sectionNumber?: number, firstLoad = false): Promise { - await CoreUtils.ignoreErrors( + await CorePromiseUtils.ignoreErrors( CoreCourse.logView(this.course.id, sectionNumber), ); @@ -765,7 +769,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { * @param show Whether if all sections is preferred. */ protected async setAllSectionsPreferred(show: boolean): Promise { - await this.currentSite?.setLocalSiteConfig(`${COURSE_ALL_SECTIONS_PREFERRED_PREFIX}${this.course.id}`, show ? 1 : 0); + await this.currentSite?.setLocalSiteConfig(`${CORE_COURSE_ALL_SECTIONS_PREFERRED_PREFIX}${this.course.id}`, show ? 1 : 0); } /** @@ -775,7 +779,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { */ protected async isAllSectionsPreferred(): Promise { const showAllSections = - await this.currentSite?.getLocalSiteConfig(`${COURSE_ALL_SECTIONS_PREFERRED_PREFIX}${this.course.id}`, 0); + await this.currentSite?.getLocalSiteConfig(`${CORE_COURSE_ALL_SECTIONS_PREFERRED_PREFIX}${this.course.id}`, 0); return !!showAllSections; } @@ -788,7 +792,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { .filter((section) => section.expanded && section.id > 0).map((section) => section.id); await this.currentSite?.setLocalSiteConfig( - `${COURSE_EXPANDED_SECTIONS_PREFIX}${this.course.id}`, + `${CORE_COURSE_EXPANDED_SECTIONS_PREFIX}${this.course.id}`, expandedSections.join(','), ); } @@ -797,8 +801,8 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { * Initializes the expanded sections for the course. */ protected async initializeExpandedSections(): Promise { - const expandedSections = await CoreUtils.ignoreErrors( - this.currentSite?.getLocalSiteConfig(`${COURSE_EXPANDED_SECTIONS_PREFIX}${this.course.id}`), + const expandedSections = await CorePromiseUtils.ignoreErrors( + this.currentSite?.getLocalSiteConfig(`${CORE_COURSE_EXPANDED_SECTIONS_PREFIX}${this.course.id}`), ); if (expandedSections === undefined) { diff --git a/src/core/features/course/components/course-index-tour/course-index-tour.ts b/src/core/features/course/components/course-index-tour/course-index-tour.ts index bcf3041e8d8..7b941ef7c84 100644 --- a/src/core/features/course/components/course-index-tour/course-index-tour.ts +++ b/src/core/features/course/components/course-index-tour/course-index-tour.ts @@ -21,7 +21,7 @@ import { CoreUserTours } from '@features/usertours/services/user-tours'; @Component({ selector: 'core-course-course-index-tour', templateUrl: 'course-index-tour.html', - styleUrls: ['course-index-tour.scss'], + styleUrl: 'course-index-tour.scss', }) export class CoreCourseCourseIndexTourComponent { diff --git a/src/core/features/course/components/course-index/course-index.ts b/src/core/features/course/components/course-index/course-index.ts index b1c3c615062..0f5214155aa 100644 --- a/src/core/features/course/components/course-index/course-index.ts +++ b/src/core/features/course/components/course-index/course-index.ts @@ -16,8 +16,6 @@ import { CoreSharedModule } from '@/core/shared.module'; import { Component, ElementRef, Input, OnInit } from '@angular/core'; import { CoreCourse, - CoreCourseModuleCompletionStatus, - CoreCourseProvider, sectionContentIsModule, } from '@features/course/services/course'; import { CoreCourseHelper, CoreCourseModuleData, CoreCourseSection } from '@features/course/services/course-helper'; @@ -28,6 +26,7 @@ import { CoreSites } from '@services/sites'; import { CoreWait } from '@singletons/wait'; import { ModalController } from '@singletons'; import { CoreDom } from '@singletons/dom'; +import { CoreCourseModuleCompletionStatus, CORE_COURSE_ALL_SECTIONS_ID } from '@features/course/constants'; /** * Component to display course index modal. @@ -47,7 +46,7 @@ export class CoreCourseCourseIndexComponent implements OnInit { @Input() selectedId?: number; @Input() course?: CoreCourseAnyCourseData; - allSectionId = CoreCourseProvider.ALL_SECTIONS_ID; + allSectionId = CORE_COURSE_ALL_SECTIONS_ID; highlighted?: string; sectionsToRender: CourseIndexSection[] = []; loaded = false; diff --git a/src/core/features/course/components/course-section/course-section.ts b/src/core/features/course/components/course-section/course-section.ts index ff7968d84fe..a7155f0cf58 100644 --- a/src/core/features/course/components/course-section/course-section.ts +++ b/src/core/features/course/components/course-section/course-section.ts @@ -25,8 +25,9 @@ import { CoreCourseComponentsModule } from '../components.module'; import { toBoolean } from '@/core/transforms/boolean'; import { CoreCourseAnyCourseData } from '@features/courses/services/courses'; import { CoreCourseViewedModulesDBRecord } from '@features/course/services/database/course'; -import { CoreCourseModuleCompletionStatus, sectionContentIsModule } from '@features/course/services/course'; +import { sectionContentIsModule } from '@features/course/services/course'; import { CoreCourseFormatDelegate } from '@features/course/services/format-delegate'; +import { CoreCourseModuleCompletionStatus } from '@features/course/constants'; /** * Component to display course section. diff --git a/src/core/features/course/components/module-completion-details/module-completion-details.ts b/src/core/features/course/components/module-completion-details/module-completion-details.ts index ee72d2d7837..c48cf7e5e78 100644 --- a/src/core/features/course/components/module-completion-details/module-completion-details.ts +++ b/src/core/features/course/components/module-completion-details/module-completion-details.ts @@ -14,9 +14,9 @@ import { CoreSharedModule } from '@/core/shared.module'; import { Component, Input, OnInit } from '@angular/core'; +import { CoreCourseModuleCompletionStatus } from '@features/course/constants'; import { - CoreCourseModuleCompletionStatus, CoreCourseModuleWSRuleDetails, } from '@features/course/services/course'; import { CoreCourseModuleCompletionData } from '@features/course/services/course-helper'; diff --git a/src/core/features/course/components/module-completion-legacy/module-completion-legacy.ts b/src/core/features/course/components/module-completion-legacy/module-completion-legacy.ts index 38846e34b9d..af3f15aef07 100644 --- a/src/core/features/course/components/module-completion-legacy/module-completion-legacy.ts +++ b/src/core/features/course/components/module-completion-legacy/module-completion-legacy.ts @@ -18,7 +18,7 @@ import { CoreUser } from '@features/user/services/user'; import { CoreCourseModuleCompletionStatus, CoreCourseModuleCompletionTracking, -} from '@features/course/services/course'; +} from '@features/course/constants'; import { CoreFilterHelper } from '@features/filter/services/filter-helper'; import { Translate } from '@singletons'; import { CoreCourseModuleCompletionBaseComponent } from '@features/course/classes/module-completion'; @@ -39,7 +39,7 @@ import { ContextLevel } from '@/core/constants'; @Component({ selector: 'core-course-module-completion-legacy', templateUrl: 'core-course-module-completion-legacy.html', - styleUrls: ['module-completion-legacy.scss'], + styleUrl: 'module-completion-legacy.scss', }) export class CoreCourseModuleCompletionLegacyComponent extends CoreCourseModuleCompletionBaseComponent implements OnInit, OnDestroy { @@ -76,7 +76,7 @@ export class CoreCourseModuleCompletionLegacyComponent extends CoreCourseModuleC let langKey: string | undefined; let image: string | undefined; - if (this.completion.tracking === CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_MANUAL) { + if (this.completion.tracking === CoreCourseModuleCompletionTracking.MANUAL) { if (this.completion.state === CoreCourseModuleCompletionStatus.COMPLETION_INCOMPLETE) { image = 'completion-manual-n'; langKey = 'core.completion-alt-manual-n'; @@ -84,7 +84,7 @@ export class CoreCourseModuleCompletionLegacyComponent extends CoreCourseModuleC image = 'completion-manual-y'; langKey = 'core.completion-alt-manual-y'; } - } else if (this.completion.tracking === CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_AUTOMATIC) { + } else if (this.completion.tracking === CoreCourseModuleCompletionTracking.AUTOMATIC) { if (this.completion.state === CoreCourseModuleCompletionStatus.COMPLETION_INCOMPLETE) { image = 'completion-auto-n'; langKey = 'core.completion-alt-auto-n'; diff --git a/src/core/features/course/components/module-completion/module-completion.ts b/src/core/features/course/components/module-completion/module-completion.ts index ac98c2dc235..55689504f6e 100644 --- a/src/core/features/course/components/module-completion/module-completion.ts +++ b/src/core/features/course/components/module-completion/module-completion.ts @@ -15,9 +15,7 @@ import { Component, Input, OnChanges, OnDestroy, OnInit, SimpleChange } from '@angular/core'; import { CoreCourseModuleCompletionBaseComponent } from '@features/course/classes/module-completion'; -import { - CoreCourseModuleCompletionStatus, -} from '@features/course/services/course'; +import { CoreCourseModuleCompletionStatus } from '@features/course/constants'; import { CorePopovers } from '@services/popovers'; import { CoreCourseHelper } from '@features/course/services/course-helper'; import { CoreUser } from '@features/user/services/user'; @@ -37,7 +35,7 @@ import { toBoolean } from '@/core/transforms/boolean'; @Component({ selector: 'core-course-module-completion', templateUrl: 'core-course-module-completion.html', - styleUrls: ['module-completion.scss'], + styleUrl: 'module-completion.scss', }) export class CoreCourseModuleCompletionComponent extends CoreCourseModuleCompletionBaseComponent diff --git a/src/core/features/course/components/module-info/module-info.ts b/src/core/features/course/components/module-info/module-info.ts index fee40aba2ed..9f1ed30ff06 100644 --- a/src/core/features/course/components/module-info/module-info.ts +++ b/src/core/features/course/components/module-info/module-info.ts @@ -32,7 +32,7 @@ import { CoreSites } from '@services/sites'; @Component({ selector: 'core-course-module-info', templateUrl: 'core-course-module-info.html', - styleUrls: ['course-module-info.scss'], + styleUrl: 'course-module-info.scss', }) export class CoreCourseModuleInfoComponent implements OnInit { diff --git a/src/core/features/course/components/module-navigation/module-navigation.ts b/src/core/features/course/components/module-navigation/module-navigation.ts index 75aa690890b..d831d2fd207 100644 --- a/src/core/features/course/components/module-navigation/module-navigation.ts +++ b/src/core/features/course/components/module-navigation/module-navigation.ts @@ -21,7 +21,7 @@ import { CoreLoadings } from '@services/loadings'; import { CoreNavigationOptions, CoreNavigator } from '@services/navigator'; import { CoreSites, CoreSitesReadingStrategy } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; /** @@ -33,7 +33,7 @@ import { CoreEventObserver, CoreEvents } from '@singletons/events'; @Component({ selector: 'core-course-module-navigation', templateUrl: 'core-course-module-navigation.html', - styleUrls: ['module-navigation.scss'], + styleUrl: 'module-navigation.scss', }) export class CoreCourseModuleNavigationComponent implements OnInit, OnDestroy { @@ -170,7 +170,7 @@ export class CoreCourseModuleNavigationComponent implements OnInit, OnDestroy { const modal = await CoreLoadings.show(); // Re-calculate module in case a new module was made visible. - await CoreUtils.ignoreErrors(this.setNextAndPreviousModules(CoreSitesReadingStrategy.PREFER_NETWORK, next, !next)); + await CorePromiseUtils.ignoreErrors(this.setNextAndPreviousModules(CoreSitesReadingStrategy.PREFER_NETWORK, next, !next)); modal.dismiss(); diff --git a/src/core/features/course/components/module-summary/module-summary.ts b/src/core/features/course/components/module-summary/module-summary.ts index f0416024170..b9a32df7e95 100644 --- a/src/core/features/course/components/module-summary/module-summary.ts +++ b/src/core/features/course/components/module-summary/module-summary.ts @@ -29,12 +29,13 @@ import { CoreNavigator } from '@services/navigator'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreText } from '@singletons/text'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { ModalController, NgZone } from '@singletons'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { Subscription } from 'rxjs'; import { CoreSharedModule } from '@/core/shared.module'; import { toBoolean } from '@/core/transforms/boolean'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Component to display a module summary modal. @@ -274,7 +275,7 @@ export class CoreCourseModuleSummaryComponent implements OnInit, OnDestroy { protected async fetchCourse(): Promise { this.course = await CoreCourseHelper.getCourseInfo(this.courseId); - this.isTeacher = await CoreUtils.ignoreErrors(CoreCourseHelper.guessIsTeacher(this.courseId, this.course), false); + this.isTeacher = await CorePromiseUtils.ignoreErrors(CoreCourseHelper.guessIsTeacher(this.courseId, this.course), false); } /** diff --git a/src/core/features/course/components/module/module.ts b/src/core/features/course/components/module/module.ts index 4f1063fb541..78dff998bfb 100644 --- a/src/core/features/course/components/module/module.ts +++ b/src/core/features/course/components/module/module.ts @@ -42,7 +42,7 @@ import { toBoolean } from '@/core/transforms/boolean'; @Component({ selector: 'core-course-module', templateUrl: 'core-course-module.html', - styleUrls: ['module.scss'], + styleUrl: 'module.scss', }) export class CoreCourseModuleComponent implements OnInit, OnDestroy { diff --git a/src/core/features/course/components/unsupported-module/unsupported-module.ts b/src/core/features/course/components/unsupported-module/unsupported-module.ts index f690a4674c0..8ed30c1da4a 100644 --- a/src/core/features/course/components/unsupported-module/unsupported-module.ts +++ b/src/core/features/course/components/unsupported-module/unsupported-module.ts @@ -23,7 +23,7 @@ import { CoreCourseModuleDelegate } from '@features/course/services/module-deleg @Component({ selector: 'core-course-unsupported-module', templateUrl: 'core-course-unsupported-module.html', - styleUrls: ['unsupported-module.scss'], + styleUrl: 'unsupported-module.scss', }) export class CoreCourseUnsupportedModuleComponent implements OnInit { diff --git a/src/core/features/course/constants.ts b/src/core/features/course/constants.ts index aecd60ad636..9e62a812872 100644 --- a/src/core/features/course/constants.ts +++ b/src/core/features/course/constants.ts @@ -12,10 +12,52 @@ // See the License for the specific language governing permissions and // limitations under the License. -export const COURSE_INDEX_PATH = ':courseId'; -export const COURSE_PAGE_NAME = 'course'; -export const CONTENTS_PAGE_NAME = 'contents'; -export const COURSE_CONTENTS_PATH = `${COURSE_PAGE_NAME}/${COURSE_INDEX_PATH}/${CONTENTS_PAGE_NAME}`; +export const CORE_COURSE_INDEX_PATH = ':courseId'; +export const CORE_COURSE_PAGE_NAME = 'course'; +export const CORE_COURSE_CONTENTS_PAGE_NAME = 'contents'; +export const CORE_COURSE_CONTENTS_PATH = `${CORE_COURSE_PAGE_NAME}/${CORE_COURSE_INDEX_PATH}/${CORE_COURSE_CONTENTS_PAGE_NAME}`; -export const COURSE_ALL_SECTIONS_PREFERRED_PREFIX = 'CoreCourseFormatAllSectionsPreferred-'; -export const COURSE_EXPANDED_SECTIONS_PREFIX = 'CoreCourseFormatExpandedSections-'; +export const CORE_COURSE_ALL_SECTIONS_PREFERRED_PREFIX = 'CoreCourseFormatAllSectionsPreferred-'; +export const CORE_COURSE_EXPANDED_SECTIONS_PREFIX = 'CoreCourseFormatExpandedSections-'; + +export const CORE_COURSE_ALL_SECTIONS_ID = -2; +export const CORE_COURSE_STEALTH_MODULES_SECTION_ID = -1; +export const CORE_COURSE_ALL_COURSES_CLEARED = -1; + +export const CORE_COURSE_PROGRESS_UPDATED_EVENT = 'progress_updated'; + +export const CORE_COURSE_AUTO_SYNCED = 'core_course_autom_synced'; + +export const CORE_COURSE_COMPONENT = 'CoreCourse'; + +export const CORE_COURSE_CORE_MODULES = [ + 'assign', 'bigbluebuttonbn', 'book', 'chat', 'choice', 'data', 'feedback', 'folder', 'forum', 'glossary', 'h5pactivity', + 'imscp', 'label', 'lesson', 'lti', 'page', 'quiz', 'resource', 'scorm', 'survey', 'url', 'wiki', 'workshop', +]; + +/** + * Course Module completion status enumeration. + */ +export const enum CoreCourseModuleCompletionStatus { + COMPLETION_INCOMPLETE = 0, + COMPLETION_COMPLETE = 1, + COMPLETION_COMPLETE_PASS = 2, + COMPLETION_COMPLETE_FAIL = 3, +} + +/** + * Completion tracking valid values. + */ +export const enum CoreCourseModuleCompletionTracking { + NONE = 0, + MANUAL = 1, + AUTOMATIC = 2, +} + +export const CoreCourseAccessDataType = { + ACCESS_GUEST: 'courses_access_guest', // eslint-disable-line @typescript-eslint/naming-convention + ACCESS_DEFAULT: 'courses_access_default', // eslint-disable-line @typescript-eslint/naming-convention +} as const; + +// eslint-disable-next-line @typescript-eslint/no-redeclare +export type CoreCourseAccessDataType = typeof CoreCourseAccessDataType[keyof typeof CoreCourseAccessDataType]; diff --git a/src/core/features/course/course-contents-lazy.module.ts b/src/core/features/course/course-contents-lazy.module.ts index 0d1d84b7467..d64416bda57 100644 --- a/src/core/features/course/course-contents-lazy.module.ts +++ b/src/core/features/course/course-contents-lazy.module.ts @@ -53,4 +53,4 @@ function buildRoutes(injector: Injector): Routes { CoreCourseContentsPage, ], }) -export class CoreCourseContentsLazyModule {} +export default class CoreCourseContentsLazyModule {} diff --git a/src/core/features/course/course-lazy.module.ts b/src/core/features/course/course-lazy.module.ts index e77e5637f06..18426ba7e7d 100644 --- a/src/core/features/course/course-lazy.module.ts +++ b/src/core/features/course/course-lazy.module.ts @@ -22,7 +22,7 @@ import { CoreCourseIndexPage } from '@features/course/pages/index'; import { CoreCourseListModTypePage } from '@features/course/pages/list-mod-type/list-mod-type'; import { CoreCourseModulePreviewPage } from '@features/course/pages/module-preview/module-preview'; import { CoreCourseHelper } from './services/course-helper'; -import { COURSE_INDEX_PATH } from './constants'; +import { CORE_COURSE_INDEX_PATH } from './constants'; /** * Build module routes. @@ -35,7 +35,7 @@ function buildRoutes(injector: Injector): Routes { return [ { - path: COURSE_INDEX_PATH, + path: CORE_COURSE_INDEX_PATH, children: [ { path: '', @@ -83,4 +83,4 @@ function buildRoutes(injector: Injector): Routes { }, ], }) -export class CoreCourseLazyModule {} +export default class CoreCourseLazyModule {} diff --git a/src/core/features/course/course-summary-lazy.module.ts b/src/core/features/course/course-summary-lazy.module.ts index ab2c5d61710..fc892150613 100644 --- a/src/core/features/course/course-summary-lazy.module.ts +++ b/src/core/features/course/course-summary-lazy.module.ts @@ -35,4 +35,4 @@ const routes: Routes = [ CoreCourseSummaryPageModule, ], }) -export class CoreCourseSummaryLazyModule {} +export default class CoreCourseSummaryLazyModule {} diff --git a/src/core/features/course/course.module.ts b/src/core/features/course/course.module.ts index 1cd36f03a1f..5e89c4b8a54 100644 --- a/src/core/features/course/course.module.ts +++ b/src/core/features/course/course.module.ts @@ -31,7 +31,7 @@ import { CoreCourseModulesTagAreaHandler } from './services/handlers/modules-tag import { CoreCourse } from './services/course'; import { buildRegExpUrlMatcher } from '@/app/app-routing.module'; import { CoreCourseIndexRoutingModule } from '@features/course/course-routing.module'; -import { COURSE_PAGE_NAME, CONTENTS_PAGE_NAME } from './constants'; +import { CORE_COURSE_PAGE_NAME, CORE_COURSE_CONTENTS_PAGE_NAME } from './constants'; /** * Get course services. @@ -70,7 +70,15 @@ export async function getCourseServices(): Promise[]> { export async function getCourseExportedObjects(): Promise> { const { CoreCourseActivityPrefetchHandlerBase } = await import('@features/course/classes/activity-prefetch-handler'); const { CoreCourseResourcePrefetchHandlerBase } = await import('@features/course/classes/resource-prefetch-handler'); - const { CoreCourseAccessDataType } = await import('@features/course/services/course'); + const { + CoreCourseAccessDataType, + CORE_COURSE_ALL_SECTIONS_ID, + CORE_COURSE_STEALTH_MODULES_SECTION_ID, + CORE_COURSE_ALL_COURSES_CLEARED, + CORE_COURSE_PROGRESS_UPDATED_EVENT, + CORE_COURSE_COMPONENT, + CORE_COURSE_CORE_MODULES, + } = await import('@features/course/constants'); const { CoreCourseUnsupportedModuleComponent } = await import ('@features/course/components/unsupported-module/unsupported-module'); const { CoreCourseFormatSingleActivityComponent } = @@ -83,6 +91,12 @@ export async function getCourseExportedObjects(): Promise[]> const routes: Routes = [ { - matcher: buildRegExpUrlMatcher(new RegExp(`^${COURSE_PAGE_NAME}(/deep)*`)), - loadChildren: () => import('@features/course/course-lazy.module').then(m => m.CoreCourseLazyModule), + matcher: buildRegExpUrlMatcher(new RegExp(`^${CORE_COURSE_PAGE_NAME}(/deep)*`)), + loadChildren: () => import('@features/course/course-lazy.module'), }, ]; const courseIndexRoutes: Routes = [ { - path: CONTENTS_PAGE_NAME, - loadChildren: () => import('@features/course/course-contents-lazy.module').then(m => m.CoreCourseContentsLazyModule), + path: CORE_COURSE_CONTENTS_PAGE_NAME, + loadChildren: () => import('@features/course/course-contents-lazy.module'), }, ]; diff --git a/src/core/features/course/directives/download-module-main-file.ts b/src/core/features/course/directives/download-module-main-file.ts index 603be67489e..82f95acab5b 100644 --- a/src/core/features/course/directives/download-module-main-file.ts +++ b/src/core/features/course/directives/download-module-main-file.ts @@ -17,8 +17,8 @@ import { Directive, Input, OnInit, ElementRef } from '@angular/core'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreCourse, CoreCourseModuleContentFile } from '@features/course/services/course'; import { CoreCourseHelper, CoreCourseModuleData } from '@features/course/services/course-helper'; -import { CoreUtilsOpenFileOptions } from '@services/utils/utils'; import { CoreLoadings } from '@services/loadings'; +import { CoreOpenerOpenFileOptions } from '@singletons/opener'; /** * Directive to allow downloading and open the main file of a module. @@ -38,7 +38,7 @@ export class CoreCourseDownloadModuleMainFileDirective implements OnInit { @Input() component?: string; // Component to link the file to. @Input() componentId?: string | number; // Component ID to use in conjunction with the component. If not defined, use moduleId. @Input() files?: CoreCourseModuleContentFile[]; // List of files of the module. If not provided, use module.contents. - @Input() options?: CoreUtilsOpenFileOptions = {}; + @Input() options?: CoreOpenerOpenFileOptions = {}; protected element: HTMLElement; diff --git a/src/core/features/course/format/singleactivity/components/singleactivity.ts b/src/core/features/course/format/singleactivity/components/singleactivity.ts index 0ee615b82b3..3d198a6134f 100644 --- a/src/core/features/course/format/singleactivity/components/singleactivity.ts +++ b/src/core/features/course/format/singleactivity/components/singleactivity.ts @@ -30,7 +30,7 @@ import type { CoreCourseModuleMainActivityComponent } from '@features/course/cla @Component({ selector: 'core-course-format-single-activity', templateUrl: 'core-course-format-single-activity.html', - styleUrls: ['single-activity.scss'], + styleUrl: 'single-activity.scss', }) export class CoreCourseFormatSingleActivityComponent implements OnChanges { diff --git a/src/core/features/course/pages/contents/contents.ts b/src/core/features/course/pages/contents/contents.ts index 99148c0e9bd..958ecd0f1ac 100644 --- a/src/core/features/course/pages/contents/contents.ts +++ b/src/core/features/course/pages/contents/contents.ts @@ -16,13 +16,11 @@ import { Component, ViewChild, OnInit, OnDestroy, forwardRef, ChangeDetectorRef import { IonContent } from '@ionic/angular'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { CoreCourses, CoreCourseAnyCourseData } from '@features/courses/services/courses'; import { CoreCourse, CoreCourseCompletionActivityStatus, - CoreCourseModuleCompletionStatus, - CoreCourseProvider, } from '@features/course/services/course'; import { CoreCourseHelper, @@ -32,7 +30,7 @@ import { } from '@features/course/services/course-helper'; import { CoreCourseFormatDelegate } from '@features/course/services/format-delegate'; import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate'; -import { CoreCourseSync, CoreCourseSyncProvider } from '@features/course/services/sync'; +import { CoreCourseSync } from '@features/course/services/sync'; import { CoreCourseFormatComponent } from '../../components/course-format/course-format'; import { CoreEvents, @@ -43,6 +41,13 @@ import { CoreRefreshContext, CORE_REFRESH_CONTEXT } from '@/core/utils/refresh-c import { CoreCoursesHelper } from '@features/courses/services/courses-helper'; import { CoreSites } from '@services/sites'; import { CoreWait } from '@singletons/wait'; +import { + CoreCourseModuleCompletionStatus, + CORE_COURSE_AUTO_SYNCED, + CORE_COURSE_PROGRESS_UPDATED_EVENT, +} from '@features/course/constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; +import { CoreObject } from '@singletons/object'; /** * Page that displays the contents of a course. @@ -105,9 +110,9 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy, CoreRefreshCon this.debouncedUpdateCachedCompletion = CoreUtils.debounce(() => { if (this.modulesHaveCompletion) { - CoreUtils.ignoreErrors(CoreCourse.getSections(this.course.id, false, true)); + CorePromiseUtils.ignoreErrors(CoreCourse.getSections(this.course.id, false, true)); } else { - CoreUtils.ignoreErrors(CoreCourse.getActivitiesCompletionStatus( + CorePromiseUtils.ignoreErrors(CoreCourse.getActivitiesCompletionStatus( this.course.id, undefined, undefined, @@ -154,7 +159,7 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy, CoreRefreshCon this.onCompletionChange(data.completion); }); - this.syncObserver = CoreEvents.on(CoreCourseSyncProvider.AUTO_SYNCED, (data) => { + this.syncObserver = CoreEvents.on(CORE_COURSE_AUTO_SYNCED, (data) => { if (!data || data.courseId != this.course.id) { return; } @@ -176,7 +181,7 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy, CoreRefreshCon */ protected async loadData(refresh?: boolean, sync?: boolean): Promise { // First of all, get the course because the data might have changed. - const result = await CoreUtils.ignoreErrors(CoreCourseHelper.getCourse(this.course.id)); + const result = await CorePromiseUtils.ignoreErrors(CoreCourseHelper.getCourse(this.course.id)); if (result) { if (this.course.id === result.course.id && 'displayname' in this.course && !('displayname' in result.course)) { @@ -188,7 +193,7 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy, CoreRefreshCon if (sync) { // Try to synchronize the course data. // For now we don't allow manual syncing, so ignore errors. - const result = await CoreUtils.ignoreErrors(CoreCourseSync.syncCourse( + const result = await CorePromiseUtils.ignoreErrors(CoreCourseSync.syncCourse( this.course.id, this.course.displayname || this.course.fullname, )); @@ -237,9 +242,9 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy, CoreRefreshCon // The module already has completion (3.6 onwards). Load the offline completion. this.modulesHaveCompletion = true; - await CoreUtils.ignoreErrors(CoreCourseHelper.loadOfflineCompletion(this.course.id, sections)); + await CorePromiseUtils.ignoreErrors(CoreCourseHelper.loadOfflineCompletion(this.course.id, sections)); } else { - const fetchedData = await CoreUtils.ignoreErrors( + const fetchedData = await CorePromiseUtils.ignoreErrors( CoreCourse.getActivitiesCompletionStatus(this.course.id), ); @@ -280,17 +285,17 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy, CoreRefreshCon if ('courseformatoptions' in this.course && this.course.courseformatoptions) { // Already loaded. - this.formatOptions = CoreUtils.objectToKeyValueMap(this.course.courseformatoptions, 'name', 'value'); + this.formatOptions = CoreObject.toKeyValueMap(this.course.courseformatoptions, 'name', 'value'); return; } - const course = await CoreUtils.ignoreErrors(CoreCourses.getCourseByField('id', this.course.id)); + const course = await CorePromiseUtils.ignoreErrors(CoreCourses.getCourseByField('id', this.course.id)); course && Object.assign(this.course, course); if (course?.courseformatoptions) { - this.formatOptions = CoreUtils.objectToKeyValueMap(course.courseformatoptions, 'name', 'value'); + this.formatOptions = CoreObject.toKeyValueMap(course.courseformatoptions, 'name', 'value'); } } @@ -301,7 +306,7 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy, CoreRefreshCon * @returns Promise resolved when done. */ async doRefresh(refresher?: HTMLIonRefresherElement): Promise { - await CoreUtils.ignoreErrors(this.invalidateData()); + await CorePromiseUtils.ignoreErrors(this.invalidateData()); try { await this.loadData(true, true); @@ -309,7 +314,7 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy, CoreRefreshCon // Do not call doRefresh on the format component if the refresher is defined in the format component // to prevent an infinite loop. if (this.displayRefresher && this.formatComponent) { - await CoreUtils.ignoreErrors(this.formatComponent.doRefresh(refresher)); + await CorePromiseUtils.ignoreErrors(this.formatComponent.doRefresh(refresher)); } refresher?.complete(); @@ -351,10 +356,10 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy, CoreRefreshCon } } - await CoreUtils.ignoreErrors(this.invalidateData()); + await CorePromiseUtils.ignoreErrors(this.invalidateData()); this.debouncedUpdateCachedCompletion?.(); } else { - await CoreUtils.ignoreErrors(this.invalidateData()); + await CorePromiseUtils.ignoreErrors(this.invalidateData()); await this.showLoadingAndRefresh(true, false); } @@ -362,7 +367,7 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy, CoreRefreshCon return; } - CoreEvents.trigger(CoreCourseProvider.PROGRESS_UPDATED, { + CoreEvents.trigger(CORE_COURSE_PROGRESS_UPDATED_EVENT, { courseId: this.course.id, progress: this.course.progress, }, siteId); } @@ -395,7 +400,7 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy, CoreRefreshCon */ protected async showLoadingAndRefresh(sync = false, invalidateData = true): Promise { // Try to keep current scroll position. - const scrollElement = await CoreUtils.ignoreErrors(this.content?.getScrollElement()); + const scrollElement = await CorePromiseUtils.ignoreErrors(this.content?.getScrollElement()); const scrollTop = scrollElement?.scrollTop ?? -1; this.updatingData = true; @@ -403,7 +408,7 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy, CoreRefreshCon try { if (invalidateData) { - await CoreUtils.ignoreErrors(this.invalidateData()); + await CorePromiseUtils.ignoreErrors(this.invalidateData()); } await this.loadData(true, sync); diff --git a/src/core/features/course/pages/course-summary/course-summary.page.ts b/src/core/features/course/pages/course-summary/course-summary.page.ts index 0f7145a5f47..4585d7cb6f2 100644 --- a/src/core/features/course/pages/course-summary/course-summary.page.ts +++ b/src/core/features/course/pages/course-summary/course-summary.page.ts @@ -21,7 +21,6 @@ import { CoreCourseCustomField, CoreCourses, CoreCourseSearchedData, - CoreCoursesProvider, CoreEnrolledCourseData, } from '@features/courses/services/courses'; import { @@ -31,7 +30,7 @@ import { import { CoreCourseHelper } from '@features/course/services/course-helper'; import { ActionSheetController, ModalController, NgZone, Translate } from '@singletons'; import { CoreNavigator } from '@services/navigator'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreCoursesHelper, CoreCourseWithImageAndColor } from '@features/courses/services/courses-helper'; import { Subscription } from 'rxjs'; import { CoreColors } from '@singletons/colors'; @@ -42,6 +41,7 @@ import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics'; import { CoreEnrolHelper } from '@features/enrol/services/enrol-helper'; import { CoreEnrolDelegate } from '@features/enrol/services/enrol-delegate'; import { CoreEnrol, CoreEnrolEnrolmentMethod } from '@features/enrol/services/enrol'; +import { CORE_COURSES_MY_COURSES_UPDATED_EVENT, CoreCoursesMyCoursesUpdatedEventAction } from '@features/courses/constants'; /** * Page that shows the summary of a course including buttons to enrol and other available options. @@ -49,7 +49,7 @@ import { CoreEnrol, CoreEnrolEnrolmentMethod } from '@features/enrol/services/en @Component({ selector: 'page-core-course-summary', templateUrl: 'course-summary.html', - styleUrls: ['course-summary.scss'], + styleUrl: 'course-summary.scss', }) export class CoreCourseSummaryPage implements OnInit, OnDestroy { @@ -174,7 +174,7 @@ export class CoreCourseSummaryPage implements OnInit, OnDestroy { await this.loadMenuHandlers(refresh); // After loading menu handlers, admOptions should be available. - this.isTeacher = await CoreUtils.ignoreErrors(CoreCourseHelper.guessIsTeacher(this.courseId, this.course), false); + this.isTeacher = await CorePromiseUtils.ignoreErrors(CoreCourseHelper.guessIsTeacher(this.courseId, this.course), false); this.dataLoaded = true; } @@ -204,7 +204,7 @@ export class CoreCourseSummaryPage implements OnInit, OnDestroy { // Ignore errors. } - const courseByField = await CoreUtils.ignoreErrors(CoreCourses.getCourseByField('id', this.courseId)); + const courseByField = await CorePromiseUtils.ignoreErrors(CoreCourses.getCourseByField('id', this.courseId)); if (courseByField) { if (this.course) { this.course.customfields = courseByField.customfields; @@ -391,10 +391,10 @@ export class CoreCourseSummaryPage implements OnInit, OnDestroy { await this.refreshData().finally(() => { // My courses have been updated, trigger event. - CoreEvents.trigger(CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, { + CoreEvents.trigger(CORE_COURSES_MY_COURSES_UPDATED_EVENT, { courseId: this.courseId, course: this.course, - action: CoreCoursesProvider.ACTION_ENROL, + action: CoreCoursesMyCoursesUpdatedEventAction.ENROL, }, CoreSites.getCurrentSiteId()); }); @@ -440,7 +440,7 @@ export class CoreCourseSummaryPage implements OnInit, OnDestroy { } // Check if user is enrolled in the course. - await CoreUtils.ignoreErrors(CoreCourses.invalidateUserCourses()); + await CorePromiseUtils.ignoreErrors(CoreCourses.invalidateUserCourses()); try { await CoreCourses.getUserCourse(this.courseId); diff --git a/src/core/features/course/pages/index/index.ts b/src/core/features/course/pages/index/index.ts index 16955652f02..99a3a1c62ac 100644 --- a/src/core/features/course/pages/index/index.ts +++ b/src/core/features/course/pages/index/index.ts @@ -20,11 +20,11 @@ import { CoreCourseFormatDelegate } from '../../services/format-delegate'; import { CoreCourseOptionsDelegate } from '../../services/course-options-delegate'; import { CoreCourseAnyCourseData } from '@features/courses/services/courses'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; -import { CoreCourse, CoreCourseProvider, CoreCourseWSSection } from '@features/course/services/course'; +import { CoreCourse, CoreCourseWSSection } from '@features/course/services/course'; import { CoreCourseHelper, CoreCourseModuleData } from '@features/course/services/course-helper'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreNavigationOptions, CoreNavigator } from '@services/navigator'; -import { CONTENTS_PAGE_NAME } from '@features/course/constants'; +import { CORE_COURSE_CONTENTS_PAGE_NAME, CORE_COURSE_PROGRESS_UPDATED_EVENT } from '@features/course/constants'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreCoursesHelper, CoreCourseWithImageAndColor } from '@features/courses/services/courses-helper'; import { CoreColors } from '@singletons/colors'; @@ -38,7 +38,7 @@ import { CoreWait } from '@singletons/wait'; @Component({ selector: 'page-core-course-index', templateUrl: 'index.html', - styleUrls: ['index.scss'], + styleUrl: 'index.scss', }) export class CoreCourseIndexPage implements OnInit, OnDestroy { @@ -64,7 +64,7 @@ export class CoreCourseIndexPage implements OnInit, OnDestroy { protected isGuest = false; protected openModule = true; protected contentsTab: CoreTabsOutletTab & { pageParams: Params } = { - page: CONTENTS_PAGE_NAME, + page: CORE_COURSE_CONTENTS_PAGE_NAME, title: 'core.course', pageParams: {}, }; @@ -93,7 +93,7 @@ export class CoreCourseIndexPage implements OnInit, OnDestroy { const siteId = CoreSites.getCurrentSiteId(); - this.progressObserver = CoreEvents.on(CoreCourseProvider.PROGRESS_UPDATED, (data) => { + this.progressObserver = CoreEvents.on(CORE_COURSE_PROGRESS_UPDATED_EVENT, (data) => { if (!this.course || this.course.id !== data.courseId || !('progress' in this.course)) { return; } @@ -234,7 +234,7 @@ export class CoreCourseIndexPage implements OnInit, OnDestroy { this.updateProgress(); // Load sections. - this.sections = await CoreUtils.ignoreErrors(CoreCourse.getSections(this.course.id, false, true), []); + this.sections = await CorePromiseUtils.ignoreErrors(CoreCourse.getSections(this.course.id, false, true), []); if (!this.sections) { return; diff --git a/src/core/features/course/pages/list-mod-type/list-mod-type.ts b/src/core/features/course/pages/list-mod-type/list-mod-type.ts index d4626e320c4..5cdad52e01e 100644 --- a/src/core/features/course/pages/list-mod-type/list-mod-type.ts +++ b/src/core/features/course/pages/list-mod-type/list-mod-type.ts @@ -20,7 +20,7 @@ import { CoreCourseModuleDelegate } from '@features/course/services/module-deleg import { CoreCourseHelper, CoreCourseSection } from '@features/course/services/course-helper'; import { CoreNavigator } from '@services/navigator'; import { CoreConstants } from '@/core/constants'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreTime } from '@singletons/time'; import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics'; @@ -190,7 +190,7 @@ export class CoreCourseListModTypePage implements OnInit { * @param refresher Refresher. */ async refreshData(refresher: HTMLIonRefresherElement): Promise { - await CoreUtils.ignoreErrors(CoreCourse.invalidateSections(this.courseId)); + await CorePromiseUtils.ignoreErrors(CoreCourse.invalidateSections(this.courseId)); try { await this.fetchData(); diff --git a/src/core/features/course/pages/module-preview/module-preview.ts b/src/core/features/course/pages/module-preview/module-preview.ts index c78607e06e5..b310da7b435 100644 --- a/src/core/features/course/pages/module-preview/module-preview.ts +++ b/src/core/features/course/pages/module-preview/module-preview.ts @@ -21,7 +21,7 @@ import { CoreModals } from '@services/modals'; import { CoreNavigator } from '@services/navigator'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; /** * Page that displays a module preview. @@ -29,7 +29,7 @@ import { CoreUtils } from '@services/utils/utils'; @Component({ selector: 'page-core-course-module-preview', templateUrl: 'module-preview.html', - styleUrls: ['module-preview.scss'], + styleUrl: 'module-preview.scss', }) export class CoreCourseModulePreviewPage implements OnInit { diff --git a/src/core/features/course/services/course-helper.ts b/src/core/features/course/services/course-helper.ts index ff525b53a33..16f50b174c7 100644 --- a/src/core/features/course/services/course-helper.ts +++ b/src/core/features/course/services/course-helper.ts @@ -22,10 +22,7 @@ import { CoreCourseCompletionActivityStatus, CoreCourseModuleWSCompletionData, CoreCourseModuleContentFile, - CoreCourseProvider, CoreCourseWSSection, - CoreCourseModuleCompletionTracking, - CoreCourseModuleCompletionStatus, CoreCourseGetContentsWSModule, sectionContentIsModule, CoreCourseAnyModuleData, @@ -35,7 +32,7 @@ import { CoreLogger } from '@singletons/logger'; import { ApplicationInit, makeSingleton, Translate } from '@singletons'; import { CoreFilepool } from '@services/filepool'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils, CoreUtilsOpenFileOptions } from '@services/utils/utils'; +import { CoreArray } from '@singletons/array'; import { CoreCourseAnyCourseData, CoreCourseBasicData, @@ -78,6 +75,15 @@ import { CoreEnrolAction, CoreEnrolDelegate } from '@features/enrol/services/enr import { LazyRoutesModule } from '@/app/app-routing.module'; import { CoreModals } from '@services/modals'; import { CoreLoadings } from '@services/loadings'; +import { + CoreCourseModuleCompletionTracking, + CoreCourseModuleCompletionStatus, + CORE_COURSE_ALL_SECTIONS_ID, + CORE_COURSE_STEALTH_MODULES_SECTION_ID, + CORE_COURSE_COMPONENT, +} from '../constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; +import { CoreOpener, CoreOpenerOpenFileOptions } from '@singletons/opener'; /** * Prefetch info of a module. @@ -255,7 +261,7 @@ export class CoreCourseHelperProvider { * @returns Wether section is stealth (accessible but not visible to students). */ isSectionStealth(section: CoreCourseWSSection): boolean { - return section.hiddenbynumsections === 1 || section.id === CoreCourseProvider.STEALTH_MODULES_SECTION_ID; + return section.hiddenbynumsections === 1 || section.id === CORE_COURSE_STEALTH_MODULES_SECTION_ID; } /** @@ -283,7 +289,7 @@ export class CoreCourseHelperProvider { refresh?: boolean, checkUpdates: boolean = true, ): Promise<{statusData: CoreCourseModulesStatus; section: CoreCourseSectionWithStatus}> { - if (section.id === CoreCourseProvider.ALL_SECTIONS_ID) { + if (section.id === CORE_COURSE_ALL_SECTIONS_ID) { throw new CoreError('Invalid section'); } @@ -439,7 +445,7 @@ export class CoreCourseHelperProvider { options.onProgress({ count: 0, total: total, success: true }); } - return CoreUtils.allPromises(promises); + return CorePromiseUtils.allPromises(promises); } /** @@ -462,7 +468,7 @@ export class CoreCourseHelperProvider { }; const getSectionSize = async (section: CoreCourseWSSection): Promise => { - if (section.id === CoreCourseProvider.ALL_SECTIONS_ID) { + if (section.id === CORE_COURSE_ALL_SECTIONS_ID) { return { size: 0, total: true }; } @@ -579,7 +585,7 @@ export class CoreCourseHelperProvider { */ createAllSectionsSection(): CoreCourseSection { return { - id: CoreCourseProvider.ALL_SECTIONS_ID, + id: CORE_COURSE_ALL_SECTIONS_ID, name: Translate.instant('core.course.allsections'), hasContent: true, summary: '', @@ -635,7 +641,7 @@ export class CoreCourseHelperProvider { componentId?: string | number, files?: CoreCourseModuleContentFile[], siteId?: string, - options: CoreUtilsOpenFileOptions = {}, + options: CoreOpenerOpenFileOptions = {}, ): Promise { siteId = siteId || CoreSites.getCurrentSiteId(); @@ -673,7 +679,7 @@ export class CoreCourseHelperProvider { ); if (CoreUrl.isLocalFileUrl(result.path)) { - return CoreUtils.openFile(result.path, options); + return CoreOpener.openFile(result.path, options); } /* In iOS, if we use the same URL in embedded browser and background download then the download only @@ -681,7 +687,7 @@ export class CoreCourseHelperProvider { result.path = result.path + '#moodlemobile-embedded'; try { - await CoreUtils.openOnlineFile(result.path); + await CoreOpener.openOnlineFile(result.path); } catch (error) { // Error opening the file, some apps don't allow opening online files. if (!CoreFile.isAvailable()) { @@ -701,7 +707,7 @@ export class CoreCourseHelperProvider { path = await CoreFilepool.getInternalUrlByUrl(siteId, mainFile.fileurl); } - await CoreUtils.openFile(path, options); + await CoreOpener.openFile(path, options); } } @@ -726,7 +732,7 @@ export class CoreCourseHelperProvider { component?: string, componentId?: string | number, files?: CoreCourseModuleContentFile[], - options: CoreUtilsOpenFileOptions = {}, + options: CoreOpenerOpenFileOptions = {}, ): Promise { if (!CoreNetwork.isOnline()) { // Not online, get the offline file. It will fail if not found. @@ -737,7 +743,7 @@ export class CoreCourseHelperProvider { throw new CoreNetworkError(); } - return CoreUtils.openFile(path, options); + return CoreOpener.openFile(path, options); } // Open in browser. @@ -749,7 +755,7 @@ export class CoreCourseHelperProvider { // Remove forcedownload when not followed by any param. fixedUrl = fixedUrl.replace(/[?|&]forcedownload=\d+/, ''); - CoreUtils.openInBrowser(fixedUrl); + CoreOpener.openInBrowser(fixedUrl); if (CoreFile.isAvailable()) { // Download the file if needed (file outdated or not downloaded). @@ -778,7 +784,7 @@ export class CoreCourseHelperProvider { componentId?: string | number, files?: CoreCourseModuleContentFile[], siteId?: string, - options: CoreUtilsOpenFileOptions = {}, + options: CoreOpenerOpenFileOptions = {}, ): Promise<{ fixedUrl: string; path: string; status?: DownloadStatus }> { siteId = siteId || CoreSites.getCurrentSiteId(); @@ -876,7 +882,7 @@ export class CoreCourseHelperProvider { component?: string, componentId?: string | number, siteId?: string, - options: CoreUtilsOpenFileOptions = {}, + options: CoreOpenerOpenFileOptions = {}, ): Promise { siteId = siteId || CoreSites.getCurrentSiteId(); @@ -1082,7 +1088,7 @@ export class CoreCourseHelperProvider { const totalOffline = offlineCompletions.length; let loaded = 0; - const offlineCompletionsMap = CoreUtils.arrayToObject(offlineCompletions, 'cmid'); + const offlineCompletionsMap = CoreArray.toObject(offlineCompletions, 'cmid'); const loadSectionOfflineCompletion = (section: CoreCourseWSSection): void => { if (!section.contents || !section.contents.length) { @@ -1298,7 +1304,7 @@ export class CoreCourseHelperProvider { // If this function is changed to do more actions if invalidateCache=true, please review those modules. CoreCourseModulePrefetchDelegate.invalidateModuleStatusCache(module); - await CoreUtils.ignoreErrors(CoreCourseModulePrefetchDelegate.invalidateCourseUpdates(courseId)); + await CorePromiseUtils.ignoreErrors(CoreCourseModulePrefetchDelegate.invalidateCourseUpdates(courseId)); } const [size, status, packageData] = await Promise.all([ @@ -1351,7 +1357,7 @@ export class CoreCourseHelperProvider { component = '', ): Promise { const siteId = CoreSites.getCurrentSiteId(); - const packageData = await CoreUtils.ignoreErrors(CoreFilepool.getPackageData(siteId, component, module.id)); + const packageData = await CorePromiseUtils.ignoreErrors(CoreFilepool.getPackageData(siteId, component, module.id)); // Treat download time. if ( @@ -1596,7 +1602,7 @@ export class CoreCourseHelperProvider { promises.push(CoreFilterHelper.getFilters(ContextLevel.COURSE, course.id)); - await CoreUtils.allPromises(promises); + await CorePromiseUtils.allPromises(promises); // Download success, mark the course as downloaded. return CoreCourse.setCourseStatus(course.id, DownloadStatus.DOWNLOADED, requiredSiteId); @@ -1635,7 +1641,7 @@ export class CoreCourseHelperProvider { // Invalidate content if refreshing and download the data. if (refresh) { - await CoreUtils.ignoreErrors(handler.invalidateContent(module.id, courseId)); + await CorePromiseUtils.ignoreErrors(handler.invalidateContent(module.id, courseId)); } await CoreCourseModulePrefetchDelegate.prefetchModule(module, courseId, true); @@ -1659,7 +1665,7 @@ export class CoreCourseHelperProvider { if (updateAllSections) { // Prefetch all the sections. If the first section is "All sections", use it. Otherwise, use a fake "All sections". allSectionsSection = sections[0]; - if (sections[0].id !== CoreCourseProvider.ALL_SECTIONS_ID) { + if (sections[0].id !== CORE_COURSE_ALL_SECTIONS_ID) { allSectionsSection = this.createAllSectionsSection(); } allSectionsSection.isDownloading = true; @@ -1667,7 +1673,7 @@ export class CoreCourseHelperProvider { const promises = sections.map(async (section) => { // Download all the sections except "All sections". - if (section.id === CoreCourseProvider.ALL_SECTIONS_ID) { + if (section.id === CORE_COURSE_ALL_SECTIONS_ID) { return; } @@ -1683,7 +1689,7 @@ export class CoreCourseHelperProvider { }); try { - await CoreUtils.allPromises(promises); + await CorePromiseUtils.allPromises(promises); // Set "All sections" data. if (allSectionsSection) { @@ -1706,7 +1712,7 @@ export class CoreCourseHelperProvider { * @returns Promise resolved when the section is prefetched. */ protected async prefetchSingleSectionIfNeeded(section: CoreCourseSectionWithStatus, courseId: number): Promise { - if (section.id === CoreCourseProvider.ALL_SECTIONS_ID || section.hiddenbynumsections) { + if (section.id === CORE_COURSE_ALL_SECTIONS_ID || section.hiddenbynumsections) { return; } @@ -1720,8 +1726,8 @@ export class CoreCourseHelperProvider { // Download the files in the section description. const introFiles = CoreFilepool.extractDownloadableFilesFromHtmlAsFakeFileObjects(section.summary); - promises.push(CoreUtils.ignoreErrors( - CoreFilepool.addFilesToQueue(siteId, introFiles, CoreCourseProvider.COMPONENT, courseId), + promises.push(CorePromiseUtils.ignoreErrors( + CoreFilepool.addFilesToQueue(siteId, introFiles, CORE_COURSE_COMPONENT, courseId), )); try { @@ -1782,7 +1788,7 @@ export class CoreCourseHelperProvider { result: CoreCourseModulesStatus, courseId: number, ): Promise { - if (section.id === CoreCourseProvider.ALL_SECTIONS_ID) { + if (section.id === CORE_COURSE_ALL_SECTIONS_ID) { return; } @@ -1858,11 +1864,11 @@ export class CoreCourseHelperProvider { */ async userHasAccessToCourse(courseId: number): Promise { if (CoreNetwork.isOnline()) { - return CoreUtils.promiseWorks( + return CorePromiseUtils.promiseWorks( CoreCourse.getSections(courseId, true, true, { getFromCache: false, emergencyCache: false }, undefined, false), ); } else { - return CoreUtils.promiseWorks( + return CorePromiseUtils.promiseWorks( CoreCourse.getSections(courseId, true, true, { getCacheUsingCacheKey: true }, undefined, false), ); } @@ -1881,7 +1887,7 @@ export class CoreCourseHelperProvider { await Promise.all([ ...modules.map((module) => this.removeModuleStoredData(module, courseId)), - siteId && CoreFilepool.removeFilesByComponent(siteId, CoreCourseProvider.COMPONENT, courseId), + siteId && CoreFilepool.removeFilesByComponent(siteId, CORE_COURSE_COMPONENT, courseId), ]); await CoreCourse.setCourseStatus(courseId, DownloadStatus.DOWNLOADABLE_NOT_DOWNLOADED); @@ -1922,7 +1928,7 @@ export class CoreCourseHelperProvider { } if (completion.cmid === undefined || - completion.tracking !== CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_MANUAL) { + completion.tracking !== CoreCourseModuleCompletionTracking.MANUAL) { return; } @@ -1963,8 +1969,8 @@ export class CoreCourseHelperProvider { * * @returns Course summary page module. */ - async getCourseSummaryRouteModule(): Promise { - return import('../course-summary-lazy.module').then(m => m.CoreCourseSummaryLazyModule); + getCourseSummaryRouteModule(): LazyRoutesModule { + return import('../course-summary-lazy.module'); } /** @@ -2087,7 +2093,7 @@ export class CoreCourseHelperProvider { return undefined; } - if (completion.tracking === CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_NONE) { + if (completion.tracking === CoreCourseModuleCompletionTracking.NONE) { return undefined; } diff --git a/src/core/features/course/services/course-options-delegate.ts b/src/core/features/course/services/course-options-delegate.ts index b2f9def37a1..949f113a065 100644 --- a/src/core/features/course/services/course-options-delegate.ts +++ b/src/core/features/course/services/course-options-delegate.ts @@ -16,18 +16,18 @@ import { Injectable } from '@angular/core'; import { CoreDelegate, CoreDelegateHandler, CoreDelegateToDisplay } from '@classes/delegate'; import { CoreEvents } from '@singletons/events'; import { CoreSites } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreCourseAnyCourseData, CoreCourseAnyCourseDataWithOptions, CoreCourses, - CoreCoursesProvider, CoreCourseUserAdminOrNavOptionIndexed, } from '@features/courses/services/courses'; -import { CoreCourseAccessDataType } from './course'; +import { CoreCourseAccessDataType } from '../constants'; import { Params } from '@angular/router'; import { makeSingleton } from '@singletons'; import { CorePromisedValue } from '@classes/promised-value'; +import { CORE_COURSES_MY_COURSES_REFRESHED_EVENT } from '@features/courses/constants'; /** * Interface that all course options handlers must implement. @@ -280,7 +280,7 @@ export class CoreCourseOptionsDelegateService extends CoreDelegate { const promises: Promise[] = []; - CoreEvents.trigger(CoreCoursesProvider.EVENT_MY_COURSES_REFRESHED); + CoreEvents.trigger(CORE_COURSES_MY_COURSES_REFRESHED_EVENT); // Invalidate course enabled data for the handlers that are enabled at site level. if (courseId) { @@ -573,7 +573,7 @@ export class CoreCourseOptionsDelegateService extends CoreDelegate>>; @@ -152,6 +139,8 @@ export class CoreCourseProvider { AsyncInstance> >; + protected static readonly ROOT_CACHE_KEY = 'mmCourse:'; + constructor() { this.logger = CoreLogger.getInstance('CoreCourseProvider'); this.statusTables = lazyMap( @@ -187,7 +176,7 @@ export class CoreCourseProvider { CorePlatform.resume.subscribe(() => { // Run the handler the app is open to keep user in online status. setTimeout(() => { - CoreUtils.ignoreErrors( + CorePromiseUtils.ignoreErrors( CoreCronDelegate.forceCronHandlerExecution(CoreCourseLogCronHandler.name), ); }, 1000); @@ -196,7 +185,7 @@ export class CoreCourseProvider { CoreEvents.on(CoreEvents.LOGIN, () => { setTimeout(() => { // Ignore errors here, since probably login is not complete: it happens on token invalid. - CoreUtils.ignoreErrors( + CorePromiseUtils.ignoreErrors( CoreCronDelegate.forceCronHandlerExecution(CoreCourseLogCronHandler.name), ); }, 1000); @@ -254,7 +243,7 @@ export class CoreCourseProvider { * @returns Whether it's an automatic completion that hasn't been completed yet. */ isIncompleteAutomaticCompletion(completion: CoreCourseModuleCompletionData): boolean { - return completion.tracking === CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_AUTOMATIC && + return completion.tracking === CoreCourseModuleCompletionTracking.AUTOMATIC && completion.state === CoreCourseModuleCompletionStatus.COMPLETION_INCOMPLETE; } @@ -271,7 +260,7 @@ export class CoreCourseProvider { } const course = await CoreCourses.getCourseByField('id', courseId, site.id); - const formatOptions = CoreUtils.objectToKeyValueMap( + const formatOptions = CoreObject.toKeyValueMap( course.courseformatoptions ?? [], 'name', 'value', @@ -292,7 +281,7 @@ export class CoreCourseProvider { await this.statusTables[site.getId()].delete(); this.triggerCourseStatusChanged( - CoreCourseProvider.ALL_COURSES_CLEARED, + CORE_COURSE_ALL_COURSES_CLEARED, DownloadStatus.DOWNLOADABLE_NOT_DOWNLOADED, site.id, ); @@ -364,7 +353,7 @@ export class CoreCourseProvider { throw Error('WS core_completion_get_activities_completion_status failed'); } - const completionStatus = CoreUtils.arrayToObject(data.statuses, 'cmid'); + const completionStatus = CoreArray.toObject(data.statuses, 'cmid'); if (!includeOffline) { return completionStatus; } @@ -379,7 +368,7 @@ export class CoreCourseProvider { const onlineCompletion = completionStatus[offlineCompletion.cmid]; // If the activity uses manual completion, override the value with the offline one. - if (onlineCompletion.tracking === CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_MANUAL) { + if (onlineCompletion.tracking === CoreCourseModuleCompletionTracking.MANUAL) { onlineCompletion.state = offlineCompletion.completed; onlineCompletion.offline = true; } @@ -401,7 +390,7 @@ export class CoreCourseProvider { * @returns Cache key. */ protected getActivitiesCompletionCacheKey(courseId: number, userId: number): string { - return ROOT_CACHE_KEY + 'activitiescompletion:' + courseId + ':' + userId; + return CoreCourseProvider.ROOT_CACHE_KEY + 'activitiescompletion:' + courseId + ':' + userId; } /** @@ -423,7 +412,7 @@ export class CoreCourseProvider { js: (record) => ids.includes(record.cmId), }); - return CoreUtils.arrayToObject(entries, 'cmId'); + return CoreArray.toObject(entries, 'cmId'); } /** @@ -456,7 +445,7 @@ export class CoreCourseProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getCourseBlocksCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), }; @@ -473,7 +462,7 @@ export class CoreCourseProvider { * @returns Cache key. */ protected getCourseBlocksCacheKey(courseId: number): string { - return ROOT_CACHE_KEY + 'courseblocks:' + courseId; + return CoreCourseProvider.ROOT_CACHE_KEY + 'courseblocks:' + courseId; } /** @@ -582,7 +571,7 @@ export class CoreCourseProvider { const preSets: CoreSiteWSPreSets = { omitExpires: preferCache, - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, }; if (includeStealth) { @@ -670,7 +659,7 @@ export class CoreCourseProvider { let foundModule: CoreCourseGetContentsWSModule | undefined; const foundSection = sections.find((section) => { - if (section.id != CoreCourseProvider.STEALTH_MODULES_SECTION_ID && + if (section.id != CORE_COURSE_STEALTH_MODULES_SECTION_ID && sectionId !== undefined && sectionId != section.id ) { @@ -736,7 +725,7 @@ export class CoreCourseProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getModuleCacheKey(moduleId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; const response = await site.read('core_course_get_course_module', params, preSets); @@ -797,7 +786,7 @@ export class CoreCourseProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getModuleBasicInfoByInstanceCacheKey(instanceId, moduleName), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; @@ -821,7 +810,7 @@ export class CoreCourseProvider { * @returns Cache key. */ protected getModuleBasicInfoByInstanceCacheKey(instanceId: number, moduleName: string): string { - return ROOT_CACHE_KEY + 'moduleByInstance:' + moduleName + ':' + instanceId; + return CoreCourseProvider.ROOT_CACHE_KEY + 'moduleByInstance:' + moduleName + ':' + instanceId; } /** @@ -831,7 +820,7 @@ export class CoreCourseProvider { * @returns Cache key. */ protected getModuleCacheKey(moduleId: number): string { - return ROOT_CACHE_KEY + 'module:' + moduleId; + return CoreCourseProvider.ROOT_CACHE_KEY + 'module:' + moduleId; } /** @@ -841,7 +830,7 @@ export class CoreCourseProvider { * @returns Cache key. */ protected getModuleByModNameCacheKey(modName: string): string { - return ROOT_CACHE_KEY + 'module:modName:' + modName; + return CoreCourseProvider.ROOT_CACHE_KEY + 'module:modName:' + modName; } /** @@ -1008,7 +997,7 @@ export class CoreCourseProvider { const preSets: CoreSiteWSPreSets = { ...options.preSets, cacheKey: this.getSectionsCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), }; @@ -1070,7 +1059,7 @@ export class CoreCourseProvider { * @returns Cache key. */ protected getSectionsCacheKey(courseId: number): string { - return ROOT_CACHE_KEY + 'sections:' + courseId; + return CoreCourseProvider.ROOT_CACHE_KEY + 'sections:' + courseId; } /** @@ -1289,9 +1278,9 @@ export class CoreCourseProvider { if (!response.status) { throw Error('WS core_course_view_course failed.'); } else { - CoreEvents.trigger(CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, { + CoreEvents.trigger(CORE_COURSES_MY_COURSES_UPDATED_EVENT, { courseId: courseId, - action: CoreCoursesProvider.ACTION_VIEW, + action: CoreCoursesMyCoursesUpdatedEventAction.VIEW, }, site.getId()); } } @@ -1331,14 +1320,14 @@ export class CoreCourseProvider { const result = await this.markCompletedManuallyOnline(cmId, completed, siteId); // Data sent to server, if there is some offline data delete it now. - await CoreUtils.ignoreErrors(CoreCourseOffline.deleteManualCompletion(cmId, siteId)); + await CorePromiseUtils.ignoreErrors(CoreCourseOffline.deleteManualCompletion(cmId, siteId)); // Invalidate module now, completion has changed. await this.invalidateModule(cmId, siteId); return result; } catch (error) { - if (CoreUtils.isWebServiceError(error)) { + if (CoreWSError.isWebServiceError(error)) { // The WebService has thrown an error, this means that responses cannot be submitted. throw error; } else { @@ -1409,7 +1398,7 @@ export class CoreCourseProvider { */ isCoreModule(moduleName: string): boolean { // If core modules are removed for a certain version we should check the version of the site. - return CoreCourseProvider.CORE_MODULES.includes(moduleName); + return CORE_COURSE_CORE_MODULES.includes(moduleName); } /** @@ -1437,10 +1426,12 @@ export class CoreCourseProvider { return; } + const { CoreSitePlugins } = await import('@features/siteplugins/services/siteplugins'); + const loading = await CoreLoadings.show(); // Wait for site plugins to be fetched. - await CoreUtils.ignoreErrors(CoreSitePlugins.waitFetchPlugins()); + await CorePromiseUtils.ignoreErrors(CoreSitePlugins.waitFetchPlugins()); if (!('format' in course) || course.format === undefined) { const result = await CoreCourseHelper.getCourse(course.id); diff --git a/src/core/features/course/services/handlers/course-tag-area.ts b/src/core/features/course/services/handlers/course-tag-area.ts index e6b1de384d2..60e47acc1de 100644 --- a/src/core/features/course/services/handlers/course-tag-area.ts +++ b/src/core/features/course/services/handlers/course-tag-area.ts @@ -16,7 +16,6 @@ import { Injectable, Type } from '@angular/core'; import { convertTextToHTMLElement } from '@/core/utils/create-html-element'; import { CoreTagAreaHandler } from '@features/tag/services/tag-area-delegate'; -import { CoreCourseTagAreaComponent } from '../../components/tag-area/tag-area'; import { makeSingleton } from '@singletons'; /** @@ -65,11 +64,11 @@ export class CoreCourseTagAreaHandlerService implements CoreTagAreaHandler { } /** - * Get the component to use to display items. - * - * @returns The component (or promise resolved with component) to use, undefined if not found. + * @inheritdoc */ - getComponent(): Type | Promise> { + async getComponent(): Promise> { + const { CoreCourseTagAreaComponent } = await import('../../components/tag-area/tag-area'); + return CoreCourseTagAreaComponent; } diff --git a/src/core/features/course/services/handlers/default-format.ts b/src/core/features/course/services/handlers/default-format.ts index 383a645fce0..a9bf0eca30f 100644 --- a/src/core/features/course/services/handlers/default-format.ts +++ b/src/core/features/course/services/handlers/default-format.ts @@ -15,7 +15,7 @@ import { Injectable } from '@angular/core'; import { CoreCourseAnyCourseData, CoreCourses } from '@features/courses/services/courses'; import { CoreNavigationOptions, CoreNavigator } from '@services/navigator'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { Translate } from '@singletons'; import { CoreCourseSection } from '../course-helper'; import { CoreCourseFormatCurrentSectionData, CoreCourseFormatHandler } from '../format-delegate'; @@ -86,7 +86,7 @@ export class CoreCourseFormatDefaultHandler implements CoreCourseFormatHandler { marker = course.marker; } else { // Try to retrieve the marker. - const courseData = await CoreUtils.ignoreErrors(CoreCourses.getCourseByField('id', course.id)); + const courseData = await CorePromiseUtils.ignoreErrors(CoreCourses.getCourseByField('id', course.id)); marker = courseData?.marker; } diff --git a/src/core/features/course/services/handlers/modules-tag-area.ts b/src/core/features/course/services/handlers/modules-tag-area.ts index 8cbd6ea4769..e0d7abb4348 100644 --- a/src/core/features/course/services/handlers/modules-tag-area.ts +++ b/src/core/features/course/services/handlers/modules-tag-area.ts @@ -16,7 +16,6 @@ import { Injectable, Type } from '@angular/core'; import { CoreTagAreaHandler } from '@features/tag/services/tag-area-delegate'; import { CoreTagFeedElement, CoreTagHelper } from '@features/tag/services/tag-helper'; -import { CoreTagFeedComponent } from '@features/tag/components/feed/feed'; import { makeSingleton } from '@singletons'; /** @@ -48,11 +47,11 @@ export class CoreCourseModulesTagAreaHandlerService implements CoreTagAreaHandle } /** - * Get the component to use to display items. - * - * @returns The component (or promise resolved with component) to use, undefined if not found. + * @inheritdoc */ - getComponent(): Type | Promise> { + async getComponent(): Promise> { + const { CoreTagFeedComponent } = await import('@features/tag/components/feed/feed'); + return CoreTagFeedComponent; } diff --git a/src/core/features/course/services/log-helper.ts b/src/core/features/course/services/log-helper.ts index 41deadb2170..7fa1520edb4 100644 --- a/src/core/features/course/services/log-helper.ts +++ b/src/core/features/course/services/log-helper.ts @@ -18,11 +18,12 @@ import { CoreNetwork } from '@services/network'; import { CoreSites } from '@services/sites'; import { CoreText } from '@singletons/text'; import { CoreTimeUtils } from '@services/utils/time'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreObject } from '@singletons/object'; import { makeSingleton } from '@singletons'; import { ACTIVITY_LOG_TABLE, CoreCourseActivityLogDBRecord } from './database/log'; import { CoreStatusWithWarningsWSResponse } from '@services/ws'; import { CoreWSError } from '@classes/errors/wserror'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Helper to manage logging to Moodle. @@ -83,7 +84,7 @@ export class CoreCourseLogHelperProvider { const conditions: Partial = { ws, - data: CoreUtils.sortAndStringify(data), + data: CoreObject.sortAndStringify(data), }; await site.getDb().deleteRecords(ACTIVITY_LOG_TABLE, conditions); @@ -141,7 +142,7 @@ export class CoreCourseLogHelperProvider { try { await this.logOnline(ws, data, site.getId()); } catch (error) { - if (CoreUtils.isWebServiceError(error)) { + if (CoreWSError.isWebServiceError(error)) { // The WebService has thrown an error, this means that responses cannot be submitted. throw error; } @@ -263,7 +264,7 @@ export class CoreCourseLogHelperProvider { component, componentid: componentId, ws, - data: CoreUtils.sortAndStringify(data), + data: CoreObject.sortAndStringify(data), time: CoreTimeUtils.timestamp(), }; @@ -343,9 +344,9 @@ export class CoreCourseLogHelperProvider { try { await this.logOnline(log.ws, data, siteId); } catch (error) { - if (CoreUtils.isWebServiceError(error)) { + if (CoreWSError.isWebServiceError(error)) { // The WebService has thrown an error, this means that responses cannot be submitted. - await CoreUtils.ignoreErrors(this.deleteWSLogs(log.ws, data, siteId)); + await CorePromiseUtils.ignoreErrors(this.deleteWSLogs(log.ws, data, siteId)); } throw error; diff --git a/src/core/features/course/services/module-prefetch-delegate.ts b/src/core/features/course/services/module-prefetch-delegate.ts index 330de8d0a53..0a28bd98c0f 100644 --- a/src/core/features/course/services/module-prefetch-delegate.ts +++ b/src/core/features/course/services/module-prefetch-delegate.ts @@ -21,7 +21,7 @@ import { CoreFileHelper } from '@services/file-helper'; import { CoreFilepool } from '@services/filepool'; import { CoreSites } from '@services/sites'; import { CoreTimeUtils } from '@services/utils/time'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreArray } from '@singletons/array'; import { CoreCourse, CoreCourseAnyModuleData, CoreCourseModuleContentFile } from './course'; import { CoreCache } from '@classes/cache'; import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site'; @@ -34,6 +34,7 @@ import { CoreWSFile, CoreWSExternalWarning } from '@services/ws'; import { CHECK_UPDATES_TIMES_TABLE, CoreCourseCheckUpdatesDBRecord } from './database/module-prefetch'; import { CoreFileSizeSum } from '@services/plugin-file-delegate'; import { CoreCourseHelper, CoreCourseModuleData } from './course-helper'; +import { CorePromiseUtils } from '@singletons/promise-utils'; const ROOT_CACHE_KEY = 'mmCourse:'; @@ -286,7 +287,7 @@ export class CoreCourseModulePrefetchDelegateService extends CoreDelegate this.syncModule(module, courseId))); } finally { // Invalidate course updates. - await CoreUtils.ignoreErrors(this.invalidateCourseUpdates(courseId)); + await CorePromiseUtils.ignoreErrors(this.invalidateCourseUpdates(courseId)); } } @@ -1138,7 +1139,7 @@ export class CoreCourseModulePrefetchDelegateService extends CoreDelegate { - await CoreUtils.ignoreErrors(CoreFilepool.removeFileByUrl(siteId, CoreFileHelper.getFileUrl(file))); + await CorePromiseUtils.ignoreErrors(CoreFilepool.removeFileByUrl(siteId, CoreFileHelper.getFileUrl(file))); })); } @@ -1316,7 +1317,7 @@ export class CoreCourseModulePrefetchDelegateService extends CoreDelegate(response.instances, 'id', result); + CoreArray.toObject(response.instances, 'id', result); // Treat warnings, adding the not supported modules. response.warnings?.forEach((warning) => { diff --git a/src/core/features/course/services/sync.ts b/src/core/features/course/services/sync.ts index e38e3ad56ff..84bb6da0299 100644 --- a/src/core/features/course/services/sync.ts +++ b/src/core/features/course/services/sync.ts @@ -18,7 +18,7 @@ import { CoreSyncBaseProvider } from '@classes/base-sync'; import { CoreSites } from '@services/sites'; import { CoreNetwork } from '@services/network'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreWSError } from '@classes/errors/wserror'; import { CoreErrorHelper } from '@services/error-helper'; import { CoreCourseOffline } from './course-offline'; import { CoreCourse } from './course'; @@ -29,6 +29,8 @@ import { CoreNetworkError } from '@classes/errors/network-error'; import { makeSingleton, Translate } from '@singletons'; import { CoreEvents } from '@singletons/events'; import { CoreCourses } from '@features/courses/services/courses'; +import { CORE_COURSE_AUTO_SYNCED } from '../constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Service to sync course offline data. This only syncs the offline data of the course itself, not the offline data of @@ -37,8 +39,6 @@ import { CoreCourses } from '@features/courses/services/courses'; @Injectable({ providedIn: 'root' }) export class CoreCourseSyncProvider extends CoreSyncBaseProvider { - static readonly AUTO_SYNCED = 'core_course_autom_synced'; - constructor() { super('CoreCourseSyncProvider'); } @@ -83,7 +83,7 @@ export class CoreCourseSyncProvider extends CoreSyncBaseProvider { if (courseNames[completion.courseid] === undefined) { - const course = await CoreUtils.ignoreErrors(CoreCourses.getUserCourse(completion.courseid, true, siteId)); + const course = await CorePromiseUtils.ignoreErrors(CoreCourses.getUserCourse(completion.courseid, true, siteId)); courseNames[completion.courseid] = course?.displayname || course?.fullname; } @@ -96,7 +96,7 @@ export class CoreCourseSyncProvider extends CoreSyncBaseProvider [], ); @@ -212,7 +212,7 @@ export class CoreCourseSyncProvider extends CoreSyncBaseProvider { + this.progressObserver = CoreEvents.on(CORE_COURSE_PROGRESS_UPDATED_EVENT, (data) => { if (!this.course || this.course.id !== data.courseId || !('progress' in this.course)) { return; } @@ -197,7 +204,7 @@ export class CoreCoursesCourseListItemComponent implements OnInit, OnDestroy, On // Listen for status change in course. this.courseStatusObserver = CoreEvents.on(CoreEvents.COURSE_STATUS_CHANGED, (data: CoreEventCourseStatusChanged) => { - if (data.courseId == this.course.id || data.courseId == CoreCourseProvider.ALL_COURSES_CLEARED) { + if (data.courseId == this.course.id || data.courseId == CORE_COURSE_ALL_COURSES_CLEARED) { this.updateCourseStatus(data.status); } }, CoreSites.getCurrentSiteId()); @@ -356,11 +363,11 @@ export class CoreCoursesCourseListItemComponent implements OnInit, OnDestroy, On this.course.hidden = hide; ( this.course).hidden = hide; - CoreEvents.trigger(CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, { + CoreEvents.trigger(CORE_COURSES_MY_COURSES_UPDATED_EVENT, { courseId: this.course.id, course: this.course, - action: CoreCoursesProvider.ACTION_STATE_CHANGED, - state: CoreCoursesProvider.STATE_HIDDEN, + action: CoreCoursesMyCoursesUpdatedEventAction.STATE_CHANGED, + state: CORE_COURSES_STATE_HIDDEN, value: hide, }, CoreSites.getCurrentSiteId()); @@ -385,11 +392,11 @@ export class CoreCoursesCourseListItemComponent implements OnInit, OnDestroy, On await CoreCourses.setFavouriteCourse(this.course.id, favourite); this.course.isfavourite = favourite; - CoreEvents.trigger(CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, { + CoreEvents.trigger(CORE_COURSES_MY_COURSES_UPDATED_EVENT, { courseId: this.course.id, course: this.course, - action: CoreCoursesProvider.ACTION_STATE_CHANGED, - state: CoreCoursesProvider.STATE_FAVOURITE, + action: CoreCoursesMyCoursesUpdatedEventAction.STATE_CHANGED, + state: CORE_COURSES_STATE_FAVOURITE, value: favourite, }, CoreSites.getCurrentSiteId()); diff --git a/src/core/features/courses/constants.ts b/src/core/features/courses/constants.ts new file mode 100644 index 00000000000..3dc3f9214c3 --- /dev/null +++ b/src/core/features/courses/constants.ts @@ -0,0 +1,38 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +export const CORE_COURSES_ENROL_INVALID_KEY = 'CoreCoursesEnrolInvalidKey'; + +export const CORE_COURSES_MY_COURSES_CHANGED_EVENT = 'courses_my_courses_changed'; // User course list changed while app is running. + +// A course was hidden/favourite, or user enroled in a course. +export const CORE_COURSES_MY_COURSES_UPDATED_EVENT = 'courses_my_courses_updated'; +export const CORE_COURSES_MY_COURSES_REFRESHED_EVENT = 'courses_my_courses_refreshed'; +export const CORE_COURSES_DASHBOARD_DOWNLOAD_ENABLED_CHANGED_EVENT = 'dashboard_download_enabled_changed'; + +// Actions for event CORE_COURSES_MY_COURSES_UPDATED_EVENT. +export const CoreCoursesMyCoursesUpdatedEventAction = { +/* eslint-disable @typescript-eslint/naming-convention */ + ENROL: 'enrol', // User enrolled in a course. + STATE_CHANGED: 'state_changed', // Course state changed (hidden, favourite). + VIEW: 'view', // Course viewed. +/* eslint-enable @typescript-eslint/naming-convention*/ +} as const; +// eslint-disable-next-line @typescript-eslint/no-redeclare +export type CoreCoursesMyCoursesUpdatedEventAction = + typeof CoreCoursesMyCoursesUpdatedEventAction[keyof typeof CoreCoursesMyCoursesUpdatedEventAction]; + +// Possible states changed. +export const CORE_COURSES_STATE_HIDDEN = 'hidden'; +export const CORE_COURSES_STATE_FAVOURITE = 'favourite'; diff --git a/src/core/features/courses/courses-dashboard-lazy.module.ts b/src/core/features/courses/courses-dashboard-lazy.module.ts index dfd408585fb..d9dccedabad 100644 --- a/src/core/features/courses/courses-dashboard-lazy.module.ts +++ b/src/core/features/courses/courses-dashboard-lazy.module.ts @@ -36,4 +36,4 @@ const routes: Routes = [ CoreCoursesDashboardPage, ], }) -export class CoreCoursesDashboardLazyModule {} +export default class CoreCoursesDashboardLazyModule {} diff --git a/src/core/features/courses/courses-lazy.module.ts b/src/core/features/courses/courses-lazy.module.ts index d51c13f011d..a179c752616 100644 --- a/src/core/features/courses/courses-lazy.module.ts +++ b/src/core/features/courses/courses-lazy.module.ts @@ -79,4 +79,4 @@ function buildRoutes(injector: Injector): Routes { }, ], }) -export class CoreCoursesLazyModule {} +export default class CoreCoursesLazyModule {} diff --git a/src/core/features/courses/courses-my-lazy.module.ts b/src/core/features/courses/courses-my-lazy.module.ts index 9f41349dbe1..375c7ae8e90 100644 --- a/src/core/features/courses/courses-my-lazy.module.ts +++ b/src/core/features/courses/courses-my-lazy.module.ts @@ -39,4 +39,4 @@ const routes: Routes = [ CoreCoursesMyPage, ], }) -export class CoreCoursesMyLazyModule {} +export default class CoreCoursesMyLazyModule {} diff --git a/src/core/features/courses/courses.module.ts b/src/core/features/courses/courses.module.ts index 7cc29f32deb..0b06ad7f58c 100644 --- a/src/core/features/courses/courses.module.ts +++ b/src/core/features/courses/courses.module.ts @@ -57,17 +57,48 @@ export async function getCoursesServices(): Promise[]> { ]; } +/** + * Get courses exported objects. + * + * @returns Courses exported objects. + */ +export async function getCoursesExportedObjects(): Promise> { + const { + CORE_COURSES_ENROL_INVALID_KEY, + CORE_COURSES_MY_COURSES_CHANGED_EVENT, + CORE_COURSES_MY_COURSES_UPDATED_EVENT, + CORE_COURSES_MY_COURSES_REFRESHED_EVENT, + CORE_COURSES_DASHBOARD_DOWNLOAD_ENABLED_CHANGED_EVENT, + CoreCoursesMyCoursesUpdatedEventAction, + CORE_COURSES_STATE_HIDDEN, + CORE_COURSES_STATE_FAVOURITE, + } = await import('@features/courses/constants'); + + /* eslint-disable @typescript-eslint/naming-convention */ + return { + CORE_COURSES_ENROL_INVALID_KEY, + CORE_COURSES_MY_COURSES_CHANGED_EVENT, + CORE_COURSES_MY_COURSES_UPDATED_EVENT, + CORE_COURSES_MY_COURSES_REFRESHED_EVENT, + CORE_COURSES_DASHBOARD_DOWNLOAD_ENABLED_CHANGED_EVENT, + CoreCoursesMyCoursesUpdatedEventAction, + CORE_COURSES_STATE_HIDDEN, + CORE_COURSES_STATE_FAVOURITE, + }; + /* eslint-enable @typescript-eslint/naming-convention */ +} + const mainMenuHomeChildrenRoutes: Routes = [ { path: CoreDashboardHomeHandlerService.PAGE_NAME, - loadChildren: () => import('./courses-dashboard-lazy.module').then(m => m.CoreCoursesDashboardLazyModule), + loadChildren: () => import('./courses-dashboard-lazy.module'), }, ]; const routes: Routes = [ { path: CoreCoursesMyCoursesMainMenuHandlerService.PAGE_NAME, - loadChildren: () => import('./courses-lazy.module').then(m => m.CoreCoursesLazyModule), + loadChildren: () => import('./courses-lazy.module'), }, ]; diff --git a/src/core/features/courses/pages/categories/categories.ts b/src/core/features/courses/pages/categories/categories.ts index f770425d24b..c52e7bd2fec 100644 --- a/src/core/features/courses/pages/categories/categories.ts +++ b/src/core/features/courses/pages/categories/categories.ts @@ -15,13 +15,18 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; -import { CoreCategoryData, CoreCourseListItem, CoreCourses, CoreCoursesProvider } from '../../services/courses'; +import { CoreUtils } from '@singletons/utils'; +import { CoreCategoryData, CoreCourseListItem, CoreCourses } from '../../services/courses'; import { Translate } from '@singletons'; import { CoreNavigator } from '@services/navigator'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics'; import { CoreTime } from '@singletons/time'; +import { + CORE_COURSES_MY_COURSES_UPDATED_EVENT, + CoreCoursesMyCoursesUpdatedEventAction, + CORE_COURSES_DASHBOARD_DOWNLOAD_ENABLED_CHANGED_EVENT, +} from '@features/courses/constants'; /** * Page that displays a list of categories and the courses in the current category if any. @@ -59,9 +64,9 @@ export class CoreCoursesCategoriesPage implements OnInit, OnDestroy { // Update list if user enrols in a course. this.myCoursesObserver = CoreEvents.on( - CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, + CORE_COURSES_MY_COURSES_UPDATED_EVENT, (data) => { - if (data.action == CoreCoursesProvider.ACTION_ENROL) { + if (data.action === CoreCoursesMyCoursesUpdatedEventAction.ENROL) { this.fetchCategories(); } }, @@ -77,7 +82,7 @@ export class CoreCoursesCategoriesPage implements OnInit, OnDestroy { this.downloadEnabled = (this.downloadCourseEnabled || this.downloadCoursesEnabled) && this.downloadEnabled; }, this.currentSiteId); - this.downloadEnabledObserver = CoreEvents.on(CoreCoursesProvider.EVENT_DASHBOARD_DOWNLOAD_ENABLED_CHANGED, (data) => { + this.downloadEnabledObserver = CoreEvents.on(CORE_COURSES_DASHBOARD_DOWNLOAD_ENABLED_CHANGED_EVENT, (data) => { this.downloadEnabled = (this.downloadCourseEnabled || this.downloadCoursesEnabled) && data.enabled; }); diff --git a/src/core/features/courses/pages/dashboard/dashboard.ts b/src/core/features/courses/pages/dashboard/dashboard.ts index 5db3bed542b..50789a2c9c5 100644 --- a/src/core/features/courses/pages/dashboard/dashboard.ts +++ b/src/core/features/courses/pages/dashboard/dashboard.ts @@ -26,7 +26,7 @@ import { CoreBlockDelegate } from '@features/block/services/block-delegate'; import { CoreTime } from '@singletons/time'; import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics'; import { Translate } from '@singletons'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Page that displays the dashboard page. @@ -61,7 +61,7 @@ export class CoreCoursesDashboardPage implements OnInit, OnDestroy { }, CoreSites.getCurrentSiteId()); this.logView = CoreTime.once(async () => { - await CoreUtils.ignoreErrors(CoreCourses.logView('dashboard')); + await CorePromiseUtils.ignoreErrors(CoreCourses.logView('dashboard')); CoreAnalytics.logEvent({ type: CoreAnalyticsEventType.VIEW_ITEM, diff --git a/src/core/features/courses/pages/list/list.ts b/src/core/features/courses/pages/list/list.ts index 1993496cb65..519b446034f 100644 --- a/src/core/features/courses/pages/list/list.ts +++ b/src/core/features/courses/pages/list/list.ts @@ -18,10 +18,15 @@ import { CoreNavigator } from '@services/navigator'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; -import { CoreCourseBasicSearchedData, CoreCourses, CoreCoursesProvider } from '../../services/courses'; +import { CoreCourseBasicSearchedData, CoreCourses } from '../../services/courses'; import { CoreTime } from '@singletons/time'; import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics'; import { Translate } from '@singletons'; +import { + CORE_COURSES_MY_COURSES_UPDATED_EVENT, + CoreCoursesMyCoursesUpdatedEventAction, + CORE_COURSES_DASHBOARD_DOWNLOAD_ENABLED_CHANGED_EVENT, +} from '@features/courses/constants'; type CoreCoursesListMode = 'search' | 'all' | 'my'; @@ -73,10 +78,10 @@ export class CoreCoursesListPage implements OnInit, OnDestroy { // Update list if user enrols in a course. this.myCoursesObserver = CoreEvents.on( - CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, + CORE_COURSES_MY_COURSES_UPDATED_EVENT, (data) => { - if (data.action == CoreCoursesProvider.ACTION_ENROL) { + if (data.action == CoreCoursesMyCoursesUpdatedEventAction.ENROL) { this.fetchCourses(); } }, @@ -98,7 +103,7 @@ export class CoreCoursesListPage implements OnInit, OnDestroy { } }, this.currentSiteId); - this.downloadEnabledObserver = CoreEvents.on(CoreCoursesProvider.EVENT_DASHBOARD_DOWNLOAD_ENABLED_CHANGED, (data) => { + this.downloadEnabledObserver = CoreEvents.on(CORE_COURSES_DASHBOARD_DOWNLOAD_ENABLED_CHANGED_EVENT, (data) => { this.downloadEnabled = (this.downloadCourseEnabled || this.downloadCoursesEnabled) && data.enabled; }); diff --git a/src/core/features/courses/pages/my/my.ts b/src/core/features/courses/pages/my/my.ts index 370e9b2440e..f7a9379aef7 100644 --- a/src/core/features/courses/pages/my/my.ts +++ b/src/core/features/courses/pages/my/my.ts @@ -23,7 +23,7 @@ import { CoreCourseBlock } from '@features/course/services/course'; import { CoreCoursesDashboard, CoreCoursesDashboardProvider } from '@features/courses/services/dashboard'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { Subscription } from 'rxjs'; import { CoreCourses } from '../../services/courses'; @@ -38,7 +38,7 @@ import { CoreWait } from '@singletons/wait'; @Component({ selector: 'page-core-courses-my', templateUrl: 'my.html', - styleUrls: ['my.scss'], + styleUrl: 'my.scss', providers: [{ provide: PageLoadsManager, useClass: PageLoadsManager, @@ -78,7 +78,7 @@ export class CoreCoursesMyPage implements OnInit, OnDestroy, AsyncDirective { }); this.logView = CoreTime.once(async () => { - await CoreUtils.ignoreErrors(CoreCourses.logView('my')); + await CorePromiseUtils.ignoreErrors(CoreCourses.logView('my')); CoreAnalytics.logEvent({ type: CoreAnalyticsEventType.VIEW_ITEM, @@ -187,7 +187,7 @@ export class CoreCoursesMyPage implements OnInit, OnDestroy, AsyncDirective { // Invalidate the blocks. if (this.myOverviewBlock) { - promises.push(CoreUtils.ignoreErrors(this.myOverviewBlock.invalidateContent())); + promises.push(CorePromiseUtils.ignoreErrors(this.myOverviewBlock.invalidateContent())); } Promise.all(promises).finally(() => { diff --git a/src/core/features/courses/services/courses-helper.ts b/src/core/features/courses/services/courses-helper.ts index b066d129ab7..742babb1b2d 100644 --- a/src/core/features/courses/services/courses-helper.ts +++ b/src/core/features/courses/services/courses-helper.ts @@ -13,7 +13,7 @@ // limitations under the License. import { Injectable } from '@angular/core'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreArray } from '@singletons/array'; import { CoreSites, CoreSitesCommonWSOptions } from '@services/sites'; import { CoreCourseAnyCourseData, @@ -157,7 +157,7 @@ export class CoreCoursesHelperProvider { // Get the extra data for the courses. return CoreCourses.getCoursesByFieldObservable('ids', courseIds, options).pipe(map(coursesInfosArray => { - const coursesInfo = CoreUtils.arrayToObject(coursesInfosArray, 'id'); + const coursesInfo = CoreArray.toObject(coursesInfosArray, 'id'); courses.forEach((course) => { this.loadCourseExtraInfo(course, coursesInfo[course.id], loadCategoryNames); @@ -435,8 +435,8 @@ export class CoreCoursesHelperProvider { * * @returns My courses page module. */ - async getMyRouteModule(): Promise { - return import('../courses-my-lazy.module').then(m => m.CoreCoursesMyLazyModule); + getMyRouteModule(): LazyRoutesModule { + return import('../courses-my-lazy.module'); } /** diff --git a/src/core/features/courses/services/courses.ts b/src/core/features/courses/services/courses.ts index 35f34ceb4a5..b61d698a463 100644 --- a/src/core/features/courses/services/courses.ts +++ b/src/core/features/courses/services/courses.ts @@ -22,10 +22,20 @@ import { CoreCourseAnyCourseDataWithExtraInfoAndOptions, CoreCourseWithImageAndC import { asyncObservable, ignoreErrors, zipIncludingComplete } from '@/core/utils/rxjs'; import { of, firstValueFrom } from 'rxjs'; import { map } from 'rxjs/operators'; -import { AddonEnrolGuest, AddonEnrolGuestInfo } from '@addons/enrol/guest/services/guest'; -import { AddonEnrolSelf } from '@addons/enrol/self/services/self'; -import { CoreEnrol, CoreEnrolEnrolmentInfo, CoreEnrolEnrolmentMethod } from '@features/enrol/services/enrol'; +import { AddonEnrolGuestInfo } from '@addons/enrol/guest/services/guest'; +import { CoreEnrolEnrolmentInfo, CoreEnrolEnrolmentMethod } from '@features/enrol/services/enrol'; import { CoreSiteWSPreSets, WSObservable } from '@classes/sites/authenticated-site'; +import { CoreCacheUpdateFrequency } from '@/core/constants'; +import { + CORE_COURSES_ENROL_INVALID_KEY, + CORE_COURSES_DASHBOARD_DOWNLOAD_ENABLED_CHANGED_EVENT, + CORE_COURSES_MY_COURSES_CHANGED_EVENT, + CORE_COURSES_MY_COURSES_REFRESHED_EVENT, + CORE_COURSES_MY_COURSES_UPDATED_EVENT, + CoreCoursesMyCoursesUpdatedEventAction, + CORE_COURSES_STATE_FAVOURITE, + CORE_COURSES_STATE_HIDDEN, +} from '../constants'; declare module '@singletons/events' { @@ -35,9 +45,9 @@ declare module '@singletons/events' { * @see https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation */ export interface CoreEventsData { - [CoreCoursesProvider.EVENT_MY_COURSES_CHANGED]: CoreCoursesMyCoursesChangedEventData; - [CoreCoursesProvider.EVENT_MY_COURSES_UPDATED]: CoreCoursesMyCoursesUpdatedEventData; - [CoreCoursesProvider.EVENT_DASHBOARD_DOWNLOAD_ENABLED_CHANGED]: CoreCoursesDashboardDownloadEnabledChangedEventData; + [CORE_COURSES_MY_COURSES_CHANGED_EVENT]: CoreCoursesMyCoursesChangedEventData; + [CORE_COURSES_MY_COURSES_UPDATED_EVENT]: CoreCoursesMyCoursesUpdatedEventData; + [CORE_COURSES_DASHBOARD_DOWNLOAD_ENABLED_CHANGED_EVENT]: CoreCoursesDashboardDownloadEnabledChangedEventData; } } @@ -50,23 +60,51 @@ export class CoreCoursesProvider { protected static readonly ROOT_CACHE_KEY = 'mmCourses:'; - static readonly SEARCH_PER_PAGE = 20; - static readonly RECENT_PER_PAGE = 10; - static readonly ENROL_INVALID_KEY = 'CoreCoursesEnrolInvalidKey'; - static readonly EVENT_MY_COURSES_CHANGED = 'courses_my_courses_changed'; // User course list changed while app is running. - // A course was hidden/favourite, or user enroled in a course. - static readonly EVENT_MY_COURSES_UPDATED = 'courses_my_courses_updated'; - static readonly EVENT_MY_COURSES_REFRESHED = 'courses_my_courses_refreshed'; - static readonly EVENT_DASHBOARD_DOWNLOAD_ENABLED_CHANGED = 'dashboard_download_enabled_changed'; + protected static readonly SEARCH_PER_PAGE = 20; + protected static readonly RECENT_PER_PAGE = 10; - // Actions for event EVENT_MY_COURSES_UPDATED. - static readonly ACTION_ENROL = 'enrol'; // User enrolled in a course. - static readonly ACTION_STATE_CHANGED = 'state_changed'; // Course state changed (hidden, favourite). - static readonly ACTION_VIEW = 'view'; // Course viewed. + /** + * @deprecated since 5.0. Use CORE_COURSES_ENROL_INVALID_KEY instead. + */ + static readonly ENROL_INVALID_KEY = CORE_COURSES_ENROL_INVALID_KEY; + /** + * @deprecated since 5.0. Use CORE_COURSES_MY_COURSES_CHANGED_EVENT instead. + */ + static readonly EVENT_MY_COURSES_CHANGED = CORE_COURSES_MY_COURSES_CHANGED_EVENT; + /** + * @deprecated since 5.0. Use CORE_COURSES_MY_COURSES_UPDATED_EVENT instead. + */ + static readonly EVENT_MY_COURSES_UPDATED = CORE_COURSES_MY_COURSES_UPDATED_EVENT; + /** + * @deprecated since 5.0. Use CORE_COURSES_MY_COURSES_REFRESHED_EVENT instead. + */ + static readonly EVENT_MY_COURSES_REFRESHED = CORE_COURSES_MY_COURSES_REFRESHED_EVENT; + /** + * @deprecated since 5.0. Use CORE_COURSES_DASHBOARD_DOWNLOAD_ENABLED_CHANGED_EVENT instead. + */ + static readonly EVENT_DASHBOARD_DOWNLOAD_ENABLED_CHANGED = CORE_COURSES_DASHBOARD_DOWNLOAD_ENABLED_CHANGED_EVENT; + + /** + * @deprecated since 5.0. Use CoreCoursesMyCoursesUpdatedEventAction.ENROL instead. + */ + static readonly ACTION_ENROL = CoreCoursesMyCoursesUpdatedEventAction.ENROL; + /** + * @deprecated since 5.0. Use CoreCoursesMyCoursesUpdatedEventAction.STATE_CHANGED instead. + */ + static readonly ACTION_STATE_CHANGED = CoreCoursesMyCoursesUpdatedEventAction.STATE_CHANGED; + /** + * @deprecated since 5.0. Use CoreCoursesMyCoursesUpdatedEventAction.VIEW instead. + */ + static readonly ACTION_VIEW = CoreCoursesMyCoursesUpdatedEventAction.VIEW; - // Possible states changed. - static readonly STATE_HIDDEN = 'hidden'; - static readonly STATE_FAVOURITE = 'favourite'; + /** + * @deprecated since 5.0. Use CORE_COURSES_STATE_HIDDEN instead. + */ + static readonly STATE_HIDDEN = CORE_COURSES_STATE_HIDDEN; + /** + * @deprecated since 5.0. Use CORE_COURSES_STATE_FAVOURITE instead. + */ + static readonly STATE_FAVOURITE = CORE_COURSES_STATE_FAVOURITE; protected userCoursesIds?: Set; protected downloadOptionsEnabled = false; @@ -100,7 +138,7 @@ export class CoreCoursesProvider { const preSets = { cacheKey: this.getCategoriesCacheKey(categoryId, addSubcategories), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, }; return site.read('core_course_get_categories', params, preSets); @@ -308,6 +346,8 @@ export class CoreCoursesProvider { * @deprecated since 4.3. Use CoreEnrol.getSupportedCourseEnrolmentMethods instead. */ async getCourseEnrolmentMethods(courseId: number, siteId?: string): Promise { + const { CoreEnrol } = await import('@features/enrol/services/enrol'); + return CoreEnrol.getSupportedCourseEnrolmentMethods(courseId, { siteId }); } @@ -320,6 +360,8 @@ export class CoreCoursesProvider { * @deprecated since 4.3 use AddonEnrolGuest.getCourseGuestEnrolmentInfo instead. */ async getCourseGuestEnrolmentInfo(instanceId: number, siteId?: string): Promise { + const { AddonEnrolGuest } = await import('@addons/enrol/guest/services/guest'); + return AddonEnrolGuest.getGuestEnrolmentInfo(instanceId, siteId); } @@ -350,7 +392,7 @@ export class CoreCoursesProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getCoursesCacheKey(ids), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, }; return site.read('core_course_get_courses', params, preSets); @@ -487,7 +529,7 @@ export class CoreCoursesProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getCoursesByFieldCacheKey(field, value), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), }; @@ -737,7 +779,7 @@ export class CoreCoursesProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getUserAdministrationOptionsCacheKey(courseIds), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), }; @@ -807,7 +849,7 @@ export class CoreCoursesProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getUserNavigationOptionsCacheKey(courseIds), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), }; @@ -910,7 +952,7 @@ export class CoreCoursesProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getUserCoursesCacheKey(), getCacheUsingCacheKey: true, - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), }; @@ -957,7 +999,7 @@ export class CoreCoursesProvider { if (added.length || removed.length) { // At least 1 course was added or removed, trigger the event. - CoreEvents.trigger(CoreCoursesProvider.EVENT_MY_COURSES_CHANGED, { + CoreEvents.trigger(CORE_COURSES_MY_COURSES_CHANGED_EVENT, { added: added, removed: removed, }, site.getId()); @@ -1023,11 +1065,12 @@ export class CoreCoursesProvider { * * @param courseId Course ID. * @param siteId Site Id. If not defined, use current site. - * @returns Promise resolved when the data is invalidated. * @deprecated since 4.3 use CoreEnrol.invalidateCourseEnrolmentMethods instead. */ async invalidateCourseEnrolmentMethods(courseId: number, siteId?: string): Promise { - return CoreEnrol.invalidateCourseEnrolmentMethods(courseId, siteId); + const { CoreEnrol } = await import('@features/enrol/services/enrol'); + + await CoreEnrol.invalidateCourseEnrolmentMethods(courseId, siteId); } /** @@ -1035,11 +1078,12 @@ export class CoreCoursesProvider { * * @param instanceId Guest instance ID. * @param siteId Site Id. If not defined, use current site. - * @returns Promise resolved when the data is invalidated. * @deprecated since 4.3 use CoreEnrolDelegate.invalidate instead. */ async invalidateCourseGuestEnrolmentInfo(instanceId: number, siteId?: string): Promise { - return AddonEnrolGuest.invalidateGuestEnrolmentInfo(instanceId, siteId); + const { AddonEnrolGuest } = await import('@addons/enrol/guest/services/guest'); + + await AddonEnrolGuest.invalidateGuestEnrolmentInfo(instanceId, siteId); } /** @@ -1231,10 +1275,12 @@ export class CoreCoursesProvider { * @param instanceId Enrol instance ID. * @param siteId Site ID. If not defined, use current site. * @returns Promise resolved if the user is enrolled. If the password is invalid, the promise is rejected - * with an object with errorcode = CoreCoursesProvider.ENROL_INVALID_KEY. + * with an object with errorcode = CORE_COURSES_ENROL_INVALID_KEY. * @deprecated since 4.3 use CoreEnrolDelegate.enrol instead. */ async selfEnrol(courseId: number, password: string = '', instanceId?: number, siteId?: string): Promise { + const { AddonEnrolSelf } = await import('@addons/enrol/self/services/self'); + return AddonEnrolSelf.selfEnrol(courseId, password, instanceId, siteId); } @@ -1281,7 +1327,7 @@ export class CoreCoursesProvider { } this.downloadOptionsEnabled = enable; - CoreEvents.trigger(CoreCoursesProvider.EVENT_DASHBOARD_DOWNLOAD_ENABLED_CHANGED, { enabled: enable }); + CoreEvents.trigger(CORE_COURSES_DASHBOARD_DOWNLOAD_ENABLED_CHANGED_EVENT, { enabled: enable }); } } @@ -1289,10 +1335,10 @@ export class CoreCoursesProvider { export const CoreCourses = makeSingleton(CoreCoursesProvider); /** - * Data sent to the EVENT_MY_COURSES_UPDATED. + * Data sent to the CORE_COURSES_MY_COURSES_UPDATED_EVENT. */ export type CoreCoursesMyCoursesUpdatedEventData = { - action: string; // Action performed. + action: CoreCoursesMyCoursesUpdatedEventAction; // Action performed. courseId?: number; // Course ID affected (if any). course?: CoreCourseAnyCourseData; // Course affected (if any). state?: string; // Only for ACTION_STATE_CHANGED. The state that changed (hidden, favourite). @@ -1300,7 +1346,7 @@ export type CoreCoursesMyCoursesUpdatedEventData = { }; /** - * Data sent to the EVENT_MY_COURSES_CHANGED. + * Data sent to the CORE_COURSES_MY_COURSES_CHANGED_EVENT. */ export type CoreCoursesMyCoursesChangedEventData = { added: number[]; @@ -1308,7 +1354,7 @@ export type CoreCoursesMyCoursesChangedEventData = { }; /** - * Data sent to the EVENT_DASHBOARD_DOWNLOAD_ENABLED_CHANGED. + * Data sent to the CORE_COURSES_DASHBOARD_DOWNLOAD_ENABLED_CHANGED_EVENT. */ export type CoreCoursesDashboardDownloadEnabledChangedEventData = { enabled: boolean; diff --git a/src/core/features/courses/services/dashboard.ts b/src/core/features/courses/services/dashboard.ts index 9b687000dc1..b1c628d36e5 100644 --- a/src/core/features/courses/services/dashboard.ts +++ b/src/core/features/courses/services/dashboard.ts @@ -23,6 +23,7 @@ import { map } from 'rxjs/operators'; import { firstValueFrom } from 'rxjs'; import { asyncObservable } from '@/core/utils/rxjs'; import { CoreSiteWSPreSets, WSObservable } from '@classes/sites/authenticated-site'; +import { CoreCacheUpdateFrequency } from '@/core/constants'; const ROOT_CACHE_KEY = 'CoreCoursesDashboard:'; @@ -90,7 +91,7 @@ export class CoreCoursesDashboardProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getDashboardBlocksCacheKey(myPage, options.userId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), }; if (options.userId) { diff --git a/src/core/features/courses/services/handlers/base-link-handler.ts b/src/core/features/courses/services/handlers/base-link-handler.ts index e8cf4072081..7e09c897a73 100644 --- a/src/core/features/courses/services/handlers/base-link-handler.ts +++ b/src/core/features/courses/services/handlers/base-link-handler.ts @@ -17,7 +17,7 @@ import { CoreContentLinksHandlerBase } from '@features/contentlinks/classes/base import { CoreCourseHelper } from '@features/course/services/course-helper'; import { CoreCourses } from '../courses'; import { Params } from '@angular/router'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreNavigator } from '@services/navigator'; import { CoreContentLinksAction } from '@features/contentlinks/services/contentlinks-delegate'; import { CoreSites } from '@services/sites'; @@ -49,7 +49,7 @@ export class CoreCoursesLinksHandlerBase extends CoreContentLinksHandlerBase { return; } - await CoreUtils.ignoreErrors(this.actionOpen(courseId, url, pageParams)); + await CorePromiseUtils.ignoreErrors(this.actionOpen(courseId, url, pageParams)); return; } @@ -86,7 +86,7 @@ export class CoreCoursesLinksHandlerBase extends CoreContentLinksHandlerBase { if (hasAccess && !guestInfo.guestAccess && !guestInfo.requiresUserInput) { // Direct access. - const course = await CoreUtils.ignoreErrors(CoreCourses.getUserCourse(courseId), { id: courseId }); + const course = await CorePromiseUtils.ignoreErrors(CoreCourses.getUserCourse(courseId), { id: courseId }); CoreCourseHelper.openCourse(course, { params: pageParams }); } else { diff --git a/src/core/features/courses/services/handlers/dashboard-home.ts b/src/core/features/courses/services/handlers/dashboard-home.ts index 343a5c20b56..17858c1d3f2 100644 --- a/src/core/features/courses/services/handlers/dashboard-home.ts +++ b/src/core/features/courses/services/handlers/dashboard-home.ts @@ -16,7 +16,7 @@ import { Injectable } from '@angular/core'; import { CoreBlockDelegate } from '@features/block/services/block-delegate'; import { CoreMainMenuHomeHandler, CoreMainMenuHomeHandlerToDisplay } from '@features/mainmenu/services/home-delegate'; import { CoreSites } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { makeSingleton } from '@singletons'; import { CoreLogger } from '@singletons/logger'; import { CoreCoursesDashboard } from '../dashboard'; @@ -60,7 +60,7 @@ export class CoreDashboardHomeHandlerService implements CoreMainMenuHomeHandler CoreBlockDelegate.areBlocksDisabled(site.getId()), CoreCoursesDashboard.isDisabled(site.getId()), CoreCoursesDashboard.isAvailable(site.getId()), - CoreUtils.ignoreErrors(site.getConfig('enabledashboard'), '1'), + CorePromiseUtils.ignoreErrors(site.getConfig('enabledashboard'), '1'), ]); const dashboardEnabled = !dashboardDisabled && dashboardConfig !== '0'; diff --git a/src/core/features/courses/services/handlers/enrol-push-click.ts b/src/core/features/courses/services/handlers/enrol-push-click.ts index c59dd189cd1..424fd4e28a3 100644 --- a/src/core/features/courses/services/handlers/enrol-push-click.ts +++ b/src/core/features/courses/services/handlers/enrol-push-click.ts @@ -20,7 +20,7 @@ import { CorePushNotificationsNotificationBasicData } from '@features/pushnotifi import { CoreLoadings } from '@services/loadings'; import { CoreNavigator } from '@services/navigator'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { makeSingleton } from '@singletons'; /** diff --git a/src/core/features/courses/services/handlers/request-push-click.ts b/src/core/features/courses/services/handlers/request-push-click.ts index 731589af346..265cc1d8038 100644 --- a/src/core/features/courses/services/handlers/request-push-click.ts +++ b/src/core/features/courses/services/handlers/request-push-click.ts @@ -20,11 +20,12 @@ import { CorePushNotificationsNotificationBasicData } from '@features/pushnotifi import { CoreNavigator } from '@services/navigator'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { makeSingleton } from '@singletons'; import { CorePath } from '@singletons/path'; import { CoreCourses } from '../courses'; import { CoreLoadings } from '@services/loadings'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Handler for course request push notifications clicks. @@ -69,7 +70,7 @@ export class CoreCoursesRequestPushClickHandlerService implements CorePushNotifi // Open the course. const modal = await CoreLoadings.show(); - await CoreUtils.ignoreErrors(CoreCourses.invalidateUserCourses(notification.site)); + await CorePromiseUtils.ignoreErrors(CoreCourses.invalidateUserCourses(notification.site)); try { const result = await CoreCourseHelper.getCourse(courseId, notification.site); diff --git a/src/core/features/courses/services/handlers/section-link.ts b/src/core/features/courses/services/handlers/section-link.ts index 933debb7cd8..617dc743d7e 100644 --- a/src/core/features/courses/services/handlers/section-link.ts +++ b/src/core/features/courses/services/handlers/section-link.ts @@ -20,7 +20,7 @@ import { CoreSites, CoreSitesReadingStrategy } from '@services/sites'; import { CoreCourse } from '@features/course/services/course'; import { CoreCoursesLinksHandlerBase } from '@features/courses/services/handlers/base-link-handler'; import { CoreLogger } from '@singletons/logger'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Handler to treat links to course section. @@ -85,12 +85,12 @@ export class CoreCoursesSectionLinkHandlerService extends CoreCoursesLinksHandle // Given that getting all the courses from a user could be very network intensive, the following // requests will only use cache. - const courses = await CoreUtils.ignoreErrors( + const courses = await CorePromiseUtils.ignoreErrors( CoreCourses.getUserCourses(true, siteId, CoreSitesReadingStrategy.ONLY_CACHE), ) ?? []; for (const course of courses) { - const courseSections = await CoreUtils.ignoreErrors(CoreCourse.getSections( + const courseSections = await CorePromiseUtils.ignoreErrors(CoreCourse.getSections( course.id, true, true, diff --git a/src/core/features/dataprivacy/components/contactdpo/contactdpo.ts b/src/core/features/dataprivacy/components/contactdpo/contactdpo.ts index 467b2334059..f9cce9830fb 100644 --- a/src/core/features/dataprivacy/components/contactdpo/contactdpo.ts +++ b/src/core/features/dataprivacy/components/contactdpo/contactdpo.ts @@ -20,7 +20,7 @@ import { CoreUser } from '@features/user/services/user'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreToasts, ToastDuration } from '@services/toasts'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { ModalController } from '@singletons'; import { CoreLoadings } from '@services/loadings'; @@ -62,7 +62,7 @@ export class CoreDataPrivacyContactDPOComponent implements OnInit { // Get current user email. const userId = CoreSites.getCurrentSiteUserId(); - const user = await CoreUtils.ignoreErrors(CoreUser.getProfile(userId)); + const user = await CorePromiseUtils.ignoreErrors(CoreUser.getProfile(userId)); this.email = user?.email || ''; diff --git a/src/core/features/dataprivacy/dataprivacy-lazy.module.ts b/src/core/features/dataprivacy/dataprivacy-lazy.module.ts index b0431a131b3..18e5bc91f85 100644 --- a/src/core/features/dataprivacy/dataprivacy-lazy.module.ts +++ b/src/core/features/dataprivacy/dataprivacy-lazy.module.ts @@ -35,4 +35,4 @@ const routes: Routes = [ CoreDataPrivacyMainPage, ], }) -export class CoreDataPrivacyLazyModule {} +export default class CoreDataPrivacyLazyModule {} diff --git a/src/core/features/dataprivacy/dataprivacy.module.ts b/src/core/features/dataprivacy/dataprivacy.module.ts index fdf75e04e87..75e9e7de5fa 100644 --- a/src/core/features/dataprivacy/dataprivacy.module.ts +++ b/src/core/features/dataprivacy/dataprivacy.module.ts @@ -25,7 +25,7 @@ import { CoreDataPrivacyCreateDataRequestLinkHandler } from './services/handlers const routes: Routes = [ { path: CORE_DATAPRIVACY_PAGE_NAME, - loadChildren: () => import('./dataprivacy-lazy.module').then(m => m.CoreDataPrivacyLazyModule), + loadChildren: () => import('./dataprivacy-lazy.module'), }, ]; diff --git a/src/core/features/dataprivacy/pages/main/main.ts b/src/core/features/dataprivacy/pages/main/main.ts index 35468473f0a..3363004b847 100644 --- a/src/core/features/dataprivacy/pages/main/main.ts +++ b/src/core/features/dataprivacy/pages/main/main.ts @@ -24,7 +24,7 @@ import { CoreModals } from '@services/modals'; import { CoreNavigator } from '@services/navigator'; import { CoreScreen } from '@services/screen'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { Translate } from '@singletons'; import { Subscription } from 'rxjs'; @@ -102,11 +102,11 @@ export class CoreDataPrivacyMainPage implements OnInit { * @param refresher Refresher. */ async refreshContent(refresher?: HTMLIonRefresherElement): Promise { - await CoreUtils.ignoreErrors( + await CorePromiseUtils.ignoreErrors( CoreDataPrivacy.invalidateAll(), ); - await CoreUtils.ignoreErrors(this.fetchContent()); + await CorePromiseUtils.ignoreErrors(this.fetchContent()); refresher?.complete(); } diff --git a/src/core/features/editor/components/rich-text-editor/rich-text-editor.ts b/src/core/features/editor/components/rich-text-editor/rich-text-editor.ts index 3e8be900e26..516bdc18c6a 100644 --- a/src/core/features/editor/components/rich-text-editor/rich-text-editor.ts +++ b/src/core/features/editor/components/rich-text-editor/rich-text-editor.ts @@ -32,7 +32,7 @@ import { CoreSites } from '@services/sites'; import { CoreFilepool } from '@services/filepool'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreUrl } from '@singletons/url'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { CoreEventFormActionData, CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreEditorOffline } from '../../services/editor-offline'; import { CoreDirectivesRegistry } from '@singletons/directives-registry'; @@ -60,7 +60,7 @@ import { CoreQRScan } from '@services/qrscan'; @Component({ selector: 'core-rich-text-editor', templateUrl: 'core-editor-rich-text-editor.html', - styleUrls: ['rich-text-editor.scss'], + styleUrl: 'rich-text-editor.scss', }) export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit, OnDestroy { diff --git a/src/core/features/editor/services/editor-offline.ts b/src/core/features/editor/services/editor-offline.ts index b6a7e912fe4..b8f84836ad8 100644 --- a/src/core/features/editor/services/editor-offline.ts +++ b/src/core/features/editor/services/editor-offline.ts @@ -16,7 +16,7 @@ import { Injectable } from '@angular/core'; import { CoreError } from '@classes/errors/error'; import { CoreSites } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreObject } from '@singletons/object'; import { makeSingleton } from '@singletons'; import { CoreLogger } from '@singletons/logger'; import { CoreEditorDraft, CoreEditorDraftPrimaryData, DRAFT_TABLE } from './database/editor'; @@ -82,7 +82,7 @@ export class CoreEditorOfflineProvider { contextlevel: contextLevel, contextinstanceid: contextInstanceId, elementid: elementId, - extraparams: CoreUtils.sortAndStringify(extraParams || {}), + extraparams: CoreObject.sortAndStringify(extraParams || {}), }; } diff --git a/src/core/features/emulator/components/capture-media/capture-media.ts b/src/core/features/emulator/components/capture-media/capture-media.ts index 98891194ed0..cd83912a3bb 100644 --- a/src/core/features/emulator/components/capture-media/capture-media.ts +++ b/src/core/features/emulator/components/capture-media/capture-media.ts @@ -33,7 +33,7 @@ import { CoreLoadings } from '@services/loadings'; @Component({ selector: 'core-emulator-capture-media', templateUrl: 'capture-media.html', - styleUrls: ['capture-media.scss'], + styleUrl: 'capture-media.scss', }) export class CoreEmulatorCaptureMediaComponent implements OnInit, OnDestroy { diff --git a/src/core/features/enrol/services/enrol-helper.ts b/src/core/features/enrol/services/enrol-helper.ts index 72ef71821de..2290141f798 100644 --- a/src/core/features/enrol/services/enrol-helper.ts +++ b/src/core/features/enrol/services/enrol-helper.ts @@ -15,7 +15,7 @@ import { Injectable } from '@angular/core'; import { makeSingleton } from '@singletons'; import { CoreEnrolAction, CoreEnrolDelegate, CoreEnrolInfoIcon } from './enrol-delegate'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { CoreEnrol, CoreEnrolEnrolmentMethod } from './enrol'; import { CoreArray } from '@singletons/array'; diff --git a/src/core/features/enrol/services/enrol.ts b/src/core/features/enrol/services/enrol.ts index b840b457eb4..a552c218c76 100644 --- a/src/core/features/enrol/services/enrol.ts +++ b/src/core/features/enrol/services/enrol.ts @@ -14,10 +14,10 @@ import { Injectable } from '@angular/core'; import { makeSingleton } from '@singletons'; -import { CoreSite } from '@classes/sites/site'; import { CoreSites } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { CoreEnrolAction, CoreEnrolDelegate } from './enrol-delegate'; +import { CoreCacheUpdateFrequency } from '@/core/constants'; /** * Service that provides functions for enrolment plugins. @@ -44,7 +44,7 @@ export class CoreEnrolService { }; const preSets = { cacheKey: this.getCourseEnrolmentMethodsCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, }; return site.read('core_enrol_get_course_enrolment_methods', params, preSets); diff --git a/src/core/features/fileuploader/services/fileuploader-helper.ts b/src/core/features/fileuploader/services/fileuploader-helper.ts index c79a5541370..745b37bd7b2 100644 --- a/src/core/features/fileuploader/services/fileuploader-helper.ts +++ b/src/core/features/fileuploader/services/fileuploader-helper.ts @@ -24,7 +24,7 @@ import { CoreFile, CoreFileProvider, CoreFileProgressEvent } from '@services/fil import { CoreDomUtils } from '@services/utils/dom'; import { CoreMimetypeUtils } from '@services/utils/mimetype'; import { CoreText } from '@singletons/text'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreArray } from '@singletons/array'; import { makeSingleton, Translate, Camera, ActionSheetController } from '@singletons'; import { CoreLogger } from '@singletons/logger'; import { CoreCanceledError } from '@classes/errors/cancelederror'; @@ -46,6 +46,7 @@ import { CorePlatform } from '@services/platform'; import { Chooser } from '@features/native/plugins'; import { CoreToasts } from '@services/toasts'; import { CoreLoadings } from '@services/loadings'; +import { CoreFileUtils } from '@singletons/file-utils'; /** * Helper service to upload files. @@ -210,7 +211,7 @@ export class CoreFileUploaderHelperProvider { options?: CoreFileUploaderOptions, ): Promise { - const fileName = options?.fileName || CoreFile.getFileAndDirectoryFromPath(path).name; + const fileName = options?.fileName || CoreFileUtils.getFileAndDirectoryFromPath(path).name; // Check that size isn't too large. if (maxSize !== undefined && maxSize != -1) { @@ -283,7 +284,7 @@ export class CoreFileUploaderHelperProvider { * @returns File name, undefined if cannot get it. */ protected getChosenFileNameFromPath(result: ChooserResult): string | undefined { - const nameAndDir = CoreFile.getFileAndDirectoryFromPath(result.uri); + const nameAndDir = CoreFileUtils.getFileAndDirectoryFromPath(result.uri); if (!nameAndDir.name) { return; @@ -634,8 +635,8 @@ export class CoreFileUploaderHelperProvider { }; if (fromAlbum) { - const imageSupported = !mimetypes || CoreUtils.indexOfRegexp(mimetypes, /^image\//) > -1; - const videoSupported = !mimetypes || CoreUtils.indexOfRegexp(mimetypes, /^video\//) > -1; + const imageSupported = !mimetypes || CoreArray.indexOfRegexp(mimetypes, /^image\//) > -1; + const videoSupported = !mimetypes || CoreArray.indexOfRegexp(mimetypes, /^video\//) > -1; options.sourceType = Camera.PictureSourceType.PHOTOLIBRARY; options.popoverOptions = { diff --git a/src/core/features/fileuploader/services/fileuploader.ts b/src/core/features/fileuploader/services/fileuploader.ts index a4ca5c9d0fd..6a4add07a49 100644 --- a/src/core/features/fileuploader/services/fileuploader.ts +++ b/src/core/features/fileuploader/services/fileuploader.ts @@ -19,11 +19,12 @@ import { MediaFile, CaptureError, CaptureVideoOptions } from '@awesome-cordova-p import { Subject } from 'rxjs'; import { CoreFile, CoreFileProvider } from '@services/file'; +import { CoreFileUtils } from '@singletons/file-utils'; import { CoreFilepool } from '@services/filepool'; import { CoreSites } from '@services/sites'; import { CoreMimetypeUtils } from '@services/utils/mimetype'; import { CoreTimeUtils } from '@services/utils/time'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { CoreWSFile, CoreWSFileUploadOptions, CoreWSUploadFileResult } from '@services/ws'; import { makeSingleton, Translate, MediaCapture, Camera } from '@singletons'; import { CoreLogger } from '@singletons/logger'; @@ -33,6 +34,7 @@ import { CoreFileEntry, CoreFileHelper } from '@services/file-helper'; import { CorePath } from '@singletons/path'; import { CorePlatform } from '@services/platform'; import { CoreModals } from '@services/modals'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * File upload options. @@ -241,7 +243,7 @@ export class CoreFileUploaderProvider { deleteAfterUpload: !isFromAlbum, mimeType: mimetype, }; - const fileName = CoreFile.getFileAndDirectoryFromPath(uri).name; + const fileName = CoreFileUtils.getFileAndDirectoryFromPath(uri).name; if (isIOS && (mimetype == 'image/jpeg' || mimetype == 'image/png')) { // In iOS, the pictures can have repeated names, even if they come from the album. @@ -285,7 +287,7 @@ export class CoreFileUploaderProvider { originalFiles.forEach((file) => { const stillInList = currentFiles.some((currentFile) => - CoreFileHelper.getFileUrl( currentFile) == CoreFileHelper.getFileUrl(file)); + CoreFileHelper.getFileUrl( currentFile) === CoreFileHelper.getFileUrl(file)); if (!stillInList) { filesToDelete.push({ @@ -401,7 +403,7 @@ export class CoreFileUploaderProvider { } if (filesObject.offline > 0) { - const offlineFiles = await CoreUtils.ignoreErrors(this.getStoredFiles(folderPath)); + const offlineFiles = await CorePromiseUtils.ignoreErrors(this.getStoredFiles(folderPath)); if (offlineFiles) { files = files.concat(offlineFiles); @@ -556,7 +558,7 @@ export class CoreFileUploaderProvider { await CoreFile.removeUnusedFiles(folderPath, files); await Promise.all(files.map(async (file) => { - if (!CoreUtils.isFileEntry(file)) { + if (!CoreFileUtils.isFileEntry(file)) { // It's an online file, add it to the result and ignore it. result.online.push({ filename: file.filename, @@ -631,7 +633,7 @@ export class CoreFileUploaderProvider { const usedNames: {[name: string]: CoreFileEntry} = {}; const filesToUpload: FileEntry[] = []; files.forEach((file) => { - if (CoreUtils.isFileEntry(file)) { + if (CoreFileUtils.isFileEntry(file)) { filesToUpload.push( file); } else { // It's an online file. @@ -678,9 +680,9 @@ export class CoreFileUploaderProvider { let fileName = ''; let fileEntry: FileEntry | undefined; - const isOnline = !CoreUtils.isFileEntry(file); + const isOnline = !CoreFileUtils.isFileEntry(file); - if (CoreUtils.isFileEntry(file)) { + if (CoreFileUtils.isFileEntry(file)) { // Local file, we already have the file entry. fileName = file.name; fileEntry = file; diff --git a/src/core/features/fileuploader/services/handlers/audio.ts b/src/core/features/fileuploader/services/handlers/audio.ts index a5e5dc573d6..13cb2a8efcf 100644 --- a/src/core/features/fileuploader/services/handlers/audio.ts +++ b/src/core/features/fileuploader/services/handlers/audio.ts @@ -14,7 +14,7 @@ import { Injectable } from '@angular/core'; -import { CoreApp } from '@services/app'; +import { CoreMedia } from '@singletons/media'; import { CorePlatform } from '@services/platform'; import { CoreArray } from '@singletons/array'; import { makeSingleton } from '@singletons'; @@ -34,7 +34,7 @@ export class CoreFileUploaderAudioHandlerService implements CoreFileUploaderHand * @inheritdoc */ async isEnabled(): Promise { - return CorePlatform.isMobile() || (CoreApp.canGetUserMedia() && CoreApp.canRecordMedia()); + return CorePlatform.isMobile() || (CoreMedia.canGetUserMedia() && CoreMedia.canRecordMedia()); } /** diff --git a/src/core/features/fileuploader/services/handlers/camera.ts b/src/core/features/fileuploader/services/handlers/camera.ts index 373b338fa24..9abf9cd3fc2 100644 --- a/src/core/features/fileuploader/services/handlers/camera.ts +++ b/src/core/features/fileuploader/services/handlers/camera.ts @@ -14,7 +14,7 @@ import { Injectable } from '@angular/core'; -import { CoreApp } from '@services/app'; +import { CoreMedia } from '@singletons/media'; import { CorePlatform } from '@services/platform'; import { CoreArray } from '@singletons/array'; import { makeSingleton } from '@singletons'; @@ -34,7 +34,7 @@ export class CoreFileUploaderCameraHandlerService implements CoreFileUploaderHan * @inheritdoc */ async isEnabled(): Promise { - return CorePlatform.isMobile() || CoreApp.canGetUserMedia(); + return CorePlatform.isMobile() || CoreMedia.canGetUserMedia(); } /** diff --git a/src/core/features/fileuploader/services/handlers/video.ts b/src/core/features/fileuploader/services/handlers/video.ts index ac655b5800e..605f92b38c9 100644 --- a/src/core/features/fileuploader/services/handlers/video.ts +++ b/src/core/features/fileuploader/services/handlers/video.ts @@ -14,7 +14,7 @@ import { Injectable } from '@angular/core'; -import { CoreApp } from '@services/app'; +import { CoreMedia } from '@singletons/media'; import { CorePlatform } from '@services/platform'; import { CoreArray } from '@singletons/array'; import { makeSingleton } from '@singletons'; @@ -34,7 +34,7 @@ export class CoreFileUploaderVideoHandlerService implements CoreFileUploaderHand * @inheritdoc */ async isEnabled(): Promise { - return CorePlatform.isMobile() || (CoreApp.canGetUserMedia() && CoreApp.canRecordMedia()); + return CorePlatform.isMobile() || (CoreMedia.canGetUserMedia() && CoreMedia.canRecordMedia()); } /** diff --git a/src/core/features/filter/services/filter-helper.ts b/src/core/features/filter/services/filter-helper.ts index 5d84c55abd2..104e3be2dde 100644 --- a/src/core/features/filter/services/filter-helper.ts +++ b/src/core/features/filter/services/filter-helper.ts @@ -34,8 +34,8 @@ import { CoreLogger } from '@singletons/logger'; import { CoreSite } from '@classes/sites/site'; import { CoreCourseHelper } from '@features/course/services/course-helper'; import { firstValueFrom } from 'rxjs'; -import { ContextLevel } from '@/core/constants'; -import { CoreUtils } from '@services/utils/utils'; +import { ContextLevel, CoreCacheUpdateFrequency } from '@/core/constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Helper service to provide filter functionalities. @@ -118,7 +118,7 @@ export class CoreFilterHelperProvider { async getCategoryContexts(categoryId: number, siteId?: string): Promise { // Get the categories of courses the user is enrolled in to decrease the number of WS requests. // Using CoreCourses.getCategories would group more categories, but it would require a new WS request. - const courses = await CoreUtils.ignoreErrors(CoreCourses.getUserCourses(true, siteId)); + const courses = await CorePromiseUtils.ignoreErrors(CoreCourses.getUserCourses(true, siteId)); const categoriesIds = (courses ?? []).map(course => course.categoryid) .filter((categoryId): categoryId is number => categoryId !== undefined); @@ -427,7 +427,7 @@ export class CoreFilterHelperProvider { const cachedData = this.moduleContextsCache[siteId][courseId][contextLevel]; - if (!CoreNetwork.isOnline() || Date.now() <= cachedData.time + site.getExpirationDelay(CoreSite.FREQUENCY_RARELY)) { + if (!CoreNetwork.isOnline() || Date.now() <= cachedData.time + site.getExpirationDelay(CoreCacheUpdateFrequency.RARELY)) { // We can use cache, return the filters if found. return cachedData.contexts[contextLevel] && cachedData.contexts[contextLevel][instanceId]; } diff --git a/src/core/features/filter/services/filter.ts b/src/core/features/filter/services/filter.ts index e85e46edd11..99d6fbb35b1 100644 --- a/src/core/features/filter/services/filter.ts +++ b/src/core/features/filter/services/filter.ts @@ -24,7 +24,7 @@ import { makeSingleton } from '@singletons'; import { CoreEvents, CoreEventSiteData } from '@singletons/events'; import { CoreLogger } from '@singletons/logger'; import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site'; -import { ContextLevel } from '@/core/constants'; +import { ContextLevel, CoreCacheUpdateFrequency } from '@/core/constants'; /** * Service to provide filter functionalities. @@ -288,7 +288,7 @@ export class CoreFilterProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getAllStatesCacheKey(), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, // Use stale while revalidate by default, but always use the first value. If data is updated it will be stored in DB. ...CoreSites.getReadingStrategyPreSets(options.readingStrategy ?? CoreSitesReadingStrategy.STALE_WHILE_REVALIDATE), }; @@ -364,7 +364,7 @@ export class CoreFilterProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getAvailableInContextsCacheKey(contextsToSend), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, splitRequest: { param: 'contexts', maxLength: 300, @@ -428,7 +428,7 @@ export class CoreFilterProvider { // Check the context isn't "expired". The time stored in this cache will not match the one in the site cache. if (cachedCtxt && (!isOnline || - Date.now() <= cachedCtxt.time + site.getExpirationDelay(CoreSite.FREQUENCY_RARELY))) { + Date.now() <= cachedCtxt.time + site.getExpirationDelay(CoreCacheUpdateFrequency.RARELY))) { result[context.contextlevel] = result[context.contextlevel] || {}; result[context.contextlevel][context.instanceid] = cachedCtxt.filters; diff --git a/src/core/features/grades/grades-course-lazy.module.ts b/src/core/features/grades/grades-course-lazy.module.ts index 877a0fec93a..c3ccc8947e2 100644 --- a/src/core/features/grades/grades-course-lazy.module.ts +++ b/src/core/features/grades/grades-course-lazy.module.ts @@ -31,4 +31,4 @@ const routes: Routes = [ CoreGradesCoursePageModule, ], }) -export class CoreGradesCourseLazyModule {} +export default class CoreGradesCourseLazyModule {} diff --git a/src/core/features/grades/grades-course-participants-lazy.module.ts b/src/core/features/grades/grades-course-participants-lazy.module.ts index 68400a44320..a9e1abce51d 100644 --- a/src/core/features/grades/grades-course-participants-lazy.module.ts +++ b/src/core/features/grades/grades-course-participants-lazy.module.ts @@ -27,7 +27,7 @@ const routes: Routes = [ children: conditionalRoutes([ { path: ':userId', - loadChildren: () => import('./grades-course-lazy.module').then(m => m.CoreGradesCourseLazyModule), + loadChildren: () => import('./grades-course-lazy.module'), data: { swipeManagerSource: 'participants' }, }, ], () => CoreScreen.isTablet), @@ -40,4 +40,4 @@ const routes: Routes = [ CoreUserParticipantsPageModule, ], }) -export class CoreGradesCourseParticipantsLazyModule {} +export default class CoreGradesCourseParticipantsLazyModule {} diff --git a/src/core/features/grades/grades-courses-lazy.module.ts b/src/core/features/grades/grades-courses-lazy.module.ts index 785cd4897a0..821e5546f7c 100644 --- a/src/core/features/grades/grades-courses-lazy.module.ts +++ b/src/core/features/grades/grades-courses-lazy.module.ts @@ -62,4 +62,4 @@ const routes: Routes = [ CoreGradesCoursesPage, ], }) -export class CoreGradesCoursesLazyModule {} +export default class CoreGradesCoursesLazyModule {} diff --git a/src/core/features/grades/grades.module.ts b/src/core/features/grades/grades.module.ts index dea9a2f1b85..40527316fa3 100644 --- a/src/core/features/grades/grades.module.ts +++ b/src/core/features/grades/grades.module.ts @@ -29,7 +29,7 @@ import { CoreGradesUserLinkHandler } from './services/handlers/user-link'; import { CoreGradesCourseParticipantsOptionHandler } from '@features/grades/services/handlers/course-participants-option'; import { conditionalRoutes } from '@/app/app-routing.module'; import { CoreScreen } from '@services/screen'; -import { COURSE_PAGE_NAME, COURSE_INDEX_PATH } from '@features/course/constants'; +import { CORE_COURSE_PAGE_NAME, CORE_COURSE_INDEX_PATH } from '@features/course/constants'; /** * Get grades services. @@ -49,17 +49,17 @@ export async function getGradesServices(): Promise[]> { const mainMenuChildrenRoutes: Routes = [ { path: GRADES_PAGE_NAME, - loadChildren: () => import('./grades-courses-lazy.module').then(m => m.CoreGradesCoursesLazyModule), + loadChildren: () => import('./grades-courses-lazy.module'), data: { swipeManagerSource: 'courses' }, }, { - path: `${COURSE_PAGE_NAME}/:courseId/${PARTICIPANTS_PAGE_NAME}/:userId/${GRADES_PAGE_NAME}`, - loadChildren: () => import('./grades-course-lazy.module').then(m => m.CoreGradesCourseLazyModule), + path: `${CORE_COURSE_PAGE_NAME}/:courseId/${PARTICIPANTS_PAGE_NAME}/:userId/${GRADES_PAGE_NAME}`, + loadChildren: () => import('./grades-course-lazy.module'), }, ...conditionalRoutes([ { - path: `${COURSE_PAGE_NAME}/${COURSE_INDEX_PATH}/${GRADES_PARTICIPANTS_PAGE_NAME}/:userId`, - loadChildren: () => import('./grades-course-lazy.module').then(m => m.CoreGradesCourseLazyModule), + path: `${CORE_COURSE_PAGE_NAME}/${CORE_COURSE_INDEX_PATH}/${GRADES_PARTICIPANTS_PAGE_NAME}/:userId`, + loadChildren: () => import('./grades-course-lazy.module'), data: { swipeManagerSource: 'participants' }, }, ], () => CoreScreen.isMobile), @@ -68,11 +68,11 @@ const mainMenuChildrenRoutes: Routes = [ const courseIndexRoutes: Routes = [ { path: GRADES_PAGE_NAME, - loadChildren: () => import('./grades-course-lazy.module').then(m => m.CoreGradesCourseLazyModule), + loadChildren: () => import('./grades-course-lazy.module'), }, { path: GRADES_PARTICIPANTS_PAGE_NAME, - loadChildren: () => import('./grades-course-participants-lazy.module').then(m => m.CoreGradesCourseParticipantsLazyModule), + loadChildren: () => import('./grades-course-participants-lazy.module'), }, ]; diff --git a/src/core/features/grades/pages/course/course.page.ts b/src/core/features/grades/pages/course/course.page.ts index 16f34780d7b..085f36489e0 100644 --- a/src/core/features/grades/pages/course/course.page.ts +++ b/src/core/features/grades/pages/course/course.page.ts @@ -24,7 +24,7 @@ import { CoreGradesHelper, } from '@features/grades/services/grades-helper'; import { CoreSites } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreNavigator } from '@services/navigator'; import { CoreScreen } from '@services/screen'; import { Translate } from '@singletons'; @@ -43,7 +43,7 @@ import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics'; @Component({ selector: 'page-core-grades-course', templateUrl: 'course.html', - styleUrls: ['course.scss'], + styleUrl: 'course.scss', }) export class CoreGradesCoursePage implements AfterViewInit, OnDestroy { @@ -185,8 +185,8 @@ export class CoreGradesCoursePage implements AfterViewInit, OnDestroy { * @param refresher Refresher. */ async refreshGrades(refresher: HTMLIonRefresherElement): Promise { - await CoreUtils.ignoreErrors(CoreGrades.invalidateCourseGradesData(this.courseId, this.userId)); - await CoreUtils.ignoreErrors(this.fetchGrades()); + await CorePromiseUtils.ignoreErrors(CoreGrades.invalidateCourseGradesData(this.courseId, this.userId)); + await CorePromiseUtils.ignoreErrors(this.fetchGrades()); refresher?.complete(); } @@ -261,7 +261,7 @@ export class CoreGradesCoursePage implements AfterViewInit, OnDestroy { * Log view. */ protected async performLogView(): Promise { - await CoreUtils.ignoreErrors(CoreGrades.logCourseGradesView(this.courseId, this.userId)); + await CorePromiseUtils.ignoreErrors(CoreGrades.logCourseGradesView(this.courseId, this.userId)); CoreAnalytics.logEvent({ type: CoreAnalyticsEventType.VIEW_ITEM, diff --git a/src/core/features/grades/pages/courses/courses.ts b/src/core/features/grades/pages/courses/courses.ts index 91f309cbb73..140bc18af1e 100644 --- a/src/core/features/grades/pages/courses/courses.ts +++ b/src/core/features/grades/pages/courses/courses.ts @@ -22,7 +22,7 @@ import { CoreGrades } from '@features/grades/services/grades'; import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { Translate } from '@singletons'; /** @@ -66,8 +66,8 @@ export class CoreGradesCoursesPage implements OnDestroy, AfterViewInit { * @param refresher Refresher. */ async refreshCourses(refresher: HTMLIonRefresherElement): Promise { - await CoreUtils.ignoreErrors(CoreGrades.invalidateCoursesGradesData()); - await CoreUtils.ignoreErrors(this.courses.reload()); + await CorePromiseUtils.ignoreErrors(CoreGrades.invalidateCoursesGradesData()); + await CorePromiseUtils.ignoreErrors(this.courses.reload()); refresher?.complete(); } diff --git a/src/core/features/grades/services/grades-helper.ts b/src/core/features/grades/services/grades-helper.ts index d09fa96c8d1..23bc939a3bc 100644 --- a/src/core/features/grades/services/grades-helper.ts +++ b/src/core/features/grades/services/grades-helper.ts @@ -22,7 +22,7 @@ import { CoreCourseSearchedData, CoreCourseUserAdminOrNavOptionIndexed, } from '@features/courses/services/courses'; -import { CoreCourse, CoreCourseAccessDataType } from '@features/course/services/course'; +import { CoreCourse } from '@features/course/services/course'; import { CoreGrades, CoreGradesGradeItem, @@ -35,7 +35,7 @@ import { } from '@features/grades/services/grades'; import { CoreText } from '@singletons/text'; import { CoreUrl } from '@singletons/url'; -import { CoreMenuItem, CoreUtils } from '@services/utils/utils'; +import { CoreMenuItem, CoreUtils } from '@singletons/utils'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreNavigator } from '@services/navigator'; import { makeSingleton, Translate } from '@singletons'; @@ -45,6 +45,9 @@ import { CoreCourseModuleDelegate } from '@features/course/services/module-deleg import { CoreCourseAccess } from '@features/course/services/course-options-delegate'; import { CoreLoadings } from '@services/loadings'; import { convertTextToHTMLElement } from '@/core/utils/create-html-element'; +import { CoreCourseAccessDataType } from '@features/course/constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; +import { CoreArray } from '@singletons/array'; export const GRADES_PAGE_NAME = 'grades'; export const GRADES_PARTICIPANTS_PAGE_NAME = 'participant-grades'; @@ -269,7 +272,7 @@ export class CoreGradesHelperProvider { try { const courses = await CoreCourses.getUserCourses(undefined, undefined, CoreSitesReadingStrategy.ONLY_CACHE); - const coursesMap = CoreUtils.arrayToObject(courses, 'id'); + const coursesMap = CoreArray.toObject(courses, 'id'); coursesWereMissing = this.addCourseData(grades, coursesMap); } catch { @@ -280,7 +283,7 @@ export class CoreGradesHelperProvider { if (coursesWereMissing) { const courses = await CoreCourses.getCoursesByField('ids', grades.map((grade) => grade.courseid).join(',')); const coursesMap = - CoreUtils.arrayToObject(courses as Record[], 'id') as + CoreArray.toObject(courses as Record[], 'id') as Record | Record; @@ -468,7 +471,7 @@ export class CoreGradesHelperProvider { // Open the item directly. const gradeId = item.id; - await CoreUtils.ignoreErrors( + await CorePromiseUtils.ignoreErrors( CoreNavigator.navigateToSitePath( `/${GRADES_PAGE_NAME}/${courseId}`, { params: { gradeId }, siteId }, @@ -479,7 +482,7 @@ export class CoreGradesHelperProvider { // Cannot get grade items or there's no need to. if (userId && userId != currentUserId) { // View another user grades. Open the grades page directly. - await CoreUtils.ignoreErrors( + await CorePromiseUtils.ignoreErrors( CoreNavigator.navigateToSitePath(`/${GRADES_PAGE_NAME}/${courseId}`, { siteId }), ); } diff --git a/src/core/features/grades/services/handlers/user.ts b/src/core/features/grades/services/handlers/user.ts index 3f1186416b0..c295c4c619c 100644 --- a/src/core/features/grades/services/handlers/user.ts +++ b/src/core/features/grades/services/handlers/user.ts @@ -13,7 +13,7 @@ // limitations under the License. import { Injectable } from '@angular/core'; -import { COURSE_PAGE_NAME } from '@features/course/constants'; +import { CORE_COURSE_PAGE_NAME } from '@features/course/constants'; import { CoreGrades } from '@features/grades/services/grades'; import { CoreUserProfile } from '@features/user/services/user'; @@ -26,7 +26,7 @@ import { import { PARTICIPANTS_PAGE_NAME } from '@features/user/constants'; import { CoreNavigator } from '@services/navigator'; import { CoreSites } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { makeSingleton } from '@singletons'; import { GRADES_PAGE_NAME } from '../grades-helper'; @@ -68,7 +68,7 @@ export class CoreGradesUserHandlerService implements CoreUserProfileHandler { } if (context === CoreUserDelegateContext.COURSE) { - return CoreUtils.ignoreErrors(CoreGrades.isPluginEnabledForCourse(courseId), false); + return CorePromiseUtils.ignoreErrors(CoreGrades.isPluginEnabledForCourse(courseId), false); } else { return CoreGrades.isCourseGradesEnabled(); } @@ -79,7 +79,7 @@ export class CoreGradesUserHandlerService implements CoreUserProfileHandler { */ async isEnabledForUser(user: CoreUserProfile, context: CoreUserDelegateContext, contextId: number): Promise { if (context === CoreUserDelegateContext.COURSE) { - return CoreUtils.promiseWorks(CoreGrades.getCourseGradesTable(contextId, user.id)); + return CorePromiseUtils.promiseWorks(CoreGrades.getCourseGradesTable(contextId, user.id)); } // All course grades only available for the current user. @@ -99,7 +99,7 @@ export class CoreGradesUserHandlerService implements CoreUserProfileHandler { event.preventDefault(); event.stopPropagation(); CoreNavigator.navigateToSitePath( - [COURSE_PAGE_NAME, contextId, PARTICIPANTS_PAGE_NAME, user.id, GRADES_PAGE_NAME].join('/'), + [CORE_COURSE_PAGE_NAME, contextId, PARTICIPANTS_PAGE_NAME, user.id, GRADES_PAGE_NAME].join('/'), ); }, }; diff --git a/src/core/features/h5p/classes/content-validator.ts b/src/core/features/h5p/classes/content-validator.ts index 25fa5bb6beb..26c58542929 100644 --- a/src/core/features/h5p/classes/content-validator.ts +++ b/src/core/features/h5p/classes/content-validator.ts @@ -13,11 +13,12 @@ // limitations under the License. import { CoreText } from '@singletons/text'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { CoreH5P } from '@features/h5p/services/h5p'; import { Translate } from '@singletons'; import { CoreH5PCore, CoreH5PLibraryData, CoreH5PLibraryAddonData, CoreH5PContentDepsTreeDependency } from './core'; import { CoreArray } from '@singletons/array'; +import { CoreObject } from '@singletons/object'; const ALLOWED_STYLEABLE_TAGS = ['span', 'p', 'div', 'h1', 'h2', 'h3', 'table', 'col', 'figure', 'td', 'th', 'li']; @@ -354,7 +355,7 @@ export class CoreH5PContentValidator { } if (!isArray) { - list = CoreUtils.objectToArray(> list); + list = CoreObject.toArray(> list); } if (!list.length) { @@ -677,7 +678,7 @@ export class CoreH5PContentValidator { */ protected filterXssSplit(tags: string[], store: boolean = false): string { if (store) { - this.allowedHtml = CoreUtils.arrayToObject(tags); + this.allowedHtml = CoreArray.toObject(tags); return ''; } diff --git a/src/core/features/h5p/classes/core.ts b/src/core/features/h5p/classes/core.ts index 59396bbefb2..fccdb027adc 100644 --- a/src/core/features/h5p/classes/core.ts +++ b/src/core/features/h5p/classes/core.ts @@ -16,7 +16,7 @@ import { Md5 } from 'ts-md5/dist/md5'; import { CoreSites } from '@services/sites'; import { CoreText } from '@singletons/text'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { CoreH5P } from '@features/h5p/services/h5p'; import { CoreH5PFileStorage } from './file-storage'; import { CoreH5PFramework } from './framework'; @@ -961,7 +961,7 @@ export class CoreH5PCore { if (params.match(pattern)) { return true; } - } else if (typeof params == 'object') { + } else if (typeof params === 'object') { for (const key in params) { const value = params[key]; diff --git a/src/core/features/h5p/classes/file-storage.ts b/src/core/features/h5p/classes/file-storage.ts index 50d1c93ea4e..afa16af1769 100644 --- a/src/core/features/h5p/classes/file-storage.ts +++ b/src/core/features/h5p/classes/file-storage.ts @@ -17,7 +17,7 @@ import { CoreFilepool } from '@services/filepool'; import { CoreSites } from '@services/sites'; import { CoreMimetypeUtils } from '@services/utils/mimetype'; import { CoreText } from '@singletons/text'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CorePath } from '@singletons/path'; import { CoreH5PCore, @@ -29,6 +29,7 @@ import { } from './core'; import { CONTENTS_LIBRARIES_TABLE_NAME, CONTENT_TABLE_NAME, CoreH5PLibraryCachedAssetsDBRecord } from '../services/database/h5p'; import { CoreH5PLibraryBeingSaved } from './storage'; +import { CoreFileUtils } from '@singletons/file-utils'; /** * Equivalent to Moodle's implementation of H5PFileStorage. @@ -115,7 +116,7 @@ export class CoreH5PFileStorage { } treated[url] = url; - const assetPathFolder = CoreFile.getFileAndDirectoryFromPath(assetPath).directory; + const assetPathFolder = CoreFileUtils.getFileAndDirectoryFromPath(assetPath).directory; fileContent = fileContent.replace( new RegExp(CoreText.escapeForRegex(match), 'g'), @@ -154,7 +155,7 @@ export class CoreH5PFileStorage { }); // Ignore errors, maybe there's no cached asset of some type. - await CoreUtils.ignoreErrors(CoreUtils.allPromises(promises)); + await CorePromiseUtils.allPromisesIgnoringErrors(promises); } /** @@ -300,7 +301,7 @@ export class CoreH5PFileStorage { async getContentFolderNameByUrl(fileUrl: string, siteId: string): Promise { const path = await CoreFilepool.getFilePathByUrl(siteId, fileUrl); - const fileAndDir = CoreFile.getFileAndDirectoryFromPath(path); + const fileAndDir = CoreFileUtils.getFileAndDirectoryFromPath(path); return CoreMimetypeUtils.removeExtension(fileAndDir.name); } @@ -418,7 +419,7 @@ export class CoreH5PFileStorage { const folderPath = this.getContentFolderPath(folderName, siteId); // Delete existing content for this package. - await CoreUtils.ignoreErrors(CoreFile.removeDir(folderPath)); + await CorePromiseUtils.ignoreErrors(CoreFile.removeDir(folderPath)); // Copy the new one. await CoreFile.moveDir(contentPath, folderPath); diff --git a/src/core/features/h5p/classes/helper.ts b/src/core/features/h5p/classes/helper.ts index 6bb2f94215c..375f7add3e8 100644 --- a/src/core/features/h5p/classes/helper.ts +++ b/src/core/features/h5p/classes/helper.ts @@ -17,7 +17,6 @@ import { FileEntry } from '@awesome-cordova-plugins/file/ngx'; import { CoreFile, CoreFileProvider } from '@services/file'; import { CoreSites } from '@services/sites'; import { CoreMimetypeUtils } from '@services/utils/mimetype'; -import { CoreUtils } from '@services/utils/utils'; import { CoreH5P } from '../services/h5p'; import { CoreH5PCore, CoreH5PDisplayOptions, CoreH5PLocalization } from './core'; import { CoreError } from '@classes/errors/error'; @@ -56,10 +55,8 @@ export class CoreH5PHelper { const config: CoreH5PDisplayOptions = { export: false, // Don't allow downloading in the app. embed: false, // Don't display the embed button in the app. - copyright: CoreUtils.notNullOrUndefined(displayOptionsObject[CoreH5PCore.DISPLAY_OPTION_COPYRIGHT]) ? - displayOptionsObject[CoreH5PCore.DISPLAY_OPTION_COPYRIGHT] : false, - icon: CoreUtils.notNullOrUndefined(displayOptionsObject[CoreH5PCore.DISPLAY_OPTION_ABOUT]) ? - displayOptionsObject[CoreH5PCore.DISPLAY_OPTION_ABOUT] : false, + copyright: displayOptionsObject[CoreH5PCore.DISPLAY_OPTION_COPYRIGHT] ?? false, + icon: displayOptionsObject[CoreH5PCore.DISPLAY_OPTION_ABOUT] ?? false, }; config.frame = config.copyright || config.export || config.embed; diff --git a/src/core/features/h5p/classes/player.ts b/src/core/features/h5p/classes/player.ts index 3dcdadb607a..1da7d60cb66 100644 --- a/src/core/features/h5p/classes/player.ts +++ b/src/core/features/h5p/classes/player.ts @@ -15,13 +15,14 @@ import { CoreFile } from '@services/file'; import { CoreSites } from '@services/sites'; import { CoreUrl } from '@singletons/url'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { CoreH5P } from '../services/h5p'; import { CoreH5PCore, CoreH5PDisplayOptions, CoreH5PContentData, CoreH5PDependenciesFiles } from './core'; import { CoreH5PCoreSettings, CoreH5PHelper } from './helper'; import { CoreH5PStorage } from './storage'; import { CorePath } from '@singletons/path'; import { CoreXAPIIRI } from '@features/xapi/classes/iri'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Equivalent to Moodle's H5P player class. @@ -175,7 +176,7 @@ export class CoreH5PPlayer { const records = await this.h5pCore.h5pFramework.getAllContentData(siteIdentifier); await Promise.all(records.map(async (record) => { - await CoreUtils.ignoreErrors(this.h5pCore.h5pFS.deleteContentIndex(record.foldername, siteIdentifier)); + await CorePromiseUtils.ignoreErrors(this.h5pCore.h5pFS.deleteContentIndex(record.foldername, siteIdentifier)); })); } @@ -191,7 +192,7 @@ export class CoreH5PPlayer { const data = await this.h5pCore.h5pFramework.getContentDataByUrl(fileUrl, siteId); - await CoreUtils.allPromises([ + await CorePromiseUtils.allPromises([ this.h5pCore.h5pFramework.deleteContentData(data.id, siteId), this.h5pCore.h5pFS.deleteContentFolder(data.foldername, siteId), @@ -298,7 +299,7 @@ export class CoreH5PPlayer { params.state = otherOptions.state; } - const customCssUrl = await CoreUtils.ignoreErrors(CoreH5P.getCustomCssSrc(siteId)); + const customCssUrl = await CorePromiseUtils.ignoreErrors(CoreH5P.getCustomCssSrc(siteId)); if (customCssUrl) { params.customCssUrl = customCssUrl; } diff --git a/src/core/features/h5p/classes/storage.ts b/src/core/features/h5p/classes/storage.ts index 4ddf8a546b4..968324d8f4c 100644 --- a/src/core/features/h5p/classes/storage.ts +++ b/src/core/features/h5p/classes/storage.ts @@ -14,7 +14,7 @@ import { CoreFile, CoreFileProvider } from '@services/file'; import { CoreSites } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CorePath } from '@singletons/path'; import { CoreH5PCore, CoreH5PLibraryBasicData } from './core'; import { CoreH5PFramework } from './framework'; @@ -57,7 +57,7 @@ export class CoreH5PStorage { const libraryData: CoreH5PLibraryBeingSaved = librariesJsonData[libString]; // Find local library with same major + minor. - const existingLibrary = await CoreUtils.ignoreErrors(this.h5pFramework.getLibraryByData(libraryData)); + const existingLibrary = await CorePromiseUtils.ignoreErrors(this.h5pFramework.getLibraryByData(libraryData)); if (existingLibrary) { // Library already installed. @@ -100,7 +100,7 @@ export class CoreH5PStorage { await this.h5pCore.h5pFS.deleteCachedAssets(removedEntries, siteId); } - await CoreUtils.allPromises(promises); + await CorePromiseUtils.allPromises(promises); } })); diff --git a/src/core/features/h5p/components/h5p-player/h5p-player.ts b/src/core/features/h5p/components/h5p-player/h5p-player.ts index 49227a1f9c9..41cc6ac3bdb 100644 --- a/src/core/features/h5p/components/h5p-player/h5p-player.ts +++ b/src/core/features/h5p/components/h5p-player/h5p-player.ts @@ -34,7 +34,7 @@ import { BehaviorSubject } from 'rxjs'; @Component({ selector: 'core-h5p-player', templateUrl: 'core-h5p-player.html', - styleUrls: ['h5p-player.scss'], + styleUrl: 'h5p-player.scss', }) export class CoreH5PPlayerComponent implements OnInit, OnChanges, OnDestroy { diff --git a/src/core/features/h5p/services/h5p.ts b/src/core/features/h5p/services/h5p.ts index 5681020ad63..d3283b85322 100644 --- a/src/core/features/h5p/services/h5p.ts +++ b/src/core/features/h5p/services/h5p.ts @@ -31,8 +31,8 @@ import { CoreError } from '@classes/errors/error'; import { CorePath } from '@singletons/path'; import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site'; import { CoreFilepool } from '@services/filepool'; -import { DownloadStatus } from '@/core/constants'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreCacheUpdateFrequency, DownloadStatus } from '@/core/constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Service to provide H5P functionalities. @@ -103,7 +103,7 @@ export class CoreH5PProvider { const state = await CoreFilepool.getFileStateByUrl(site.getId(), customCssUrl); if (state === DownloadStatus.DOWNLOADABLE_NOT_DOWNLOADED) { // File not downloaded, URL has changed or first time. Delete previously downloaded file. - await CoreUtils.ignoreErrors( + await CorePromiseUtils.ignoreErrors( CoreFilepool.removeFilesByComponent(site.getId(), CoreH5PProvider.CUSTOM_CSS_COMPONENT, 1), ); } @@ -145,7 +145,7 @@ export class CoreH5PProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getTrustedH5PFileCacheKey(url), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, }; if (ignoreCache) { diff --git a/src/core/features/h5p/services/handlers/pluginfile.ts b/src/core/features/h5p/services/handlers/pluginfile.ts index 3c3f6a51378..7e04da07c50 100644 --- a/src/core/features/h5p/services/handlers/pluginfile.ts +++ b/src/core/features/h5p/services/handlers/pluginfile.ts @@ -20,7 +20,7 @@ import { CorePluginFileDownloadableResult, CorePluginFileHandler } from '@servic import { CoreSites } from '@services/sites'; import { CoreMimetypeUtils } from '@services/utils/mimetype'; import { CoreUrl } from '@singletons/url'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreWSError } from '@classes/errors/wserror'; import { CoreWSFile } from '@services/ws'; import { CoreH5P } from '../h5p'; import { Translate, makeSingleton } from '@singletons'; @@ -103,7 +103,7 @@ export class CoreH5PPluginFileHandlerService implements CorePluginFileHandler { return trustedFile.filesize || 0; } catch (error) { - if (CoreUtils.isWebServiceError(error)) { + if (CoreWSError.isWebServiceError(error)) { // WS returned an error, it means it cannot be downloaded. return 0; } diff --git a/src/core/features/login/components/exceeded-attempts/exceeded-attempts.ts b/src/core/features/login/components/exceeded-attempts/exceeded-attempts.ts index 57b4b8a3035..98bf6b9009c 100644 --- a/src/core/features/login/components/exceeded-attempts/exceeded-attempts.ts +++ b/src/core/features/login/components/exceeded-attempts/exceeded-attempts.ts @@ -19,7 +19,7 @@ import { CoreUserSupport } from '@features/user/services/support'; @Component({ selector: 'core-login-exceeded-attempts', templateUrl: 'exceeded-attempts.html', - styleUrls: ['./exceeded-attempts.scss'], + styleUrl: './exceeded-attempts.scss', }) export class CoreLoginExceededAttemptsComponent implements OnInit { diff --git a/src/core/features/login/components/login-methods/login-methods.ts b/src/core/features/login/components/login-methods/login-methods.ts index f4c24f2551a..9b8f3b1a52d 100644 --- a/src/core/features/login/components/login-methods/login-methods.ts +++ b/src/core/features/login/components/login-methods/login-methods.ts @@ -24,7 +24,7 @@ import { CoreDomUtils } from '@services/utils/dom'; @Component({ selector: 'core-login-methods', templateUrl: 'login-methods.html', - styleUrls: ['../../login.scss'], + styleUrl: '../../login.scss', }) export class CoreLoginMethodsComponent implements OnInit { diff --git a/src/core/features/login/components/site-onboarding/site-onboarding.ts b/src/core/features/login/components/site-onboarding/site-onboarding.ts index da8e6b39ec8..3bf0f10c3af 100644 --- a/src/core/features/login/components/site-onboarding/site-onboarding.ts +++ b/src/core/features/login/components/site-onboarding/site-onboarding.ts @@ -15,7 +15,7 @@ import { Component } from '@angular/core'; import { CoreConfig } from '@services/config'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreOpener } from '@singletons/opener'; import { GET_STARTED_URL, ONBOARDING_DONE } from '@features/login/constants'; import { ModalController } from '@singletons'; import { CoreSharedModule } from '@/core/shared.module'; @@ -84,7 +84,7 @@ export class CoreLoginSiteOnboardingComponent { this.saveOnboardingDone(); - CoreUtils.openInBrowser(GET_STARTED_URL, { showBrowserWarning: false }); + CoreOpener.openInBrowser(GET_STARTED_URL, { showBrowserWarning: false }); ModalController.dismiss(); } diff --git a/src/core/features/login/guards/has-sites.ts b/src/core/features/login/guards/has-sites.ts index af2b7f9d86d..64075d985a7 100644 --- a/src/core/features/login/guards/has-sites.ts +++ b/src/core/features/login/guards/has-sites.ts @@ -14,7 +14,7 @@ import { CanActivateFn } from '@angular/router'; import { CoreSites } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { Router } from '@singletons'; import { CoreLoginHelper } from '../services/login-helper'; @@ -24,7 +24,7 @@ import { CoreLoginHelper } from '../services/login-helper'; * @returns True if user has sites, redirect route otherwise. */ export const hasSitesGuard: CanActivateFn = async () => { - const sites = await CoreUtils.ignoreErrors(CoreSites.getSites(), []); + const sites = await CorePromiseUtils.ignoreErrors(CoreSites.getSites(), []); if (sites.length > 0) { return true; diff --git a/src/core/features/login/login-credentials-lazy.module.ts b/src/core/features/login/login-credentials-lazy.module.ts index 498025e90fd..0827e8b9863 100644 --- a/src/core/features/login/login-credentials-lazy.module.ts +++ b/src/core/features/login/login-credentials-lazy.module.ts @@ -36,4 +36,4 @@ const routes: Routes = [ CoreLoginCredentialsPage, ], }) -export class CoreLoginCredentialsLazyModule {} +export default class CoreLoginCredentialsLazyModule {} diff --git a/src/core/features/login/login-lazy.module.ts b/src/core/features/login/login-lazy.module.ts index 13e05d136b5..ff6f82bcfe1 100644 --- a/src/core/features/login/login-lazy.module.ts +++ b/src/core/features/login/login-lazy.module.ts @@ -78,4 +78,4 @@ const routes: Routes = [ CoreLoginEmailSignupPage, ], }) -export class CoreLoginLazyModule {} +export default class CoreLoginLazyModule {} diff --git a/src/core/features/login/login-reconnect-lazy.module.ts b/src/core/features/login/login-reconnect-lazy.module.ts index 156ebd21066..22084638fb7 100644 --- a/src/core/features/login/login-reconnect-lazy.module.ts +++ b/src/core/features/login/login-reconnect-lazy.module.ts @@ -36,4 +36,4 @@ const routes: Routes = [ CoreLoginReconnectPage, ], }) -export class CoreLoginReconnectLazyModule {} +export default class CoreLoginReconnectLazyModule {} diff --git a/src/core/features/login/login.module.ts b/src/core/features/login/login.module.ts index 7d15b21759e..b0d3d8fbd5b 100644 --- a/src/core/features/login/login.module.ts +++ b/src/core/features/login/login.module.ts @@ -38,7 +38,7 @@ export async function getLoginServices(): Promise[]> { const appRoutes: Routes = [ { path: 'login', - loadChildren: () => import('./login-lazy.module').then(m => m.CoreLoginLazyModule), + loadChildren: () => import('./login-lazy.module'), canActivate: [redirectGuard], }, ]; diff --git a/src/core/features/login/pages/change-password/change-password.ts b/src/core/features/login/pages/change-password/change-password.ts index f2c063d8243..97e759f1ebb 100644 --- a/src/core/features/login/pages/change-password/change-password.ts +++ b/src/core/features/login/pages/change-password/change-password.ts @@ -19,8 +19,8 @@ import { CoreLoginHelper } from '@features/login/services/login-helper'; import { Translate } from '@singletons'; import { CoreNavigator } from '@services/navigator'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; -import { CoreUtils } from '@services/utils/utils'; import { CoreUserSupport } from '@features/user/services/support'; +import { CoreOpener } from '@singletons/opener'; /** * Page that shows instructions to change the password. @@ -94,7 +94,7 @@ export class CoreLoginChangePasswordPage implements OnDestroy { this.urlLoadedObserver = CoreEvents.on(CoreEvents.IAB_LOAD_STOP, (event) => { if (event.url.match(/\/login\/change_password\.php.*return=1/)) { // Password has changed, close the IAB now. - CoreUtils.closeInAppBrowser(); + CoreOpener.closeInAppBrowser(); this.login(); return; @@ -105,7 +105,7 @@ export class CoreLoginChangePasswordPage implements OnDestroy { } // Use a script to check if the user changed the password, in some platforms we cannot tell using the URL. - CoreUtils.getInAppBrowserInstance()?.executeScript({ + CoreOpener.getInAppBrowserInstance()?.executeScript({ code: ` if ( document.querySelector('input[type="password"]') === null && @@ -119,7 +119,7 @@ export class CoreLoginChangePasswordPage implements OnDestroy { this.messageObserver = CoreEvents.on(CoreEvents.IAB_MESSAGE, (data) => { if (data.passwordChanged) { - CoreUtils.closeInAppBrowser(); + CoreOpener.closeInAppBrowser(); this.login(); } }); diff --git a/src/core/features/login/pages/credentials/credentials.ts b/src/core/features/login/pages/credentials/credentials.ts index cd2f1606d07..b8638cab50a 100644 --- a/src/core/features/login/pages/credentials/credentials.ts +++ b/src/core/features/login/pages/credentials/credentials.ts @@ -17,7 +17,7 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { Subscription } from 'rxjs'; import { debounceTime } from 'rxjs/operators'; -import { CoreApp } from '@services/app'; +import { CoreSSO } from '@singletons/sso'; import { CoreNetwork } from '@services/network'; import { CoreSiteCheckResponse, CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; @@ -49,7 +49,7 @@ import { CoreLoadings } from '@services/loadings'; @Component({ selector: 'page-core-login-credentials', templateUrl: 'credentials.html', - styleUrls: ['../../login.scss'], + styleUrl: '../../login.scss', }) export class CoreLoginCredentialsPage implements OnInit, OnDestroy { @@ -251,7 +251,7 @@ export class CoreLoginCredentialsPage implements OnInit, OnDestroy { e?.stopPropagation(); // Check that there's no SSO authentication ongoing and the view hasn't changed. - if (CoreApp.isSSOAuthenticationOngoing() || this.viewLeft || !this.siteCheck) { + if (CoreSSO.isSSOAuthenticationOngoing() || this.viewLeft || !this.siteCheck) { return; } diff --git a/src/core/features/login/pages/email-signup/email-signup.ts b/src/core/features/login/pages/email-signup/email-signup.ts index 35d1b1433ca..2604de18cb5 100644 --- a/src/core/features/login/pages/email-signup/email-signup.ts +++ b/src/core/features/login/pages/email-signup/email-signup.ts @@ -17,7 +17,7 @@ import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms' import { CoreDomUtils } from '@services/utils/dom'; import { CoreText } from '@singletons/text'; -import { CoreCountry, CoreUtils } from '@services/utils/utils'; +import { CoreCountries, CoreCountry } from '@singletons/countries'; import { CoreWS, CoreWSExternalWarning } from '@services/ws'; import { Translate } from '@singletons'; import { CoreSitePublicConfigResponse, CoreUnauthenticatedSite } from '@classes/sites/unauthenticated-site'; @@ -38,6 +38,8 @@ import { EMAIL_SIGNUP_FEATURE_NAME } from '@features/login/constants'; import { CoreInputErrorsMessages } from '@components/input-errors/input-errors'; import { CoreViewer } from '@features/viewer/services/viewer'; import { CoreLoadings } from '@services/loadings'; +import { CorePromiseUtils } from '@singletons/promise-utils'; +import { CoreOpener } from '@singletons/opener'; /** * Page to signup using email. @@ -45,7 +47,7 @@ import { CoreLoadings } from '@services/loadings'; @Component({ selector: 'page-core-login-email-signup', templateUrl: 'email-signup.html', - styleUrls: ['../../login.scss'], + styleUrl: '../../login.scss', }) export class CoreLoginEmailSignupPage implements OnInit { @@ -189,7 +191,7 @@ export class CoreLoginEmailSignupPage implements OnInit { // Check content verification. if (this.ageDigitalConsentVerification === undefined) { - const result = await CoreUtils.ignoreErrors( + const result = await CorePromiseUtils.ignoreErrors( CoreWS.callAjax( 'core_auth_is_age_digital_consent_verification_enabled', {}, @@ -243,7 +245,7 @@ export class CoreLoginEmailSignupPage implements OnInit { } this.namefieldsErrors = namefieldsErrors; - this.countries = await CoreUtils.getCountryListSorted(); + this.countries = await CoreCountries.getCountryListSorted(); } /** @@ -394,7 +396,7 @@ export class CoreLoginEmailSignupPage implements OnInit { * Show contact information on site (we have to display again the age verification form). */ showContactOnSite(): void { - CoreUtils.openInBrowser( + CoreOpener.openInBrowser( CorePath.concatenatePaths(this.site.getURL(), '/login/verify_age_location.php'), { showBrowserWarning: false }, ); diff --git a/src/core/features/login/pages/reconnect/reconnect.ts b/src/core/features/login/pages/reconnect/reconnect.ts index 972e9cd59f8..684d36a1e7a 100644 --- a/src/core/features/login/pages/reconnect/reconnect.ts +++ b/src/core/features/login/pages/reconnect/reconnect.ts @@ -18,7 +18,7 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { CoreNetwork } from '@services/network'; import { CoreSiteBasicInfo, CoreSites, CoreSitesReadingStrategy } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreLoginHelper } from '@features/login/services/login-helper'; import { CoreSite } from '@classes/sites/site'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; @@ -41,7 +41,7 @@ import { CoreLoadings } from '@services/loadings'; @Component({ selector: 'page-core-login-reconnect', templateUrl: 'reconnect.html', - styleUrls: ['../../login.scss'], + styleUrl: '../../login.scss', }) export class CoreLoginReconnectPage implements OnInit, OnDestroy { @@ -180,7 +180,7 @@ export class CoreLoginReconnectPage implements OnInit, OnDestroy { * Get some data (like identity providers) from the site config. */ protected async checkSiteConfig(): Promise { - this.siteConfig = await CoreUtils.ignoreErrors(this.site.getPublicConfig({ + this.siteConfig = await CorePromiseUtils.ignoreErrors(this.site.getPublicConfig({ readingStrategy: CoreSitesReadingStrategy.PREFER_NETWORK, })); diff --git a/src/core/features/login/pages/site/site.ts b/src/core/features/login/pages/site/site.ts index 586c8761d64..02d9cb4b56a 100644 --- a/src/core/features/login/pages/site/site.ts +++ b/src/core/features/login/pages/site/site.ts @@ -18,7 +18,7 @@ import { FormBuilder, FormGroup, ValidatorFn, AbstractControl, ValidationErrors import { CoreNetwork } from '@services/network'; import { CoreConfig } from '@services/config'; import { CoreSites, CoreSiteCheckResponse, CoreLoginSiteInfo, CoreSitesDemoSiteData } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreLoginHelper, @@ -49,6 +49,8 @@ import { CoreKeyboard } from '@singletons/keyboard'; import { CoreModals } from '@services/modals'; import { CoreQRScan } from '@services/qrscan'; import { CoreLoadings } from '@services/loadings'; +import { CorePromiseUtils } from '@singletons/promise-utils'; +import { CoreCountries } from '@singletons/countries'; /** * Site (url) chooser when adding a new site. @@ -172,12 +174,15 @@ export class CoreLoginSitePage implements OnInit { * @returns Referrer URL, undefined if no URL to use. */ protected async consumeInstallReferrerUrl(): Promise { - const url = await CoreUtils.ignoreErrors(CoreUtils.timeoutPromise(CoreReferrer.consumeInstallReferrerUrl(), 1000)); + const url = await CorePromiseUtils.ignoreErrors( + CorePromiseUtils.timeoutPromise(CoreReferrer.consumeInstallReferrerUrl(), 1000), + ); + if (!url) { return; } - const hasSites = (await CoreUtils.ignoreErrors(CoreSites.getSites(), [])).length > 0; + const hasSites = (await CorePromiseUtils.ignoreErrors(CoreSites.getSites(), [])).length > 0; if (hasSites) { // There are sites stored already, don't use the referrer URL since it's an update or a backup was restored. return; @@ -219,7 +224,7 @@ export class CoreLoginSitePage implements OnInit { site.title = name && alias ? name + ' (' + alias + ')' : name + alias; const country = this.siteFinderSettings.displaycountry && site.countrycode ? - CoreUtils.getCountryName(site.countrycode) : ''; + CoreCountries.getCountryName(site.countrycode) : ''; const city = this.siteFinderSettings.displaycity && site.city ? site.city : ''; diff --git a/src/core/features/login/services/handlers/cron.ts b/src/core/features/login/services/handlers/cron.ts index 3b6c53838be..f488363f26f 100644 --- a/src/core/features/login/services/handlers/cron.ts +++ b/src/core/features/login/services/handlers/cron.ts @@ -15,7 +15,7 @@ import { Injectable } from '@angular/core'; import { CoreCronHandler } from '@services/cron'; import { CoreSites, CoreSitesReadingStrategy } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { makeSingleton } from '@singletons'; /** @@ -39,11 +39,11 @@ export class CoreLoginCronHandlerService implements CoreCronHandler { // Do not check twice in the same 10 minutes. const site = await CoreSites.getSite(siteId); - const config = await CoreUtils.ignoreErrors(site.getPublicConfig({ + const config = await CorePromiseUtils.ignoreErrors(site.getPublicConfig({ readingStrategy: CoreSitesReadingStrategy.ONLY_NETWORK, })); - CoreUtils.ignoreErrors(CoreSites.checkApplication(config)); + CorePromiseUtils.ignoreErrors(CoreSites.checkApplication(config)); } /** diff --git a/src/core/features/login/services/login-helper.ts b/src/core/features/login/services/login-helper.ts index fcb3abbd232..5b148bdd2f9 100644 --- a/src/core/features/login/services/login-helper.ts +++ b/src/core/features/login/services/login-helper.ts @@ -23,7 +23,7 @@ import { CoreSites, CoreLoginSiteInfo, CoreSiteBasicInfo } from '@services/sites import { CoreWS, CoreWSExternalWarning } from '@services/ws'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreText } from '@singletons/text'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreObject } from '@singletons/object'; import { CoreConstants } from '@/core/constants'; import { CoreSite } from '@classes/sites/site'; import { CoreError } from '@classes/errors/error'; @@ -61,6 +61,9 @@ import { CoreSiteError, CoreSiteErrorDebug } from '@classes/errors/siteerror'; import { CoreQRScan } from '@services/qrscan'; import { CoreLoadings } from '@services/loadings'; import { CoreErrorHelper } from '@services/error-helper'; +import { CoreSSO } from '@singletons/sso'; +import { CorePromiseUtils } from '@singletons/promise-utils'; +import { CoreOpener } from '@singletons/opener'; /** * Helper provider that provides some common features regarding authentication. @@ -131,7 +134,7 @@ export class CoreLoginHelperProvider { const currentSite = CoreSites.getCurrentSite(); if ( - !CoreApp.isSSOAuthenticationOngoing() && + !CoreSSO.isSSOAuthenticationOngoing() && currentSite?.isLoggedOut() && CoreNavigator.isCurrent('/login/reconnect') ) { @@ -171,7 +174,7 @@ export class CoreLoginHelperProvider { async forgottenPasswordClicked(siteUrl: string, username: string, siteConfig?: CoreSitePublicConfigResponse): Promise { if (siteConfig && siteConfig.forgottenpasswordurl) { // URL set, open it. - CoreUtils.openInApp(siteConfig.forgottenpasswordurl); + CoreOpener.openInApp(siteConfig.forgottenpasswordurl); return; } @@ -304,18 +307,6 @@ export class CoreLoginHelperProvider { return CorePolicy.getSitePoliciesURL(siteId); } - /** - * Get fixed site or sites. - * - * @returns Fixed site or list of fixed sites. - * @deprecated since 4.2. Use CoreConstants.CONFIG.sites or getAvailableSites() instead. - */ - getFixedSites(): string | CoreLoginSiteInfo[] { - const notStagingSites = CoreConstants.CONFIG.sites.filter(site => !site.staging); - - return notStagingSites.length === 1 ? notStagingSites[0].url : notStagingSites; - } - /** * Get Available sites (includes staging sites if are enabled). It doesn't include demo mode site. * @@ -384,7 +375,7 @@ export class CoreLoginHelperProvider { * @returns Valid identity providers. */ async getValidIdentityProvidersForSite(site: CoreUnauthenticatedSite): Promise { - const siteConfig = await CoreUtils.ignoreErrors(site.getPublicConfig()); + const siteConfig = await CorePromiseUtils.ignoreErrors(site.getPublicConfig()); if (!siteConfig) { return []; } @@ -480,18 +471,6 @@ export class CoreLoginHelperProvider { return CoreSites.newSite(siteUrl, token, privateToken, true, oauthId); } - /** - * Check if the app is configured to use several fixed URLs. - * - * @returns Whether there are several fixed URLs. - * @deprecated since 4.2. Use CoreConstants.CONFIG.sites.length > 1 instead. - */ - async hasSeveralFixedSites(): Promise { - const sites = await this.getAvailableSites(); - - return sites.length > 1; - } - /** * Given a site public config, check if email signup is disabled. * @@ -521,16 +500,6 @@ export class CoreLoginHelperProvider { return !!disabledFeatures.match(regEx); } - /** - * Check if the app is configured to use a fixed URL (only 1). - * - * @returns Whether there is 1 fixed URL. - * @deprecated since 4.2. Use isSingleFixedSite instead. - */ - isFixedUrlSet(): boolean { - return CoreConstants.CONFIG.sites.filter(site => !site.staging).length === 1; - } - /** * Check if the app is configured to use a fixed URL (only 1). * @@ -659,7 +628,7 @@ export class CoreLoginHelperProvider { }); // Always open it in browser because the user might have the session stored in there. - CoreUtils.openInBrowser(loginUrl, { showBrowserWarning: false }); + CoreOpener.openInBrowser(loginUrl, { showBrowserWarning: false }); CoreApp.closeApp(); return true; @@ -696,12 +665,12 @@ export class CoreLoginHelperProvider { this.logger.debug('openBrowserForSSOLogin loginUrl:', loginUrl); if (this.isSSOEmbeddedBrowser(typeOfLogin)) { - CoreUtils.openInApp(loginUrl, { + CoreOpener.openInApp(loginUrl, { clearsessioncache: 'yes', // Clear the session cache to allow for multiple logins. closebuttoncaption: Translate.instant('core.login.cancel'), }); } else { - CoreUtils.openInBrowser(loginUrl, { showBrowserWarning: false }); + CoreOpener.openInBrowser(loginUrl, { showBrowserWarning: false }); CoreApp.closeApp(); } } catch (error) { @@ -723,7 +692,7 @@ export class CoreLoginHelperProvider { await alert.onDidDismiss(); - CoreUtils.openInApp(siteUrl + '/login/change_password.php'); + CoreOpener.openInApp(siteUrl + '/login/change_password.php'); } /** @@ -732,7 +701,7 @@ export class CoreLoginHelperProvider { * @param siteUrl URL of the site. */ openForgottenPassword(siteUrl: string): void { - CoreUtils.openInApp(siteUrl + '/login/forgot_password.php'); + CoreOpener.openInApp(siteUrl + '/login/forgot_password.php'); } /** @@ -827,20 +796,20 @@ export class CoreLoginHelperProvider { launchUrl = launchUrl || siteUrl + '/admin/tool/mobile/launch.php'; const passport = Math.random() * 1000; - let loginUrl = launchUrl + '?service=' + service; - loginUrl += '&passport=' + passport; - loginUrl += '&urlscheme=' + CoreConstants.CONFIG.customurlscheme; + const additionalParams = Object.assign(urlParams || {}, { + service, + passport, + urlscheme: CoreConstants.CONFIG.customurlscheme, + }); - if (urlParams) { - loginUrl = CoreUrl.addParamsToUrl(loginUrl, urlParams); - } + const loginUrl = CoreUrl.addParamsToUrl(launchUrl, additionalParams); - // Store the siteurl and passport in CoreConfigProvider for persistence. - // We are "configuring" the app to wait for an SSO. CoreConfigProvider shouldn't be used as a temporary storage. + // Store the siteurl and passport in CoreConfig for persistence. + // We are "configuring" the app to wait for an SSO. CoreConfig shouldn't be used as a temporary storage. await CoreConfig.set(CoreConstants.LOGIN_LAUNCH_DATA, JSON.stringify( { - siteUrl: siteUrl, - passport: passport, + siteUrl, + passport, ...redirectData, ssoUrlParams: urlParams || {}, })); @@ -908,7 +877,7 @@ export class CoreLoginHelperProvider { return; } - await CoreUtils.ignoreErrors(CoreNavigator.navigate('/login/reconnect', { + await CorePromiseUtils.ignoreErrors(CoreNavigator.navigate('/login/reconnect', { params: { siteId, ...redirectData, @@ -1008,7 +977,7 @@ export class CoreLoginHelperProvider { async openInBrowserFallback(siteUrl: string, debug?: CoreSiteErrorDebug): Promise { CoreEvents.trigger(APP_UNSUPPORTED_CHURN, { siteUrl, debug }); - await CoreUtils.openInBrowser(siteUrl, { showBrowserWarning: false }); + await CoreOpener.openInBrowser(siteUrl, { showBrowserWarning: false }); } /** @@ -1364,7 +1333,7 @@ export class CoreLoginHelperProvider { * @returns Promise resolved with account list. */ async getAccountsList(): Promise { - const sites = await CoreUtils.ignoreErrors(CoreSites.getSortedSites(), [] as CoreSiteBasicInfo[]); + const sites = await CorePromiseUtils.ignoreErrors(CoreSites.getSortedSites(), [] as CoreSiteBasicInfo[]); const accountsList: CoreAccountsList = { sameSite: [], @@ -1382,7 +1351,7 @@ export class CoreLoginHelperProvider { // Add site counter and classify sites. await Promise.all(sites.map(async (site) => { - site.badge = await CoreUtils.ignoreErrors(CorePushNotifications.getSiteCounter(site.id)) || 0; + site.badge = await CorePromiseUtils.ignoreErrors(CorePushNotifications.getSiteCounter(site.id)) || 0; if (site.id === currentSiteId) { accountsList.currentSite = site; @@ -1399,7 +1368,7 @@ export class CoreLoginHelperProvider { return; })); - accountsList.otherSites = CoreUtils.objectToArray(otherSites); + accountsList.otherSites = CoreObject.toArray(otherSites); return accountsList; } @@ -1451,8 +1420,8 @@ export class CoreLoginHelperProvider { * * @returns Reconnect page route module. */ - async getReconnectRouteModule(): Promise { - return import('@features/login/login-reconnect-lazy.module').then(m => m.CoreLoginReconnectLazyModule); + getReconnectRouteModule(): LazyRoutesModule { + return import('@features/login/login-reconnect-lazy.module'); } /** @@ -1460,8 +1429,8 @@ export class CoreLoginHelperProvider { * * @returns Credentials page route module. */ - async getCredentialsRouteModule(): Promise { - return import('@features/login/login-credentials-lazy.module').then(m => m.CoreLoginCredentialsLazyModule); + getCredentialsRouteModule(): LazyRoutesModule { + return import('@features/login/login-credentials-lazy.module'); } /** diff --git a/src/core/features/mainmenu/components/user-menu-button/user-menu-button.ts b/src/core/features/mainmenu/components/user-menu-button/user-menu-button.ts index a15b2a8e708..eaa5afad658 100644 --- a/src/core/features/mainmenu/components/user-menu-button/user-menu-button.ts +++ b/src/core/features/mainmenu/components/user-menu-button/user-menu-button.ts @@ -32,7 +32,7 @@ import { toBoolean } from '@/core/transforms/boolean'; @Component({ selector: 'core-user-menu-button', templateUrl: 'user-menu-button.html', - styleUrls: ['user-menu-button.scss'], + styleUrl: 'user-menu-button.scss', }) export class CoreMainMenuUserButtonComponent implements OnInit { diff --git a/src/core/features/mainmenu/components/user-menu-tour/user-menu-tour.ts b/src/core/features/mainmenu/components/user-menu-tour/user-menu-tour.ts index 4d7c1a3d7c2..81666484217 100644 --- a/src/core/features/mainmenu/components/user-menu-tour/user-menu-tour.ts +++ b/src/core/features/mainmenu/components/user-menu-tour/user-menu-tour.ts @@ -21,7 +21,7 @@ import { CoreUserTours } from '@features/usertours/services/user-tours'; @Component({ selector: 'core-mainmenu-user-menu-tour', templateUrl: 'user-menu-tour.html', - styleUrls: ['user-menu-tour.scss'], + styleUrl: 'user-menu-tour.scss', }) export class CoreMainMenuUserMenuTourComponent { 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 953565fbe83..e52071150bd 100644 --- a/src/core/features/mainmenu/components/user-menu/user-menu.ts +++ b/src/core/features/mainmenu/components/user-menu/user-menu.ts @@ -32,7 +32,7 @@ import { CoreModals } from '@services/modals'; import { CoreNavigator } from '@services/navigator'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { ModalController, Translate } from '@singletons'; import { Subscription } from 'rxjs'; @@ -141,7 +141,7 @@ export class CoreMainMenuUserMenuComponent implements OnInit, OnDestroy { return; } - const siteConfig = await CoreUtils.ignoreErrors(currentSite.getPublicConfig()); + const siteConfig = await CorePromiseUtils.ignoreErrors(currentSite.getPublicConfig()); this.siteLogo = currentSite.getLogoUrl(siteConfig); this.siteLogoLoaded = true; } diff --git a/src/core/features/mainmenu/constants.ts b/src/core/features/mainmenu/constants.ts new file mode 100644 index 00000000000..16d6ea7a7be --- /dev/null +++ b/src/core/features/mainmenu/constants.ts @@ -0,0 +1,20 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +export const MAIN_MENU_NUM_MAIN_HANDLERS = 4; +export const MAIN_MENU_ITEM_MIN_WIDTH = 72; // Min with of every item, based on 5 items on a 360 pixel wide screen. +export const MAIN_MENU_MORE_PAGE_NAME = 'more'; + +export const MAIN_MENU_HANDLER_BADGE_UPDATED_EVENT = 'main_menu_handler_badge_updated'; +export const MAIN_MENU_VISIBILITY_UPDATED_EVENT = 'main_menu_visbility_updated'; diff --git a/src/core/features/mainmenu/guards/auth.ts b/src/core/features/mainmenu/guards/auth.ts index 68923cb9b09..b63c4e0f4b9 100644 --- a/src/core/features/mainmenu/guards/auth.ts +++ b/src/core/features/mainmenu/guards/auth.ts @@ -14,7 +14,7 @@ import { CanActivateFn } from '@angular/router'; import { CoreLoginHelper } from '@features/login/services/login-helper'; -import { CoreApp } from '@services/app'; +import { CoreRedirects } from '@singletons/redirects'; import { CoreSites } from '@services/sites'; import { Router } from '@singletons'; @@ -35,7 +35,7 @@ export const authGuard: CanActivateFn = async () => { const siteId = CoreSites.getCurrentSiteId(); // Pass redirect data (if any and belongs to same site). - let redirect = CoreApp.consumeMemoryRedirect(); + let redirect = CoreRedirects.consumeMemoryRedirect(); if (redirect?.siteId !== siteId) { redirect = null; } diff --git a/src/core/features/mainmenu/mainmenu-home-lazy.module.ts b/src/core/features/mainmenu/mainmenu-home-lazy.module.ts index 99d14e33deb..a0541483197 100644 --- a/src/core/features/mainmenu/mainmenu-home-lazy.module.ts +++ b/src/core/features/mainmenu/mainmenu-home-lazy.module.ts @@ -57,4 +57,4 @@ function buildRoutes(injector: Injector): Routes { CoreMainMenuHomePage, ], }) -export class CoreMainMenuHomeLazyModule {} +export default class CoreMainMenuHomeLazyModule {} diff --git a/src/core/features/mainmenu/mainmenu-lazy.module.ts b/src/core/features/mainmenu/mainmenu-lazy.module.ts index 0d71ebe0a84..c4f027e9633 100644 --- a/src/core/features/mainmenu/mainmenu-lazy.module.ts +++ b/src/core/features/mainmenu/mainmenu-lazy.module.ts @@ -19,8 +19,8 @@ import { CoreSharedModule } from '@/core/shared.module'; import { resolveMainMenuRoutes } from './mainmenu-routing.module'; import { CoreMainMenuPage } from './pages/menu/menu'; import { CoreMainMenuHomeHandlerService } from './services/handlers/mainmenu'; -import { CoreMainMenuProvider } from './services/mainmenu'; import { CoreMainMenuComponentsModule } from './components/components.module'; +import { MAIN_MENU_MORE_PAGE_NAME } from './constants'; /** * Build module routes. @@ -38,11 +38,11 @@ function buildRoutes(injector: Injector): Routes { children: [ { path: CoreMainMenuHomeHandlerService.PAGE_NAME, - loadChildren: () => import('./mainmenu-home-lazy.module').then(m => m.CoreMainMenuHomeLazyModule), + loadChildren: () => import('./mainmenu-home-lazy.module'), }, { - path: CoreMainMenuProvider.MORE_PAGE_NAME, - loadChildren: () => import('./mainmenu-more-lazy.module').then(m => m.CoreMainMenuMoreLazyModule), + path: MAIN_MENU_MORE_PAGE_NAME, + loadChildren: () => import('./mainmenu-more-lazy.module'), }, ...mainMenuRoutes.children, ], @@ -63,4 +63,4 @@ function buildRoutes(injector: Injector): Routes { { provide: ROUTES, multi: true, useFactory: buildRoutes, deps: [Injector] }, ], }) -export class CoreMainMenuLazyModule {} +export default class CoreMainMenuLazyModule {} diff --git a/src/core/features/mainmenu/mainmenu-more-lazy.module.ts b/src/core/features/mainmenu/mainmenu-more-lazy.module.ts index 0c3f9f667e2..b78ed0db407 100644 --- a/src/core/features/mainmenu/mainmenu-more-lazy.module.ts +++ b/src/core/features/mainmenu/mainmenu-more-lazy.module.ts @@ -17,9 +17,9 @@ import { Routes, ROUTES } from '@angular/router'; import { CoreSharedModule } from '@/core/shared.module'; import { buildTabMainRoutes } from '@features/mainmenu/mainmenu-tab-routing.module'; -import { CoreMainMenuProvider } from '@features/mainmenu/services/mainmenu'; import { CoreMainMenuComponentsModule } from '@features/mainmenu/components/components.module'; import { CoreMainMenuMorePage } from '@features/mainmenu/pages/more/more'; +import { MAIN_MENU_MORE_PAGE_NAME } from './constants'; /** * Build module routes. @@ -31,7 +31,7 @@ function buildRoutes(injector: Injector): Routes { return buildTabMainRoutes(injector, { component: CoreMainMenuMorePage, data: { - mainMenuTabRoot: CoreMainMenuProvider.MORE_PAGE_NAME, + mainMenuTabRoot: MAIN_MENU_MORE_PAGE_NAME, }, }); } @@ -53,4 +53,4 @@ function buildRoutes(injector: Injector): Routes { CoreMainMenuMorePage, ], }) -export class CoreMainMenuMoreLazyModule {} +export default class CoreMainMenuMoreLazyModule {} diff --git a/src/core/features/mainmenu/mainmenu-reload-lazy.module.ts b/src/core/features/mainmenu/mainmenu-reload-lazy.module.ts index b1aaa8baaad..79a20778a91 100644 --- a/src/core/features/mainmenu/mainmenu-reload-lazy.module.ts +++ b/src/core/features/mainmenu/mainmenu-reload-lazy.module.ts @@ -33,4 +33,4 @@ const routes: Routes = [ CoreMainMenuReloadPage, ], }) -export class CoreMainMenuReloadLazyModule {} +export default class CoreMainMenuReloadLazyModule {} diff --git a/src/core/features/mainmenu/mainmenu.module.ts b/src/core/features/mainmenu/mainmenu.module.ts index 4f423f83355..fa4e0354bef 100644 --- a/src/core/features/mainmenu/mainmenu.module.ts +++ b/src/core/features/mainmenu/mainmenu.module.ts @@ -38,6 +38,32 @@ export async function getMainMenuServices(): Promise[]> { ]; } +/** + * Get main menu exported objects. + * + * @returns Main menu exported objects. + */ +export async function getMainMenuExportedObjects(): Promise> { + const { + MAIN_MENU_NUM_MAIN_HANDLERS, + MAIN_MENU_ITEM_MIN_WIDTH, + MAIN_MENU_MORE_PAGE_NAME, + MAIN_MENU_HANDLER_BADGE_UPDATED_EVENT, + MAIN_MENU_VISIBILITY_UPDATED_EVENT, + + } = await import('@features/mainmenu/constants'); + + /* eslint-disable @typescript-eslint/naming-convention */ + return { + MAIN_MENU_NUM_MAIN_HANDLERS, + MAIN_MENU_ITEM_MIN_WIDTH, + MAIN_MENU_MORE_PAGE_NAME, + MAIN_MENU_HANDLER_BADGE_UPDATED_EVENT, + MAIN_MENU_VISIBILITY_UPDATED_EVENT, + }; + /* eslint-enable @typescript-eslint/naming-convention */ +} + const appRoutes: Routes = [ { path: '', @@ -46,12 +72,12 @@ const appRoutes: Routes = [ }, { path: 'main', - loadChildren: () => import('./mainmenu-lazy.module').then(m => m.CoreMainMenuLazyModule), + loadChildren: () => import('./mainmenu-lazy.module'), canActivate: [authGuard], }, { path: 'reload', - loadChildren: () => import('./mainmenu-reload-lazy.module').then( m => m.CoreMainMenuReloadLazyModule), + loadChildren: () => import('./mainmenu-reload-lazy.module'), }, ]; diff --git a/src/core/features/mainmenu/pages/home/home.ts b/src/core/features/mainmenu/pages/home/home.ts index ab928b4c7b2..31dacfcedf5 100644 --- a/src/core/features/mainmenu/pages/home/home.ts +++ b/src/core/features/mainmenu/pages/home/home.ts @@ -19,7 +19,7 @@ import { CoreSites } from '@services/sites'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreTabsOutletComponent, CoreTabsOutletTab } from '@components/tabs-outlet/tabs-outlet'; import { CoreMainMenuHomeDelegate, CoreMainMenuHomeHandlerToDisplay } from '../../services/home-delegate'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreArray } from '@singletons/array'; import { CoreMainMenuHomeHandlerService } from '@features/mainmenu/services/handlers/mainmenu'; /** @@ -62,7 +62,7 @@ export class CoreMainMenuHomePage implements OnInit { initHandlers(handlers: CoreMainMenuHomeHandlerToDisplay[]): void { // Re-build the list of tabs. const loaded = CoreMainMenuHomeDelegate.areHandlersLoaded(); - const handlersMap = CoreUtils.arrayToObject(handlers, 'title'); + const handlersMap = CoreArray.toObject(handlers, 'title'); const newTabs = handlers.map((handler): CoreTabsOutletTab => { const tab = this.tabs.find(tab => tab.title == handler.title); diff --git a/src/core/features/mainmenu/pages/menu/menu.ts b/src/core/features/mainmenu/pages/menu/menu.ts index ab32457f4b8..8f59e6d8f4c 100644 --- a/src/core/features/mainmenu/pages/menu/menu.ts +++ b/src/core/features/mainmenu/pages/menu/menu.ts @@ -18,10 +18,10 @@ import { BackButtonEvent } from '@ionic/core'; import { Subscription } from 'rxjs'; import { CoreEvents, CoreEventObserver } from '@singletons/events'; -import { CoreMainMenu, CoreMainMenuProvider } from '../../services/mainmenu'; +import { CoreMainMenu } from '../../services/mainmenu'; import { CoreMainMenuDelegate, CoreMainMenuHandlerToDisplay } from '../../services/mainmenu-delegate'; import { Router } from '@singletons'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { CoreAriaRoleTab, CoreAriaRoleTabFindable } from '@classes/aria-role-tab'; import { CoreNavigator } from '@services/navigator'; import { filter } from 'rxjs/operators'; @@ -35,6 +35,11 @@ import { CoreWait } from '@singletons/wait'; import { CoreMainMenuDeepLinkManager } from '@features/mainmenu/classes/deep-link-manager'; import { CoreSiteInfoUserHomepage } from '@classes/sites/unauthenticated-site'; import { CoreContentLinksHelper } from '@features/contentlinks/services/contentlinks-helper'; +import { + MAIN_MENU_MORE_PAGE_NAME, + MAIN_MENU_HANDLER_BADGE_UPDATED_EVENT, + MAIN_MENU_VISIBILITY_UPDATED_EVENT, +} from '@features/mainmenu/constants'; const ANIMATION_DURATION = 500; @@ -63,7 +68,7 @@ const ANIMATION_DURATION = 500; animate(`${ANIMATION_DURATION}ms ease-in-out`, style({ transform: 'translateY(0)' })), ]), ])], - styleUrls: ['menu.scss'], + styleUrl: 'menu.scss', }) export class CoreMainMenuPage implements OnInit, OnDestroy { @@ -72,7 +77,7 @@ export class CoreMainMenuPage implements OnInit, OnDestroy { loaded = false; showTabs = false; tabsPlacement: 'bottom' | 'side' = 'bottom'; - morePageName = CoreMainMenuProvider.MORE_PAGE_NAME; + morePageName = MAIN_MENU_MORE_PAGE_NAME; selectedTab?: string; isMainScreen = false; moreBadge = false; @@ -124,7 +129,7 @@ export class CoreMainMenuPage implements OnInit, OnDestroy { this.updateHandlers(previousHandlers); }); - this.badgeUpdateObserver = CoreEvents.on(CoreMainMenuProvider.MAIN_MENU_HANDLER_BADGE_UPDATED, (data) => { + this.badgeUpdateObserver = CoreEvents.on(MAIN_MENU_HANDLER_BADGE_UPDATED_EVENT, (data) => { if (data.siteId == CoreSites.getCurrentSiteId()) { this.updateMoreBadge(); } @@ -361,7 +366,7 @@ export class CoreMainMenuPage implements OnInit, OnDestroy { await CoreWait.wait(ANIMATION_DURATION); await CoreWait.nextTick(); - CoreEvents.trigger(CoreMainMenuProvider.MAIN_MENU_VISIBILITY_UPDATED); + CoreEvents.trigger(MAIN_MENU_VISIBILITY_UPDATED_EVENT); } } diff --git a/src/core/features/mainmenu/pages/more/more.ts b/src/core/features/mainmenu/pages/more/more.ts index d20ae7e31da..5d6241e8f78 100644 --- a/src/core/features/mainmenu/pages/more/more.ts +++ b/src/core/features/mainmenu/pages/more/more.ts @@ -31,7 +31,7 @@ import { CoreViewer } from '@features/viewer/services/viewer'; @Component({ selector: 'page-core-mainmenu-more', templateUrl: 'more.html', - styleUrls: ['more.scss'], + styleUrl: 'more.scss', }) export class CoreMainMenuMorePage implements OnInit, OnDestroy { diff --git a/src/core/features/mainmenu/services/mainmenu.ts b/src/core/features/mainmenu/services/mainmenu.ts index 1ad919e162a..1b1d08baaa6 100644 --- a/src/core/features/mainmenu/services/mainmenu.ts +++ b/src/core/features/mainmenu/services/mainmenu.ts @@ -22,6 +22,13 @@ import { Device, makeSingleton } from '@singletons'; import { CoreText } from '@singletons/text'; import { CoreScreen } from '@services/screen'; import { CorePlatform } from '@services/platform'; +import { + MAIN_MENU_HANDLER_BADGE_UPDATED_EVENT, + MAIN_MENU_ITEM_MIN_WIDTH, + MAIN_MENU_MORE_PAGE_NAME, + MAIN_MENU_NUM_MAIN_HANDLERS, + MAIN_MENU_VISIBILITY_UPDATED_EVENT, +} from '../constants'; declare module '@singletons/events' { @@ -31,8 +38,8 @@ declare module '@singletons/events' { * @see https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation */ export interface CoreEventsData { - [CoreMainMenuProvider.MAIN_MENU_HANDLER_BADGE_UPDATED]: CoreMainMenuHandlerBadgeUpdatedEventData; - [CoreMainMenuProvider.MAIN_MENU_VISIBILITY_UPDATED]: void; + [MAIN_MENU_HANDLER_BADGE_UPDATED_EVENT]: CoreMainMenuHandlerBadgeUpdatedEventData; + [MAIN_MENU_VISIBILITY_UPDATED_EVENT]: void; } } @@ -43,11 +50,26 @@ declare module '@singletons/events' { @Injectable({ providedIn: 'root' }) export class CoreMainMenuProvider { - static readonly NUM_MAIN_HANDLERS = 4; - static readonly ITEM_MIN_WIDTH = 72; // Min with of every item, based on 5 items on a 360 pixel wide screen. - static readonly MORE_PAGE_NAME = 'more'; - static readonly MAIN_MENU_HANDLER_BADGE_UPDATED = 'main_menu_handler_badge_updated'; - static readonly MAIN_MENU_VISIBILITY_UPDATED = 'main_menu_visbility_updated'; + /** + * @deprecated since 5.0. Use MAIN_MENU_NUM_MAIN_HANDLERS instead. + */ + static readonly NUM_MAIN_HANDLERS = MAIN_MENU_NUM_MAIN_HANDLERS; + /** + * @deprecated since 5.0. Use MAIN_MENU_ITEM_MIN_WIDTH instead. + */ + static readonly ITEM_MIN_WIDTH = MAIN_MENU_ITEM_MIN_WIDTH; + /** + * @deprecated since 5.0. Use MAIN_MENU_MORE_PAGE_NAME instead. + */ + static readonly MORE_PAGE_NAME = MAIN_MENU_MORE_PAGE_NAME; + /** + * @deprecated since 5.0. Use MAIN_MENU_HANDLER_BADGE_UPDATED_EVENT instead. + */ + static readonly MAIN_MENU_HANDLER_BADGE_UPDATED = MAIN_MENU_HANDLER_BADGE_UPDATED_EVENT; + /** + * @deprecated since 5.0. Use MAIN_MENU_VISIBILITY_UPDATED_EVENT instead. + */ + static readonly MAIN_MENU_VISIBILITY_UPDATED = MAIN_MENU_VISIBILITY_UPDATED_EVENT; /** * Get the current main menu handlers. @@ -229,9 +251,9 @@ export class CoreMainMenuProvider { if (CoreScreen.isTablet) { // Tablet, menu will be displayed vertically. - numElements = Math.floor(window.innerHeight / CoreMainMenuProvider.ITEM_MIN_WIDTH); + numElements = Math.floor(window.innerHeight / MAIN_MENU_ITEM_MIN_WIDTH); } else { - numElements = Math.floor(window.innerWidth / CoreMainMenuProvider.ITEM_MIN_WIDTH); + numElements = Math.floor(window.innerWidth / MAIN_MENU_ITEM_MIN_WIDTH); // Set a maximum elements to show and skip more button. numElements = numElements >= 5 ? 5 : numElements; @@ -241,7 +263,7 @@ export class CoreMainMenuProvider { return numElements > 1 ? numElements - 1 : 1; } - return CoreMainMenuProvider.NUM_MAIN_HANDLERS; + return MAIN_MENU_NUM_MAIN_HANDLERS; } /** @@ -260,7 +282,7 @@ export class CoreMainMenuProvider { * @returns Promise resolved with boolean: whether it's the root of a main menu tab. */ async isMainMenuTab(pageName: string): Promise { - if (pageName == CoreMainMenuProvider.MORE_PAGE_NAME) { + if (pageName == MAIN_MENU_MORE_PAGE_NAME) { return true; } diff --git a/src/core/features/policy/pages/acceptances/acceptances.ts b/src/core/features/policy/pages/acceptances/acceptances.ts index 0497847ed70..c41bf30dd58 100644 --- a/src/core/features/policy/pages/acceptances/acceptances.ts +++ b/src/core/features/policy/pages/acceptances/acceptances.ts @@ -16,7 +16,7 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { CoreSites, CoreSitesReadingStrategy } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics'; import { Translate } from '@singletons'; import { CorePolicy, CorePolicySitePolicy, CorePolicyStatus } from '@features/policy/services/policy'; @@ -35,7 +35,7 @@ import { CoreLoadings } from '@services/loadings'; @Component({ selector: 'page-core-policy-acceptances', templateUrl: 'acceptances.html', - styleUrls: ['acceptances.scss'], + styleUrl: 'acceptances.scss', }) export class CorePolicyAcceptancesPage implements OnInit, OnDestroy { @@ -90,7 +90,7 @@ export class CorePolicyAcceptancesPage implements OnInit, OnDestroy { return; } - this.canContactDPO = await CoreUtils.ignoreErrors(CoreDataPrivacy.isEnabled(), false); + this.canContactDPO = await CorePromiseUtils.ignoreErrors(CoreDataPrivacy.isEnabled(), false); } /** @@ -164,9 +164,9 @@ export class CorePolicyAcceptancesPage implements OnInit, OnDestroy { * @param refresher Refresher. */ async refreshAcceptances(refresher?: HTMLIonRefresherElement): Promise { - await CoreUtils.ignoreErrors(CorePolicy.invalidateAcceptances()); + await CorePromiseUtils.ignoreErrors(CorePolicy.invalidateAcceptances()); - await CoreUtils.ignoreErrors(this.fetchAcceptances()); + await CorePromiseUtils.ignoreErrors(this.fetchAcceptances()); refresher?.complete(); } diff --git a/src/core/features/policy/pages/site-policy/site-policy.ts b/src/core/features/policy/pages/site-policy/site-policy.ts index 55ee737d4a6..94be08c6104 100644 --- a/src/core/features/policy/pages/site-policy/site-policy.ts +++ b/src/core/features/policy/pages/site-policy/site-policy.ts @@ -16,7 +16,6 @@ import { ChangeDetectorRef, Component, ElementRef, OnDestroy, OnInit, ViewChild import { CoreSites, CoreSitesReadingStrategy } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; import { CoreMimetypeUtils } from '@services/utils/mimetype'; import { CoreSite } from '@classes/sites/site'; import { CoreNavigator } from '@services/navigator'; @@ -33,6 +32,7 @@ import { CoreDom } from '@singletons/dom'; import { CoreWait } from '@singletons/wait'; import { CoreModals } from '@services/modals'; import { CoreLoadings } from '@services/loadings'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Page to accept a site policy. @@ -40,7 +40,7 @@ import { CoreLoadings } from '@services/loadings'; @Component({ selector: 'page-core-policy-site-policy', templateUrl: 'site-policy.html', - styleUrls: ['site-policy.scss'], + styleUrl: 'site-policy.scss', }) export class CorePolicySitePolicyPage implements OnInit, OnDestroy { @@ -83,7 +83,7 @@ export class CorePolicySitePolicyPage implements OnInit, OnDestroy { try { this.currentSite = CoreSites.getRequiredCurrentSite(); - this.siteName = (await CoreUtils.ignoreErrors(this.currentSite.getSiteName(), '')) || ''; + this.siteName = (await CorePromiseUtils.ignoreErrors(this.currentSite.getSiteName(), '')) || ''; } catch { // Not logged in, stop. this.cancel(); @@ -140,7 +140,7 @@ export class CorePolicySitePolicyPage implements OnInit, OnDestroy { // Try to get the mime type. try { - const mimeType = await CoreUtils.getMimeTypeFromUrl(this.sitePoliciesURL); + const mimeType = await CoreMimetypeUtils.getMimeTypeFromUrl(this.sitePoliciesURL); const extension = CoreMimetypeUtils.getExtension(mimeType, this.sitePoliciesURL); this.showInline = extension == 'html' || extension == 'htm'; @@ -280,7 +280,7 @@ export class CorePolicySitePolicyPage implements OnInit, OnDestroy { * @returns Promise resolved when done. */ async cancel(): Promise { - await CoreUtils.ignoreErrors(CoreSites.logout()); + await CorePromiseUtils.ignoreErrors(CoreSites.logout()); await CoreNavigator.navigate('/login/sites', { reset: true }); } @@ -442,7 +442,7 @@ export class CorePolicySitePolicyPage implements OnInit, OnDestroy { */ protected async finishAcceptingPolicies(): Promise { // Invalidate cache since some WS don't return error if site policy is not accepted. - await CoreUtils.ignoreErrors(this.currentSite.invalidateWsCache()); + await CorePromiseUtils.ignoreErrors(this.currentSite.invalidateWsCache()); CoreEvents.trigger(CoreEvents.SITE_POLICY_AGREED, {}, this.siteId); diff --git a/src/core/features/policy/policy-lazy.module.ts b/src/core/features/policy/policy-lazy.module.ts index 7fc310608bb..f2ab53d1409 100644 --- a/src/core/features/policy/policy-lazy.module.ts +++ b/src/core/features/policy/policy-lazy.module.ts @@ -41,4 +41,4 @@ const routes: Routes = [ CorePolicyAcceptancesPage, ], }) -export class CorePolicyLazyModule {} +export default class CorePolicyLazyModule {} diff --git a/src/core/features/policy/policy.module.ts b/src/core/features/policy/policy.module.ts index 407713570a1..9750842f3f6 100644 --- a/src/core/features/policy/policy.module.ts +++ b/src/core/features/policy/policy.module.ts @@ -27,7 +27,7 @@ import { CoreContentLinksDelegate } from '@features/contentlinks/services/conten const routes: Routes = [ { path: POLICY_PAGE_NAME, - loadChildren: () => import('./policy-lazy.module').then(m => m.CorePolicyLazyModule), + loadChildren: () => import('./policy-lazy.module'), }, ]; diff --git a/src/core/features/policy/services/policy.ts b/src/core/features/policy/services/policy.ts index df7b8a8f961..f3e5e7a823b 100644 --- a/src/core/features/policy/services/policy.ts +++ b/src/core/features/policy/services/policy.ts @@ -21,7 +21,7 @@ import { CoreSites, CoreSitesCommonWSOptions } from '@services/sites'; import { CoreWSExternalWarning } from '@services/ws'; import { makeSingleton } from '@singletons'; import { POLICY_PAGE_NAME, SITE_POLICY_PAGE_NAME } from '../constants'; -import { CoreSite } from '@classes/sites/site'; +import { CoreCacheUpdateFrequency } from '@/core/constants'; /** * Service that provides some common features regarding policies. @@ -140,7 +140,7 @@ export class CorePolicyService { }; const preSets = { cacheKey: this.getUserAcceptancesCacheKey(userId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), }; diff --git a/src/core/features/pushnotifications/services/database/pushnotifications.ts b/src/core/features/pushnotifications/services/database/pushnotifications.ts index 37d851bba03..d95d1249fa6 100644 --- a/src/core/features/pushnotifications/services/database/pushnotifications.ts +++ b/src/core/features/pushnotifications/services/database/pushnotifications.ts @@ -13,7 +13,7 @@ // limitations under the License. import { SQLiteDB } from '@classes/sqlitedb'; -import { CoreAppSchema } from '@services/app'; +import { CoreAppSchema } from '@services/app-db'; import { CoreSiteSchema } from '@services/sites'; /** diff --git a/src/core/features/pushnotifications/services/push-delegate.ts b/src/core/features/pushnotifications/services/push-delegate.ts index af24f47c9d3..d75e0c9fc1e 100644 --- a/src/core/features/pushnotifications/services/push-delegate.ts +++ b/src/core/features/pushnotifications/services/push-delegate.ts @@ -16,7 +16,7 @@ import { Injectable } from '@angular/core'; import { Subject } from 'rxjs'; import { CoreSites } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { makeSingleton } from '@singletons'; import { CoreLogger } from '@singletons/logger'; import { CorePushNotificationsNotificationBasicData } from './pushnotifications'; @@ -102,7 +102,7 @@ export class CorePushNotificationsDelegateService { } }); - await CoreUtils.ignoreErrors(CoreUtils.allPromises(promises)); + await CorePromiseUtils.allPromisesIgnoringErrors(promises); // Sort by priority. handlers = handlers.sort((a, b) => (a.priority || 0) <= (b.priority || 0) ? 1 : -1); diff --git a/src/core/features/pushnotifications/services/pushnotifications.ts b/src/core/features/pushnotifications/services/pushnotifications.ts index 0ac67814966..3144fbeae36 100644 --- a/src/core/features/pushnotifications/services/pushnotifications.ts +++ b/src/core/features/pushnotifications/services/pushnotifications.ts @@ -16,11 +16,11 @@ import { Injectable } from '@angular/core'; import { ILocalNotification } from '@awesome-cordova-plugins/local-notifications'; import { NotificationEventResponse, PushOptions, RegistrationEventResponse } from '@awesome-cordova-plugins/push/ngx'; -import { CoreApp } from '@services/app'; +import { CoreAppDB } from '@services/app-db'; import { CoreSites } from '@services/sites'; import { CorePushNotificationsDelegate } from './push-delegate'; import { CoreLocalNotifications } from '@services/local-notifications'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { CoreText } from '@singletons/text'; import { CoreConfig } from '@services/config'; import { CoreConstants } from '@/core/constants'; @@ -44,7 +44,6 @@ import { import { CoreError } from '@classes/errors/error'; import { CoreWSExternalWarning } from '@services/ws'; import { CoreSitesFactory } from '@services/sites-factory'; -import { CoreMainMenuProvider } from '@features/mainmenu/services/mainmenu'; import { AsyncInstance, asyncInstance } from '@/core/utils/async-instance'; import { CoreDatabaseTable } from '@classes/database/database-table'; import { CoreDatabaseCachingStrategy, CoreDatabaseTableProxy } from '@classes/database/database-table-proxy'; @@ -56,6 +55,9 @@ import { CoreSiteInfo } from '@classes/sites/unauthenticated-site'; import { Push } from '@features/native/plugins'; import { CoreNavigator } from '@services/navigator'; import { CoreWait } from '@singletons/wait'; +import { MAIN_MENU_HANDLER_BADGE_UPDATED_EVENT } from '@features/mainmenu/constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; +import { CoreWSError } from '@classes/errors/wserror'; /** * Service to handle push notifications. @@ -149,7 +151,7 @@ export class CorePushNotificationsProvider { } }); - CoreEvents.on(CoreMainMenuProvider.MAIN_MENU_HANDLER_BADGE_UPDATED, (data) => { + CoreEvents.on(MAIN_MENU_HANDLER_BADGE_UPDATED_EVENT, (data) => { this.updateAddonCounter(data.handler, data.value, data.siteId); }); @@ -204,13 +206,9 @@ export class CorePushNotificationsProvider { * @returns Promise resolved when done. */ protected async initializeDatabase(): Promise { - try { - await CoreApp.createTablesFromSchema(APP_SCHEMA); - } catch { - // Ignore errors. - } + await CoreAppDB.createTablesFromSchema(APP_SCHEMA); - const database = CoreApp.getDB(); + const database = CoreAppDB.getDB(); const badgesTable = new CoreDatabaseTableProxy( { cachingStrategy: CoreDatabaseCachingStrategy.Eager }, database, @@ -581,9 +579,9 @@ export class CorePushNotificationsProvider { try { response = await site.write('core_user_remove_user_device', data); } catch (error) { - if (CoreUtils.isWebServiceError(error) || CoreUtils.isExpiredTokenError(error)) { + if (CoreWSError.isWebServiceError(error) || CoreWSError.isExpiredTokenError(error)) { // Cannot unregister. Don't try again. - await CoreUtils.ignoreErrors(this.pendingUnregistersTable.delete({ + await CorePromiseUtils.ignoreErrors(this.pendingUnregistersTable.delete({ token: site.getToken(), siteid: site.getId(), })); @@ -606,7 +604,7 @@ export class CorePushNotificationsProvider { throw new CoreError('Cannot unregister device'); } - await CoreUtils.ignoreErrors(Promise.all([ + await CorePromiseUtils.ignoreErrors(Promise.all([ // Remove the device from the local DB. this.registeredDevicesTables[site.getId()].delete(this.getRequiredRegisterData()), // Remove pending unregisters for this site. @@ -755,7 +753,7 @@ export class CorePushNotificationsProvider { if (neededActions.unregister) { // Unregister the device first. - await CoreUtils.ignoreErrors(this.unregisterDeviceOnMoodle(site)); + await CorePromiseUtils.ignoreErrors(this.unregisterDeviceOnMoodle(site)); } if (neededActions.register) { @@ -773,7 +771,7 @@ export class CorePushNotificationsProvider { CoreEvents.trigger(CoreEvents.DEVICE_REGISTERED_IN_MOODLE, {}, site.getId()); // Insert the device in the local DB. - await CoreUtils.ignoreErrors(this.registeredDevicesTables[site.getId()].insert(data)); + await CorePromiseUtils.ignoreErrors(this.registeredDevicesTables[site.getId()].insert(data)); } else if (neededActions.updatePublicKey) { // Device already registered, make sure the public key is up to date. const response = await this.updatePublicKeyOnMoodle(site, data); @@ -793,7 +791,7 @@ export class CorePushNotificationsProvider { } } finally { // Remove pending unregisters for this site. - await CoreUtils.ignoreErrors(this.pendingUnregistersTable.deleteByPrimaryKey({ siteid: site.getId() })); + await CorePromiseUtils.ignoreErrors(this.pendingUnregistersTable.deleteByPrimaryKey({ siteid: site.getId() })); } } @@ -934,7 +932,7 @@ export class CorePushNotificationsProvider { } // Check if the device is already registered. - const records = await CoreUtils.ignoreErrors( + const records = await CorePromiseUtils.ignoreErrors( this.registeredDevicesTables[site.getId()].getMany({ appid: data.appid, uuid: data.uuid, diff --git a/src/core/features/question/components/question/question.ts b/src/core/features/question/components/question/question.ts index 7a7fbf7efb9..e1ac05885a1 100644 --- a/src/core/features/question/components/question/question.ts +++ b/src/core/features/question/components/question/question.ts @@ -22,7 +22,7 @@ import { CoreQuestionDelegate } from '@features/question/services/question-deleg import { CoreQuestionBehaviourButton, CoreQuestionHelper, CoreQuestionQuestion } from '@features/question/services/question-helper'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { Translate } from '@singletons'; import { CoreDirectivesRegistry } from '@singletons/directives-registry'; import { CoreLogger } from '@singletons/logger'; @@ -33,7 +33,7 @@ import { CoreLogger } from '@singletons/logger'; @Component({ selector: 'core-question', templateUrl: 'core-question.html', - styleUrls: ['../../question.scss'], + styleUrl: '../../question.scss', }) export class CoreQuestionComponent implements OnInit, AsyncDirective { @@ -87,7 +87,7 @@ export class CoreQuestionComponent implements OnInit, AsyncDirective { } // Get the component to render the question. - this.componentClass = await CoreUtils.ignoreErrors( + this.componentClass = await CorePromiseUtils.ignoreErrors( CoreQuestionDelegate.getComponentForQuestion(this.question), ); diff --git a/src/core/features/question/services/question-helper.ts b/src/core/features/question/services/question-helper.ts index d8dc0c5fcd2..0ed8aeffa9e 100644 --- a/src/core/features/question/services/question-helper.ts +++ b/src/core/features/question/services/question-helper.ts @@ -21,7 +21,7 @@ import { CoreFilepool } from '@services/filepool'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreText } from '@singletons/text'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { CoreWSFile } from '@services/ws'; import { makeSingleton, Translate } from '@singletons'; import { CoreQuestion, CoreQuestionProvider, CoreQuestionQuestionParsed, CoreQuestionsAnswers } from './question'; @@ -33,6 +33,7 @@ import { CoreIonicColorNames } from '@singletons/colors'; import { CoreViewer } from '@features/viewer/services/viewer'; import { convertTextToHTMLElement } from '@/core/utils/create-html-element'; import { AddonModQuizNavigationQuestion } from '@addons/mod/quiz/components/navigation-modal/navigation-modal'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Service with some common functions to handle questions. @@ -115,7 +116,7 @@ export class CoreQuestionHelperProvider { const folderPath = CoreQuestion.getQuestionFolder(question.type, component, questionComponentId, siteId); // Ignore errors, maybe the folder doesn't exist. - await CoreUtils.ignoreErrors(CoreFile.removeDir(folderPath)); + await CorePromiseUtils.ignoreErrors(CoreFile.removeDir(folderPath)); } /** @@ -570,7 +571,7 @@ export class CoreQuestionHelperProvider { * @returns Promise resolved when done. */ async loadLocalAnswers(question: CoreQuestionQuestion, component: string, attemptId: number): Promise { - const answers = await CoreUtils.ignoreErrors( + const answers = await CorePromiseUtils.ignoreErrors( CoreQuestion.getQuestionAnswers(component, attemptId, question.slot), ); @@ -715,7 +716,7 @@ export class CoreQuestionHelperProvider { componentId: string | number, siteId?: string, ): Promise { - await CoreUtils.allPromises(questions.map(async (question) => { + await CorePromiseUtils.allPromises(questions.map(async (question) => { await CoreQuestionDelegate.prepareAnswersForQuestion( question, answers, diff --git a/src/core/features/question/services/question.ts b/src/core/features/question/services/question.ts index 1426bba3d8f..99331ff7b3c 100644 --- a/src/core/features/question/services/question.ts +++ b/src/core/features/question/services/question.ts @@ -18,7 +18,7 @@ import { CoreFile } from '@services/file'; import { CoreSites } from '@services/sites'; import { CoreText } from '@singletons/text'; import { CoreTimeUtils } from '@services/utils/time'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreArray } from '@singletons/array'; import { CoreWSExternalFile } from '@services/ws'; import { makeSingleton } from '@singletons'; import { CorePath } from '@singletons/path'; @@ -37,6 +37,7 @@ import { QUESTION_NEEDS_GRADING_STATE_CLASSES, QUESTION_TODO_STATE_CLASSES, } from '@features/question/constants'; +import { CoreObject } from '@singletons/object'; const QUESTION_PREFIX_REGEX = /q\d+:(\d+)_/; const STATES: Record = { @@ -150,7 +151,7 @@ export class CoreQuestionProvider { */ compareAllAnswers(prevAnswers: Record, newAnswers: Record): boolean { // Get all the keys. - const keys = CoreUtils.mergeArraysWithoutDuplicates(Object.keys(prevAnswers), Object.keys(newAnswers)); + const keys = CoreArray.mergeWithoutDuplicates(Object.keys(prevAnswers), Object.keys(newAnswers)); // Check that all the keys have the same value on both objects. for (const i in keys) { @@ -158,7 +159,7 @@ export class CoreQuestionProvider { // Ignore extra answers like sequencecheck or certainty. if (!this.isExtraAnswer(key[0])) { - if (!CoreUtils.sameAtKeyMissingIsBlank(prevAnswers, newAnswers, key)) { + if (!CoreObject.sameAtKeyMissingIsBlank(prevAnswers, newAnswers, key)) { return false; } } diff --git a/src/core/features/rating/services/rating-offline.ts b/src/core/features/rating/services/rating-offline.ts index b4a84aa2f43..d848457f52a 100644 --- a/src/core/features/rating/services/rating-offline.ts +++ b/src/core/features/rating/services/rating-offline.ts @@ -15,7 +15,7 @@ import { ContextLevel } from '@/core/constants'; import { Injectable } from '@angular/core'; import { CoreSites } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { makeSingleton } from '@singletons'; import { CoreRatingDBPrimaryData, CoreRatingDBRecord, RATINGS_TABLE } from './database/rating'; @@ -265,7 +265,7 @@ export class CoreRatingOfflineProvider { conditions.itemsetid = itemSetId; } - return CoreUtils.promiseWorks(site.getDb().recordExists(RATINGS_TABLE, conditions)); + return CorePromiseUtils.promiseWorks(site.getDb().recordExists(RATINGS_TABLE, conditions)); } } diff --git a/src/core/features/rating/services/rating-sync.ts b/src/core/features/rating/services/rating-sync.ts index 9e51a576f57..0a9e5e54dba 100644 --- a/src/core/features/rating/services/rating-sync.ts +++ b/src/core/features/rating/services/rating-sync.ts @@ -19,7 +19,7 @@ import { CoreNetworkError } from '@classes/errors/network-error'; import { CoreNetwork } from '@services/network'; import { CoreSites } from '@services/sites'; import { CoreErrorHelper } from '@services/error-helper'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreWSError } from '@classes/errors/wserror'; import { makeSingleton } from '@singletons'; import { CoreEvents } from '@singletons/events'; import { CoreRating } from './rating'; @@ -222,7 +222,7 @@ export class CoreRatingSyncProvider extends CoreSyncBaseProvider => { // Migrate reminders. New format @since 4.0. const oldTable = 'addon_calendar_reminders'; - const tableExists = await CoreUtils.promiseWorks(db.tableExists(oldTable)); + const tableExists = await CorePromiseUtils.promiseWorks(db.tableExists(oldTable)); if (!tableExists) { return; } @@ -100,7 +101,7 @@ const migrateFromCalendarRemindersV1 = async (db: SQLiteDB): Promise => { // Get the event to compare the reminder time with the event time. if (!events[record.eventid]) { try { - events[record.eventid] = await db.getRecord(EVENTS_TABLE, { id: record.eventid }); + events[record.eventid] = await db.getRecord(ADDON_CALENDAR_EVENTS_TABLE, { id: record.eventid }); } catch { // Event not found in local DB, shouldn't happen. Ignore the reminder. return; @@ -113,7 +114,7 @@ const migrateFromCalendarRemindersV1 = async (db: SQLiteDB): Promise => { if (!reminderTime || reminderTime === -1) { // Default reminder. - reminderTime = CoreRemindersService.DEFAULT_REMINDER_TIMEBEFORE; + reminderTime = REMINDERS_DEFAULT_REMINDER_TIMEBEFORE; } else if (reminderTime > event.timestart) { // Reminder is after the event, ignore it. return; @@ -144,7 +145,7 @@ const migrateFromCalendarRemindersV1 = async (db: SQLiteDB): Promise => { const migrateFromCalendarRemindersV2 = async (db: SQLiteDB): Promise => { const oldTable = 'addon_calendar_reminders_2'; - const tableExists = await CoreUtils.promiseWorks(db.tableExists(oldTable)); + const tableExists = await CorePromiseUtils.promiseWorks(db.tableExists(oldTable)); if (!tableExists) { return; } @@ -157,7 +158,7 @@ const migrateFromCalendarRemindersV2 = async (db: SQLiteDB): Promise => { // Get the event to compare the reminder time with the event time. if (!events[record.eventid]) { try { - events[record.eventid] = await db.getRecord(EVENTS_TABLE, { id: record.eventid }); + events[record.eventid] = await db.getRecord(ADDON_CALENDAR_EVENTS_TABLE, { id: record.eventid }); } catch { // Event not found in local DB, shouldn't happen. Ignore the reminder. return; @@ -165,7 +166,7 @@ const migrateFromCalendarRemindersV2 = async (db: SQLiteDB): Promise => { } const event = events[record.eventid]; - const reminderTime = record.time || CoreRemindersService.DEFAULT_REMINDER_TIMEBEFORE; + const reminderTime = record.time || REMINDERS_DEFAULT_REMINDER_TIMEBEFORE; if (uniqueReminder[record.eventid] === undefined) { uniqueReminder[record.eventid] = []; diff --git a/src/core/features/reminders/services/reminders.ts b/src/core/features/reminders/services/reminders.ts index d9e995f3bd0..fc0741de174 100644 --- a/src/core/features/reminders/services/reminders.ts +++ b/src/core/features/reminders/services/reminders.ts @@ -27,31 +27,14 @@ import { lazyMap, LazyMap } from '@/core/utils/lazy-map'; import { CoreDatabaseTable } from '@classes/database/database-table'; import { asyncInstance, AsyncInstance } from '@/core/utils/async-instance'; import { CoreDatabaseCachingStrategy } from '@classes/database/database-table-proxy'; - -/** - * Units to set a reminder. - */ -export enum CoreRemindersUnits { - MINUTE = CoreConstants.SECONDS_MINUTE, - HOUR = CoreConstants.SECONDS_HOUR, - DAY = CoreConstants.SECONDS_DAY, - WEEK = CoreConstants.SECONDS_WEEK, -} - -const REMINDER_UNITS_LABELS = { - single: { - [CoreRemindersUnits.MINUTE]: 'core.minute', - [CoreRemindersUnits.HOUR]: 'core.hour', - [CoreRemindersUnits.DAY]: 'core.day', - [CoreRemindersUnits.WEEK]: 'core.week', - }, - multi: { - [CoreRemindersUnits.MINUTE]: 'core.minutes', - [CoreRemindersUnits.HOUR]: 'core.hours', - [CoreRemindersUnits.DAY]: 'core.days', - [CoreRemindersUnits.WEEK]: 'core.weeks', - }, -}; +import { + CoreRemindersUnits, + REMINDERS_DEFAULT_NOTIFICATION_TIME_CHANGED, + REMINDERS_DEFAULT_NOTIFICATION_TIME_SETTING, + REMINDERS_DEFAULT_REMINDER_TIMEBEFORE, + REMINDERS_DISABLED, + REMINDERS_UNITS_LABELS, +} from '../constants'; /** * Service to handle reminders. @@ -59,11 +42,23 @@ const REMINDER_UNITS_LABELS = { @Injectable({ providedIn: 'root' }) export class CoreRemindersService { - static readonly DEFAULT_REMINDER_TIMEBEFORE = -1; - static readonly DISABLED = -1; + /** + * @deprecated since 5.0. Use REMINDERS_DEFAULT_REMINDER_TIMEBEFORE instead. + */ + static readonly DEFAULT_REMINDER_TIMEBEFORE = REMINDERS_DEFAULT_REMINDER_TIMEBEFORE; + /** + * @deprecated since 5.0. Use REMINDERS_DISABLED instead. + */ + static readonly DISABLED = REMINDERS_DISABLED; - static readonly DEFAULT_NOTIFICATION_TIME_SETTING = 'CoreRemindersDefaultNotification'; - static readonly DEFAULT_NOTIFICATION_TIME_CHANGED = 'CoreRemindersDefaultNotificationChangedEvent'; + /** + * @deprecated since 5.0. Use REMINDERS_DEFAULT_NOTIFICATION_TIME_SETTING instead. + */ + static readonly DEFAULT_NOTIFICATION_TIME_SETTING = REMINDERS_DEFAULT_NOTIFICATION_TIME_SETTING; + /** + * @deprecated since 5.0. Use REMINDERS_DEFAULT_NOTIFICATION_TIME_CHANGED instead. + */ + static readonly DEFAULT_NOTIFICATION_TIME_CHANGED = REMINDERS_DEFAULT_NOTIFICATION_TIME_CHANGED; protected remindersTables: LazyMap>>; @@ -91,7 +86,7 @@ export class CoreRemindersService { this.scheduleAllNotifications(); - CoreEvents.on(CoreRemindersService.DEFAULT_NOTIFICATION_TIME_CHANGED, async (data) => { + CoreEvents.on(REMINDERS_DEFAULT_NOTIFICATION_TIME_CHANGED, async (data) => { const site = await CoreSites.getSite(data.siteId); const siteId = site.getId(); @@ -215,7 +210,7 @@ export class CoreRemindersService { protected async getRemindersWithDefaultTime(siteId?: string): Promise { siteId ??= CoreSites.getCurrentSiteId(); - return this.remindersTables[siteId].getMany({ timebefore: CoreRemindersService.DEFAULT_REMINDER_TIMEBEFORE }, { + return this.remindersTables[siteId].getMany({ timebefore: REMINDERS_DEFAULT_REMINDER_TIMEBEFORE }, { sorting: [ { time: 'asc' }, ], @@ -294,11 +289,11 @@ export class CoreRemindersService { siteId = siteId || CoreSites.getCurrentSiteId(); - const timebefore = reminder.timebefore === CoreRemindersService.DEFAULT_REMINDER_TIMEBEFORE + const timebefore = reminder.timebefore === REMINDERS_DEFAULT_REMINDER_TIMEBEFORE ? await this.getDefaultNotificationTime(siteId) : reminder.timebefore; - if (timebefore === CoreRemindersService.DISABLED) { + if (timebefore === REMINDERS_DISABLED) { // Notification disabled. Cancel. return this.cancelReminder(reminder.id, reminder.component, siteId); } @@ -364,7 +359,7 @@ export class CoreRemindersService { * @returns Translated label. */ getUnitValueLabel(value: number, unit: CoreRemindersUnits, addDefaultLabel = false): string { - if (value === CoreRemindersService.DISABLED) { + if (value === REMINDERS_DISABLED) { return Translate.instant('core.settings.disabled'); } @@ -373,8 +368,8 @@ export class CoreRemindersService { } const unitsLabel = value === 1 ? - REMINDER_UNITS_LABELS.single[unit] : - REMINDER_UNITS_LABELS.multi[unit]; + REMINDERS_UNITS_LABELS.single[unit] : + REMINDERS_UNITS_LABELS.multi[unit]; const label = Translate.instant('core.reminders.timebefore', { units: Translate.instant(unitsLabel), @@ -397,7 +392,7 @@ export class CoreRemindersService { static convertSecondsToValueAndUnit(seconds?: number): CoreReminderValueAndUnit { if (seconds === undefined || seconds < 0) { return { - value: CoreRemindersService.DISABLED, + value: REMINDERS_DISABLED, unit: CoreRemindersUnits.MINUTE, }; } else if (seconds === 0) { @@ -437,7 +432,7 @@ export class CoreRemindersService { async getDefaultNotificationTime(siteId?: string): Promise { siteId = siteId || CoreSites.getCurrentSiteId(); - const key = CoreRemindersService.DEFAULT_NOTIFICATION_TIME_SETTING + '#' + siteId; + const key = REMINDERS_DEFAULT_NOTIFICATION_TIME_SETTING + '#' + siteId; return CoreConfig.get(key, CoreConstants.CONFIG.calendarreminderdefaultvalue || 3600); } @@ -452,11 +447,11 @@ export class CoreRemindersService { async setDefaultNotificationTime(time: number, siteId?: string): Promise { siteId = siteId || CoreSites.getCurrentSiteId(); - const key = CoreRemindersService.DEFAULT_NOTIFICATION_TIME_SETTING + '#' + siteId; + const key = REMINDERS_DEFAULT_NOTIFICATION_TIME_SETTING + '#' + siteId; await CoreConfig.set(key, time); - CoreEvents.trigger(CoreRemindersService.DEFAULT_NOTIFICATION_TIME_CHANGED, { time }, siteId); + CoreEvents.trigger(REMINDERS_DEFAULT_NOTIFICATION_TIME_CHANGED, { time }, siteId); } } diff --git a/src/core/features/reportbuilder/components/report-column/report-column.ts b/src/core/features/reportbuilder/components/report-column/report-column.ts index 42700014557..cb31ebaaf63 100644 --- a/src/core/features/reportbuilder/components/report-column/report-column.ts +++ b/src/core/features/reportbuilder/components/report-column/report-column.ts @@ -19,7 +19,7 @@ import { CoreReportBuilder } from '@features/reportbuilder/services/reportbuilde @Component({ selector: 'core-report-builder-report-column', templateUrl: './report-column.html', - styleUrls: ['./report-column.scss'], + styleUrl: './report-column.scss', }) export class CoreReportBuilderReportColumnComponent { diff --git a/src/core/features/reportbuilder/components/report-detail/report-detail.ts b/src/core/features/reportbuilder/components/report-detail/report-detail.ts index 67d7cbf36af..8fe12e1e042 100644 --- a/src/core/features/reportbuilder/components/report-detail/report-detail.ts +++ b/src/core/features/reportbuilder/components/report-detail/report-detail.ts @@ -27,16 +27,17 @@ import { CoreScreen } from '@services/screen'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreErrorObject } from '@services/error-helper'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreOpener } from '@singletons/opener'; import { Translate } from '@singletons'; import { CoreTime } from '@singletons/time'; import { BehaviorSubject, Observable } from 'rxjs'; import { map } from 'rxjs/operators'; +import { CorePromiseUtils } from '@singletons/promise-utils'; @Component({ selector: 'core-report-builder-report-detail', templateUrl: './report-detail.html', - styleUrls: ['./report-detail.scss'], + styleUrl: './report-detail.scss', }) export class CoreReportBuilderReportDetailComponent implements OnInit { @@ -78,7 +79,7 @@ export class CoreReportBuilderReportDetailComponent implements OnInit { ); this.logView = CoreTime.once(async (report) => { - await CoreUtils.ignoreErrors(CoreReportBuilder.viewReport(this.reportId)); + await CorePromiseUtils.ignoreErrors(CoreReportBuilder.viewReport(this.reportId)); CoreAnalytics.logEvent({ type: CoreAnalyticsEventType.VIEW_ITEM, @@ -149,7 +150,7 @@ export class CoreReportBuilderReportDetailComponent implements OnInit { handler: async () => { const site = CoreSites.getRequiredCurrentSite(); const href = `${site.getURL()}/reportbuilder/view.php?id=${this.reportId}`; - await CoreUtils.openInBrowser(href, { showBrowserWarning: false }); + await CoreOpener.openInBrowser(href, { showBrowserWarning: false }); await CoreNavigator.back(); }, }, @@ -171,9 +172,9 @@ export class CoreReportBuilderReportDetailComponent implements OnInit { * @param ionRefresher ionic refresher. */ async refreshReport(ionRefresher?: HTMLIonRefresherElement): Promise { - await CoreUtils.ignoreErrors(CoreReportBuilder.invalidateReport()); + await CorePromiseUtils.ignoreErrors(CoreReportBuilder.invalidateReport()); this.updateState({ page: 0, canLoadMoreRows: false }); - await CoreUtils.ignoreErrors(this.getReport()); + await CorePromiseUtils.ignoreErrors(this.getReport()); await ionRefresher?.complete(); } diff --git a/src/core/features/reportbuilder/pages/list/list.ts b/src/core/features/reportbuilder/pages/list/list.ts index 6d951d1a9e4..d0d91aa74d1 100644 --- a/src/core/features/reportbuilder/pages/list/list.ts +++ b/src/core/features/reportbuilder/pages/list/list.ts @@ -20,7 +20,7 @@ import { CoreReportBuilder, CoreReportBuilderReport, REPORTS_LIST_LIMIT } from ' import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics'; import { CoreNavigator } from '@services/navigator'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { Translate } from '@singletons'; import { CoreTime } from '@singletons/time'; import { BehaviorSubject } from 'rxjs'; @@ -114,8 +114,8 @@ export class CoreReportBuilderListPage implements AfterViewInit, OnDestroy { * @param ionRefresher ionRefresher. */ async refreshReports(ionRefresher?: HTMLIonRefresherElement): Promise { - await CoreUtils.ignoreErrors(CoreReportBuilder.invalidateReportsList()); - await CoreUtils.ignoreErrors(this.fetchReports(true)); + await CorePromiseUtils.ignoreErrors(CoreReportBuilder.invalidateReportsList()); + await CorePromiseUtils.ignoreErrors(this.fetchReports(true)); await ionRefresher?.complete(); } diff --git a/src/core/features/reportbuilder/reportbuilder-lazy.module.ts b/src/core/features/reportbuilder/reportbuilder-lazy.module.ts index c5064d38860..7e142f6dd15 100644 --- a/src/core/features/reportbuilder/reportbuilder-lazy.module.ts +++ b/src/core/features/reportbuilder/reportbuilder-lazy.module.ts @@ -41,4 +41,4 @@ const routes: Routes = [ CoreReportBuilderReportPage, ], }) -export class CoreReportBuilderLazyModule {} +export default class CoreReportBuilderLazyModule {} diff --git a/src/core/features/reportbuilder/reportbuilder.module.ts b/src/core/features/reportbuilder/reportbuilder.module.ts index 98680d344e9..ba5cdca4b28 100644 --- a/src/core/features/reportbuilder/reportbuilder.module.ts +++ b/src/core/features/reportbuilder/reportbuilder.module.ts @@ -23,7 +23,7 @@ import { CoreReportBuilderHandler, CoreReportBuilderHandlerService } from './ser const routes: Routes = [ { path: CoreReportBuilderHandlerService.PAGE_NAME, - loadChildren: () => import('./reportbuilder-lazy.module').then(m => m.CoreReportBuilderLazyModule), + loadChildren: () => import('./reportbuilder-lazy.module'), }, ]; diff --git a/src/core/features/search/components/global-search-filters/global-search-filters.component.ts b/src/core/features/search/components/global-search-filters/global-search-filters.component.ts index c956f216e1c..d704b9d718c 100644 --- a/src/core/features/search/components/global-search-filters/global-search-filters.component.ts +++ b/src/core/features/search/components/global-search-filters/global-search-filters.component.ts @@ -22,7 +22,7 @@ import { } from '@features/search/services/global-search'; import { CoreEvents } from '@singletons/events'; import { ModalController } from '@singletons'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreSharedModule } from '@/core/shared.module'; import { toBoolean } from '@/core/transforms/boolean'; @@ -31,7 +31,7 @@ type Filter = T & { checked: boolean }; @Component({ selector: 'core-search-global-search-filters', templateUrl: 'global-search-filters.html', - styleUrls: ['./global-search-filters.scss'], + styleUrl: './global-search-filters.scss', standalone: true, imports: [ CoreSharedModule, @@ -151,7 +151,7 @@ export class CoreSearchGlobalSearchFiltersComponent implements OnInit { * @param refresher Refresher. */ async refreshFilters(refresher?: HTMLIonRefresherElement): Promise { - await CoreUtils.ignoreErrors(Promise.all([ + await CorePromiseUtils.ignoreErrors(Promise.all([ CoreSearchGlobalSearch.invalidateSearchAreas(), CoreCourses.invalidateUserCourses(), ])); diff --git a/src/core/features/search/components/global-search-result/global-search-result.ts b/src/core/features/search/components/global-search-result/global-search-result.ts index 7349e08993a..a9aa23d6f9f 100644 --- a/src/core/features/search/components/global-search-result/global-search-result.ts +++ b/src/core/features/search/components/global-search-result/global-search-result.ts @@ -19,7 +19,7 @@ import { CoreSearchGlobalSearchResult, CoreSearchGlobalSearchResultContext } fro @Component({ selector: 'core-search-global-search-result', templateUrl: 'global-search-result.html', - styleUrls: ['./global-search-result.scss'], + styleUrl: './global-search-result.scss', }) export class CoreSearchGlobalSearchResultComponent implements OnChanges { diff --git a/src/core/features/search/components/search-box/search-box.ts b/src/core/features/search/components/search-box/search-box.ts index 6494339ec90..7ae54709fd6 100644 --- a/src/core/features/search/components/search-box/search-box.ts +++ b/src/core/features/search/components/search-box/search-box.ts @@ -34,7 +34,7 @@ import { toBoolean } from '@/core/transforms/boolean'; @Component({ selector: 'core-search-box', templateUrl: 'core-search-box.html', - styleUrls: ['search-box.scss'], + styleUrl: 'search-box.scss', }) export class CoreSearchBoxComponent implements OnInit { diff --git a/src/core/features/search/pages/global-search/global-search.ts b/src/core/features/search/pages/global-search/global-search.ts index 00451e924e0..9e7091f8c6b 100644 --- a/src/core/features/search/pages/global-search/global-search.ts +++ b/src/core/features/search/pages/global-search/global-search.ts @@ -16,7 +16,7 @@ import { Component, OnInit, OnDestroy, AfterViewInit, ViewChild } from '@angular import { CoreDomUtils } from '@services/utils/dom'; import { CoreSearchGlobalSearchResultsSource } from '@features/search/classes/global-search-results-source'; import { CoreSites } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics'; import { Translate } from '@singletons'; import { CoreUrl } from '@singletons/url'; @@ -30,6 +30,7 @@ import { import { CoreNavigator } from '@services/navigator'; import { CoreSearchBoxComponent } from '@features/search/components/search-box/search-box'; import { CoreModals } from '@services/modals'; +import { CorePromiseUtils } from '@singletons/promise-utils'; @Component({ selector: 'page-core-search-global-search', @@ -104,7 +105,7 @@ export class CoreSearchGlobalSearchPage implements OnInit, OnDestroy, AfterViewI await CoreDomUtils.showOperationModals('core.searching', true, async () => { await this.resultsSource.reload(); - await CoreUtils.ignoreErrors( + await CorePromiseUtils.ignoreErrors( CoreSearchGlobalSearch.logViewResults(this.resultsSource.getQuery(), this.resultsSource.getFilters()), ); diff --git a/src/core/features/search/search-lazy.module.ts b/src/core/features/search/search-lazy.module.ts index 55d6d586215..05bc71bbec2 100644 --- a/src/core/features/search/search-lazy.module.ts +++ b/src/core/features/search/search-lazy.module.ts @@ -50,4 +50,4 @@ function buildRoutes(injector: Injector): Routes { }, ], }) -export class CoreSearchLazyModule {} +export default class CoreSearchLazyModule {} diff --git a/src/core/features/search/search.module.ts b/src/core/features/search/search.module.ts index c7fc4375cb9..8a3ea5be0f1 100644 --- a/src/core/features/search/search.module.ts +++ b/src/core/features/search/search.module.ts @@ -44,7 +44,7 @@ export async function getSearchServices(): Promise[]> { const mainMenuChildrenRoutes: Routes = [ { path: CORE_SEARCH_PAGE_NAME, - loadChildren: () => import('./search-lazy.module').then(m => m.CoreSearchLazyModule), + loadChildren: () => import('./search-lazy.module'), }, ]; diff --git a/src/core/features/search/services/global-search.ts b/src/core/features/search/services/global-search.ts index ecfd8f5ccf4..36717424232 100644 --- a/src/core/features/search/services/global-search.ts +++ b/src/core/features/search/services/global-search.ts @@ -19,7 +19,7 @@ import { CoreWSExternalWarning } from '@services/ws'; import { CoreCourseListItem, CoreCourses } from '@features/courses/services/courses'; import { CoreUserWithAvatar } from '@components/user-avatar/user-avatar'; import { CoreUser } from '@features/user/services/user'; -import { CoreSite } from '@classes/sites/site'; +import { CoreCacheUpdateFrequency } from '@/core/constants'; declare module '@singletons/events' { @@ -154,7 +154,7 @@ export class CoreSearchGlobalSearchService { const params: CoreSearchGetSearchAreasListWSParams = {}; const { areas } = await site.read('core_search_get_search_areas_list', params, { - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, cacheKey: CoreSearchGlobalSearchService.SEARCH_AREAS_CACHE_KEY, }); diff --git a/src/core/features/settings/pages/about/about.ts b/src/core/features/settings/pages/about/about.ts index 729f820dec0..fb1975ead1a 100644 --- a/src/core/features/settings/pages/about/about.ts +++ b/src/core/features/settings/pages/about/about.ts @@ -25,7 +25,7 @@ import { CoreSite } from '@classes/sites/site'; @Component({ selector: 'page-core-app-settings-about', templateUrl: 'about.html', - styleUrls: ['about.scss'], + styleUrl: 'about.scss', }) export class CoreSettingsAboutPage { diff --git a/src/core/features/settings/pages/dev/dev.ts b/src/core/features/settings/pages/dev/dev.ts index 29123a57d1b..6f781f66801 100644 --- a/src/core/features/settings/pages/dev/dev.ts +++ b/src/core/features/settings/pages/dev/dev.ts @@ -21,7 +21,6 @@ import { ONBOARDING_DONE, } from '@features/login/constants'; import { CoreSettingsHelper } from '@features/settings/services/settings-helper'; -import { CoreSitePlugins } from '@features/siteplugins/services/siteplugins'; import { CoreUserTours } from '@features/usertours/services/user-tours'; import { CoreCacheManager } from '@services/cache-manager'; import { CoreConfig } from '@services/config'; @@ -124,6 +123,7 @@ export class CoreSettingsDevPage implements OnInit { } }); + const { CoreSitePlugins } = await import('@features/siteplugins/services/siteplugins'); this.sitePlugins = CoreSitePlugins.getCurrentSitePluginList().map((plugin) => ({ addon: plugin.addon, component: plugin.component, diff --git a/src/core/features/settings/pages/deviceinfo/deviceinfo.ts b/src/core/features/settings/pages/deviceinfo/deviceinfo.ts index fac179b8910..d4e0e3ecda6 100644 --- a/src/core/features/settings/pages/deviceinfo/deviceinfo.ts +++ b/src/core/features/settings/pages/deviceinfo/deviceinfo.ts @@ -19,7 +19,7 @@ import { Device, Translate, NgZone } from '@singletons'; import { CoreLang } from '@services/lang'; import { CoreFile } from '@services/file'; import { CoreSites } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { Subscription } from 'rxjs'; import { CorePushNotifications } from '@features/pushnotifications/services/pushnotifications'; import { CoreConfig } from '@services/config'; @@ -69,7 +69,7 @@ interface CoreSettingsDeviceInfo { @Component({ selector: 'page-core-app-settings-deviceinfo', templateUrl: 'deviceinfo.html', - styleUrls: ['deviceinfo.scss'], + styleUrl: 'deviceinfo.scss', }) export class CoreSettingsDeviceInfoPage implements OnDestroy { @@ -208,7 +208,7 @@ export class CoreSettingsDeviceInfoPage implements OnDestroy { this.showDevOptions = this.devOptionsForced || showDevOptionsOnConfig == 1; const publicKey = this.deviceInfo.pushId ? - await CoreUtils.ignoreErrors(CorePushNotifications.getPublicKey()) : + await CorePromiseUtils.ignoreErrors(CorePushNotifications.getPublicKey()) : undefined; this.deviceInfo.encryptedPushSupported = publicKey !== undefined; } diff --git a/src/core/features/settings/pages/general/general.ts b/src/core/features/settings/pages/general/general.ts index c19b483a36d..471d6821e0b 100644 --- a/src/core/features/settings/pages/general/general.ts +++ b/src/core/features/settings/pages/general/general.ts @@ -22,7 +22,7 @@ import { CoreSettingsHelper, CoreColorScheme, CoreZoomLevel } from '../../servic import { CoreIframeUtils } from '@services/utils/iframe'; import { Translate } from '@singletons'; import { CoreSites } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { AlertButton } from '@ionic/angular'; import { CoreNavigator } from '@services/navigator'; import { CorePlatform } from '@services/platform'; @@ -35,7 +35,7 @@ import { CoreNative } from '@features/native/services/native'; @Component({ selector: 'page-core-app-settings-general', templateUrl: 'general.html', - styleUrls: ['general.scss'], + styleUrl: 'general.scss', }) export class CoreSettingsGeneralPage { @@ -175,7 +175,7 @@ export class CoreSettingsGeneralPage { protected async applyLanguageAndRestart(): Promise { // Invalidate cache for all sites to get the content in the right language. const sites = await CoreSites.getSitesInstances(); - await CoreUtils.ignoreErrors(Promise.all(sites.map((site) => site.invalidateWsCache()))); + await CorePromiseUtils.ignoreErrors(Promise.all(sites.map((site) => site.invalidateWsCache()))); CoreEvents.trigger(CoreEvents.LANGUAGE_CHANGED, this.selectedLanguage); diff --git a/src/core/features/settings/pages/space-usage/space-usage.ts b/src/core/features/settings/pages/space-usage/space-usage.ts index 48afef1775d..f83368dec74 100644 --- a/src/core/features/settings/pages/space-usage/space-usage.ts +++ b/src/core/features/settings/pages/space-usage/space-usage.ts @@ -15,11 +15,12 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { CoreSiteBasicInfo, CoreSites } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreObject } from '@singletons/object'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreSettingsHelper } from '../../services/settings-helper'; import { CoreAccountsList } from '@features/login/services/login-helper'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Page that displays the space usage settings. @@ -94,7 +95,7 @@ export class CoreSettingsSpaceUsagePage implements OnInit, OnDestroy { // Calculate total usage. let totalSize = 0; - const sites = await CoreUtils.ignoreErrors(CoreSites.getSortedSites(), [] as CoreSiteBasicInfo[]); + const sites = await CorePromiseUtils.ignoreErrors(CoreSites.getSortedSites(), [] as CoreSiteBasicInfo[]); const sitesWithUsage = await Promise.all(sites.map((site) => this.getSiteWithUsage(site))); let siteUrl = ''; @@ -128,7 +129,7 @@ export class CoreSettingsSpaceUsagePage implements OnInit, OnDestroy { } }); - this.accountsList.otherSites = CoreUtils.objectToArray(otherSites); + this.accountsList.otherSites = CoreObject.toArray(otherSites); this.accountsList.count = sites.length; this.totalSpaceUsage = totalSize; diff --git a/src/core/features/settings/services/settings-helper.ts b/src/core/features/settings/services/settings-helper.ts index 0936acacac9..558c2deadd6 100644 --- a/src/core/features/settings/services/settings-helper.ts +++ b/src/core/features/settings/services/settings-helper.ts @@ -20,7 +20,7 @@ import { CoreEvents } from '@singletons/events'; import { CoreFilepool } from '@services/filepool'; import { CoreSite } from '@classes/sites/site'; import { CoreSites } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreConstants } from '@/core/constants'; import { CoreConfig } from '@services/config'; import { CoreFilter } from '@features/filter/services/filter'; @@ -249,7 +249,7 @@ export class CoreSettingsHelperProvider { const syncPromise = Promise.all([ // Invalidate all the site files so they are re-downloaded. - CoreUtils.ignoreErrors(CoreFilepool.invalidateAllFiles(siteId)), + CorePromiseUtils.ignoreErrors(CoreFilepool.invalidateAllFiles(siteId)), // Invalidate and synchronize site data. site.invalidateWsCache(), CoreSites.updateSiteInfo(site.getId()), diff --git a/src/core/features/settings/settings-lazy.module.ts b/src/core/features/settings/settings-lazy.module.ts index 69007a40795..84be212d3b6 100644 --- a/src/core/features/settings/settings-lazy.module.ts +++ b/src/core/features/settings/settings-lazy.module.ts @@ -45,7 +45,7 @@ const sectionRoutes: Routes = [ }, { path: SHAREDFILES_PAGE_NAME, - loadChildren: () => import('@features/sharedfiles/sharedfiles-lazy.module').then(m => m.CoreSharedFilesLazyModule), + loadChildren: () => import('@features/sharedfiles/sharedfiles-lazy.module'), }, { path: 'about', @@ -114,4 +114,4 @@ const routes: Routes = [ CoreSettingsErrorLogPage, ], }) -export class CoreSettingsLazyModule {} +export default class CoreSettingsLazyModule {} diff --git a/src/core/features/settings/settings-site-lazy.module.ts b/src/core/features/settings/settings-site-lazy.module.ts index 82ccd02598b..67d0c4ad1d3 100644 --- a/src/core/features/settings/settings-site-lazy.module.ts +++ b/src/core/features/settings/settings-site-lazy.module.ts @@ -61,4 +61,4 @@ function buildRoutes(injector: Injector): Routes { CoreSharedModule, ], }) -export class CoreettingsSiteLazyModule {} +export default class CoreettingsSiteLazyModule {} diff --git a/src/core/features/settings/settings.module.ts b/src/core/features/settings/settings.module.ts index dd77dbf1bdd..72a98a2d846 100644 --- a/src/core/features/settings/settings.module.ts +++ b/src/core/features/settings/settings.module.ts @@ -37,18 +37,18 @@ export async function getSettingsServices(): Promise[]> { const appRoutes: Routes = [ { path: 'settings', - loadChildren: () => import('./settings-lazy.module').then(m => m.CoreSettingsLazyModule), + loadChildren: () => import('./settings-lazy.module'), }, ]; const mainMenuMoreRoutes: Routes = [ { path: 'settings', - loadChildren: () => import('./settings-lazy.module').then(m => m.CoreSettingsLazyModule), + loadChildren: () => import('./settings-lazy.module'), }, { path: 'preferences', - loadChildren: () => import('./settings-site-lazy.module').then(m => m.CoreettingsSiteLazyModule), + loadChildren: () => import('./settings-site-lazy.module'), }, ]; diff --git a/src/core/features/sharedfiles/components/list-modal/list-modal.ts b/src/core/features/sharedfiles/components/list-modal/list-modal.ts index 6138c50fd04..d113e72af75 100644 --- a/src/core/features/sharedfiles/components/list-modal/list-modal.ts +++ b/src/core/features/sharedfiles/components/list-modal/list-modal.ts @@ -17,7 +17,7 @@ import { toBoolean } from '@/core/transforms/boolean'; import { Component, OnInit, Input } from '@angular/core'; import { FileEntry } from '@awesome-cordova-plugins/file/ngx'; -import { CoreFile } from '@services/file'; +import { CoreFileUtils } from '@singletons/file-utils'; import { ModalController, Translate } from '@singletons'; import { CoreSharedFilesComponentsModule } from '../components.module'; @@ -58,7 +58,7 @@ export class CoreSharedFilesListModalComponent implements OnInit { */ calculateTitle(path?: string): void { if (path) { - this.title = CoreFile.getFileAndDirectoryFromPath(path).name; + this.title = CoreFileUtils.getFileAndDirectoryFromPath(path).name; } else { this.title = Translate.instant('core.sharedfiles.sharedfiles'); } diff --git a/src/core/features/sharedfiles/pages/choose-site/choose-site.ts b/src/core/features/sharedfiles/pages/choose-site/choose-site.ts index 271ce4ada2a..02ff14e507d 100644 --- a/src/core/features/sharedfiles/pages/choose-site/choose-site.ts +++ b/src/core/features/sharedfiles/pages/choose-site/choose-site.ts @@ -20,6 +20,7 @@ import { CoreFile } from '@services/file'; import { CoreNavigator } from '@services/navigator'; import { CoreSiteBasicInfo } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; +import { CoreFileUtils } from '@singletons/file-utils'; /** * Page to display the list of sites to choose one to store a shared file. @@ -57,7 +58,7 @@ export class CoreSharedFilesChooseSitePage implements OnInit { } if (this.filePath) { - const fileAndDir = CoreFile.getFileAndDirectoryFromPath(this.filePath); + const fileAndDir = CoreFileUtils.getFileAndDirectoryFromPath(this.filePath); this.fileName = fileAndDir.name; } diff --git a/src/core/features/sharedfiles/pages/list/list.ts b/src/core/features/sharedfiles/pages/list/list.ts index 2525cd2dbd4..c25f49452e1 100644 --- a/src/core/features/sharedfiles/pages/list/list.ts +++ b/src/core/features/sharedfiles/pages/list/list.ts @@ -14,7 +14,7 @@ import { Component, OnInit } from '@angular/core'; -import { CoreFile } from '@services/file'; +import { CoreFileUtils } from '@singletons/file-utils'; import { CoreNavigator } from '@services/navigator'; import { Translate } from '@singletons'; @@ -54,7 +54,7 @@ export class CoreSharedFilesListPage implements OnInit { */ calculateTitle(path?: string): void { if (path) { - this.title = CoreFile.getFileAndDirectoryFromPath(path).name; + this.title = CoreFileUtils.getFileAndDirectoryFromPath(path).name; } else { this.title = Translate.instant('core.sharedfiles.sharedfiles'); } diff --git a/src/core/features/sharedfiles/services/database/sharedfiles.ts b/src/core/features/sharedfiles/services/database/sharedfiles.ts index 346fae39f3c..6bc106df5b0 100644 --- a/src/core/features/sharedfiles/services/database/sharedfiles.ts +++ b/src/core/features/sharedfiles/services/database/sharedfiles.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { CoreAppSchema } from '@services/app'; +import { CoreAppSchema } from '@services/app-db'; /** * Database variables for CoreSharedFilesProvider service. diff --git a/src/core/features/sharedfiles/services/sharedfiles.ts b/src/core/features/sharedfiles/services/sharedfiles.ts index 166fc611911..1b20acd823c 100644 --- a/src/core/features/sharedfiles/services/sharedfiles.ts +++ b/src/core/features/sharedfiles/services/sharedfiles.ts @@ -17,9 +17,9 @@ import { FileEntry, DirectoryEntry } from '@awesome-cordova-plugins/file/ngx'; import { Md5 } from 'ts-md5/dist/md5'; import { CoreLogger } from '@singletons/logger'; -import { CoreApp } from '@services/app'; +import { CoreAppDB } from '@services/app-db'; import { CoreFile } from '@services/file'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreMimetypeUtils } from '@services/utils/mimetype'; import { CoreSites } from '@services/sites'; import { CoreEvents } from '@singletons/events'; @@ -51,13 +51,9 @@ export class CoreSharedFilesProvider { * @returns Promise resolved when done. */ async initializeDatabase(): Promise { - try { - await CoreApp.createTablesFromSchema(APP_SCHEMA); - } catch (e) { - // Ignore errors. - } + await CoreAppDB.createTablesFromSchema(APP_SCHEMA); - const database = CoreApp.getDB(); + const database = CoreAppDB.getDB(); const sharedFilesTable = new CoreDatabaseTableProxy( { cachingStrategy: CoreDatabaseCachingStrategy.None }, database, @@ -78,7 +74,7 @@ export class CoreSharedFilesProvider { async checkIOSNewFiles(): Promise { this.logger.debug('Search for new files on iOS'); - const entries = await CoreUtils.ignoreErrors(CoreFile.getDirectoryContents('Inbox')); + const entries = await CorePromiseUtils.ignoreErrors(CoreFile.getDirectoryContents('Inbox')); if (!entries || !entries.length) { return; @@ -131,7 +127,7 @@ export class CoreSharedFilesProvider { async deleteInboxFile(entry: FileEntry): Promise { this.logger.debug('Delete inbox file: ' + entry.name); - await CoreUtils.ignoreErrors(CoreFile.removeFileByFileEntry(entry)); + await CorePromiseUtils.ignoreErrors(CoreFile.removeFileByFileEntry(entry)); try { await this.unmarkAsTreated(this.getFileId(entry)); diff --git a/src/core/features/sharedfiles/sharedfiles-lazy.module.ts b/src/core/features/sharedfiles/sharedfiles-lazy.module.ts index 878a53cf9cd..55c890622b0 100644 --- a/src/core/features/sharedfiles/sharedfiles-lazy.module.ts +++ b/src/core/features/sharedfiles/sharedfiles-lazy.module.ts @@ -42,4 +42,4 @@ const routes: Routes = [ CoreSharedFilesChooseSitePage, ], }) -export class CoreSharedFilesLazyModule {} +export default class CoreSharedFilesLazyModule {} diff --git a/src/core/features/sharedfiles/sharedfiles.module.ts b/src/core/features/sharedfiles/sharedfiles.module.ts index 45d715d00fe..26d6562a9ad 100644 --- a/src/core/features/sharedfiles/sharedfiles.module.ts +++ b/src/core/features/sharedfiles/sharedfiles.module.ts @@ -45,7 +45,7 @@ export async function getSharedFilesServices(): Promise[]> { const routes: Routes = [ { path: SHAREDFILES_PAGE_NAME, - loadChildren: () => import('./sharedfiles-lazy.module').then(m => m.CoreSharedFilesLazyModule), + loadChildren: () => import('./sharedfiles-lazy.module'), }, ]; diff --git a/src/core/features/sitehome/pages/index/index.ts b/src/core/features/sitehome/pages/index/index.ts index 96b773d1433..07672dbe900 100644 --- a/src/core/features/sitehome/pages/index/index.ts +++ b/src/core/features/sitehome/pages/index/index.ts @@ -27,7 +27,7 @@ import { CoreCourseModuleDelegate } from '@features/course/services/module-deleg import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate'; import { CoreNavigationOptions, CoreNavigator } from '@services/navigator'; import { CoreBlockHelper } from '@features/block/services/block-helper'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreTime } from '@singletons/time'; import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics'; import { ContextLevel } from '@/core/constants'; @@ -39,7 +39,7 @@ import { CoreModals } from '@services/modals'; @Component({ selector: 'page-core-sitehome-index', templateUrl: 'index.html', - styleUrls: ['index.scss'], + styleUrl: 'index.scss', }) export class CoreSiteHomeIndexPage implements OnInit, OnDestroy { @@ -67,7 +67,7 @@ export class CoreSiteHomeIndexPage implements OnInit, OnDestroy { }, CoreSites.getCurrentSiteId()); this.logView = CoreTime.once(async () => { - await CoreUtils.ignoreErrors(CoreCourse.logView(this.siteHomeId)); + await CorePromiseUtils.ignoreErrors(CoreCourse.logView(this.siteHomeId)); CoreAnalytics.logEvent({ type: CoreAnalyticsEventType.VIEW_ITEM, diff --git a/src/core/features/sitehome/services/sitehome.ts b/src/core/features/sitehome/services/sitehome.ts index 3dcf98555a4..3594f103c9e 100644 --- a/src/core/features/sitehome/services/sitehome.ts +++ b/src/core/features/sitehome/services/sitehome.ts @@ -19,7 +19,7 @@ import { CoreSite } from '@classes/sites/site'; import { makeSingleton } from '@singletons'; import { CoreCourse } from '../../course/services/course'; import { CoreCourses } from '../../courses/services/courses'; -import { AddonModForum, AddonModForumData } from '@addons/mod/forum/services/forum'; +import { AddonModForumData } from '@addons/mod/forum/services/forum'; import { CoreError } from '@classes/errors/error'; import { CoreBlockHelper } from '@features/block/services/block-helper'; import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site'; @@ -53,6 +53,8 @@ export class CoreSiteHomeProvider { siteHomeId = CoreSites.getCurrentSiteHomeId(); } + const { AddonModForum } = await import('@addons/mod/forum/services/forum'); + const forums = await AddonModForum.getCourseForums(siteHomeId); const forum = forums.find((forum) => forum.type == 'news'); @@ -67,9 +69,10 @@ export class CoreSiteHomeProvider { * Invalidate the WS call to get the news forum for the Site Home. * * @param siteHomeId Site Home ID. - * @returns Promise resolved when invalidated. */ async invalidateNewsForum(siteHomeId: number): Promise { + const { AddonModForum } = await import('@addons/mod/forum/services/forum'); + await AddonModForum.invalidateForumData(siteHomeId); } diff --git a/src/core/features/sitehome/sitehome-lazy.module.ts b/src/core/features/sitehome/sitehome-lazy.module.ts index ce14d218e15..7a0d4a38d98 100644 --- a/src/core/features/sitehome/sitehome-lazy.module.ts +++ b/src/core/features/sitehome/sitehome-lazy.module.ts @@ -38,4 +38,4 @@ const routes: Routes = [ CoreSiteHomeIndexPage, ], }) -export class CoreSiteHomeLazyModule {} +export default class CoreSiteHomeLazyModule {} diff --git a/src/core/features/sitehome/sitehome.module.ts b/src/core/features/sitehome/sitehome.module.ts index c968d462584..fc597d53374 100644 --- a/src/core/features/sitehome/sitehome.module.ts +++ b/src/core/features/sitehome/sitehome.module.ts @@ -37,7 +37,7 @@ export async function getSiteHomeServices(): Promise[]> { const mainMenuHomeRoutes: Routes = [ { path: CoreSiteHomeHomeHandlerService.PAGE_NAME, - loadChildren: () => import('./sitehome-lazy.module').then(m => m.CoreSiteHomeLazyModule), + loadChildren: () => import('./sitehome-lazy.module'), }, ]; diff --git a/src/core/features/siteplugins/classes/compile-init-component.ts b/src/core/features/siteplugins/classes/compile-init-component.ts index 6fe45e7770e..8949f015ca1 100644 --- a/src/core/features/siteplugins/classes/compile-init-component.ts +++ b/src/core/features/siteplugins/classes/compile-init-component.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { CoreUtils } from '@services/utils/utils'; +import { CoreObject } from '@singletons/object'; import { CoreSitePlugins, CoreSitePluginsInitHandlerData } from '../services/siteplugins'; /** @@ -55,7 +55,7 @@ export class CoreSitePluginsCompileInitComponent { // Load first template. if (this.handlerSchema.methodTemplates?.length) { this.content = this.handlerSchema.methodTemplates[0].html; - this.jsData.CONTENT_TEMPLATES = CoreUtils.objectToKeyValueMap( + this.jsData.CONTENT_TEMPLATES = CoreObject.toKeyValueMap( this.handlerSchema.methodTemplates, 'id', 'html', diff --git a/src/core/features/siteplugins/classes/handlers/assign-feedback-handler.ts b/src/core/features/siteplugins/classes/handlers/assign-feedback-handler.ts index ade01a6ee49..10c9b32257b 100644 --- a/src/core/features/siteplugins/classes/handlers/assign-feedback-handler.ts +++ b/src/core/features/siteplugins/classes/handlers/assign-feedback-handler.ts @@ -16,7 +16,6 @@ import { Type } from '@angular/core'; import { AddonModAssignDefaultFeedbackHandler } from '@addons/mod/assign/services/handlers/default-feedback'; import { AddonModAssignPlugin } from '@addons/mod/assign/services/assign'; -import { CoreSitePluginsAssignFeedbackComponent } from '@features/siteplugins/components/assign-feedback/assign-feedback'; import { Translate } from '@singletons'; import type{ IAddonModAssignFeedbackPluginComponent } from '@addons/mod/assign/classes/base-feedback-plugin-component'; @@ -32,7 +31,10 @@ export class CoreSitePluginsAssignFeedbackHandler extends AddonModAssignDefaultF /** * @inheritdoc */ - getComponent(): Type | undefined { + async getComponent(): Promise> { + const { CoreSitePluginsAssignFeedbackComponent } = + await import('@features/siteplugins/components/assign-feedback/assign-feedback'); + return CoreSitePluginsAssignFeedbackComponent; } diff --git a/src/core/features/siteplugins/classes/handlers/assign-submission-handler.ts b/src/core/features/siteplugins/classes/handlers/assign-submission-handler.ts index cf7f46b4c2d..84591e43fc9 100644 --- a/src/core/features/siteplugins/classes/handlers/assign-submission-handler.ts +++ b/src/core/features/siteplugins/classes/handlers/assign-submission-handler.ts @@ -17,7 +17,6 @@ import { Type } from '@angular/core'; import { AddonModAssignPlugin } from '@addons/mod/assign/services/assign'; import { AddonModAssignDefaultSubmissionHandler } from '@addons/mod/assign/services/handlers/default-submission'; import { Translate } from '@singletons'; -import { CoreSitePluginsAssignSubmissionComponent } from '../../components/assign-submission/assign-submission'; import type { AddonModAssignSubmissionPluginBaseComponent } from '@addons/mod/assign/classes/base-submission-plugin-component'; /** @@ -32,7 +31,9 @@ export class CoreSitePluginsAssignSubmissionHandler extends AddonModAssignDefaul /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { + const { CoreSitePluginsAssignSubmissionComponent } = await import('../../components/assign-submission/assign-submission'); + return CoreSitePluginsAssignSubmissionComponent; } diff --git a/src/core/features/siteplugins/classes/handlers/module-handler.ts b/src/core/features/siteplugins/classes/handlers/module-handler.ts index a7fc626d442..0eba196bbc8 100644 --- a/src/core/features/siteplugins/classes/handlers/module-handler.ts +++ b/src/core/features/siteplugins/classes/handlers/module-handler.ts @@ -28,7 +28,7 @@ import { CoreNavigationOptions, CoreNavigator } from '@services/navigator'; import { CoreLogger } from '@singletons/logger'; import { CoreSitePluginsBaseHandler } from './base-handler'; import { CoreEvents } from '@singletons/events'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CORE_SITE_PLUGINS_UPDATE_COURSE_CONTENT } from '@features/siteplugins/constants'; /** @@ -184,7 +184,7 @@ export class CoreSitePluginsModuleHandler extends CoreSitePluginsBaseHandler imp }; if (refresh) { - await CoreUtils.ignoreErrors(CoreSitePlugins.invalidateContent(this.plugin.component, method, args)); + await CorePromiseUtils.ignoreErrors(CoreSitePlugins.invalidateContent(this.plugin.component, method, args)); } try { diff --git a/src/core/features/siteplugins/classes/handlers/module-prefetch-handler.ts b/src/core/features/siteplugins/classes/handlers/module-prefetch-handler.ts index 91dde49eea6..cf422c31eae 100644 --- a/src/core/features/siteplugins/classes/handlers/module-prefetch-handler.ts +++ b/src/core/features/siteplugins/classes/handlers/module-prefetch-handler.ts @@ -18,7 +18,7 @@ import { CoreSitePlugins, CoreSitePluginsCourseModuleHandlerData } from '@featur import { CoreFilepool } from '@services/filepool'; import { CoreFileSizeSum } from '@services/plugin-file-delegate'; import { CoreSites } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Handler to prefetch a module site plugin. @@ -200,7 +200,7 @@ export class CoreSitePluginsModulePrefetchHandler extends CoreCourseActivityPref } } - return CoreUtils.allPromises(promises); + return CorePromiseUtils.allPromises(promises); } /** diff --git a/src/core/features/siteplugins/classes/handlers/question-handler.ts b/src/core/features/siteplugins/classes/handlers/question-handler.ts index 79c8d9dc407..9d2866d70c0 100644 --- a/src/core/features/siteplugins/classes/handlers/question-handler.ts +++ b/src/core/features/siteplugins/classes/handlers/question-handler.ts @@ -15,7 +15,6 @@ import { Type } from '@angular/core'; import { CoreQuestionBaseHandler } from '@features/question/classes/base-question-handler'; -import { CoreSitePluginsQuestionComponent } from '@features/siteplugins/components/question/question'; /** * Handler to display a question site plugin. @@ -29,7 +28,9 @@ export class CoreSitePluginsQuestionHandler extends CoreQuestionBaseHandler { /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { + const { CoreSitePluginsQuestionComponent } = await import('@features/siteplugins/components/question/question'); + return CoreSitePluginsQuestionComponent; } diff --git a/src/core/features/siteplugins/classes/handlers/user-profile-field-handler.ts b/src/core/features/siteplugins/classes/handlers/user-profile-field-handler.ts index d8a8c897dd1..a201d4ad83a 100644 --- a/src/core/features/siteplugins/classes/handlers/user-profile-field-handler.ts +++ b/src/core/features/siteplugins/classes/handlers/user-profile-field-handler.ts @@ -15,7 +15,6 @@ import { Type } from '@angular/core'; import { AuthEmailSignupProfileField } from '@features/login/services/login-helper'; -import { CoreSitePluginsUserProfileFieldComponent } from '@features/siteplugins/components/user-profile-field/user-profile-field'; import { CoreUserProfileField } from '@features/user/services/user'; import { CoreUserProfileFieldHandler, CoreUserProfileFieldHandlerData } from '@features/user/services/user-profile-field-delegate'; import { CoreFormFields } from '@singletons/form'; @@ -33,7 +32,10 @@ export class CoreSitePluginsUserProfileFieldHandler extends CoreSitePluginsBaseH /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { + const { CoreSitePluginsUserProfileFieldComponent } = + await import('@features/siteplugins/components/user-profile-field/user-profile-field'); + return CoreSitePluginsUserProfileFieldComponent; } diff --git a/src/core/features/siteplugins/classes/handlers/workshop-assessment-strategy-handler.ts b/src/core/features/siteplugins/classes/handlers/workshop-assessment-strategy-handler.ts index f78b2f60b1e..880f547f4ed 100644 --- a/src/core/features/siteplugins/classes/handlers/workshop-assessment-strategy-handler.ts +++ b/src/core/features/siteplugins/classes/handlers/workshop-assessment-strategy-handler.ts @@ -15,9 +15,6 @@ import { AddonWorkshopAssessmentStrategyHandler } from '@addons/mod/workshop/services/assessment-strategy-delegate'; import { AddonModWorkshopGetAssessmentFormFieldsParsedData } from '@addons/mod/workshop/services/workshop'; import { Type } from '@angular/core'; -import { - CoreSitePluginsWorkshopAssessmentStrategyComponent, -} from '@features/siteplugins/components/workshop-assessment-strategy/workshop-assessment-strategy'; import { CoreFormFields } from '@singletons/form'; import { CoreSitePluginsBaseHandler } from './base-handler'; @@ -35,7 +32,10 @@ export class CoreSitePluginsWorkshopAssessmentStrategyHandler /** * @inheritdoc */ - getComponent(): Type { + async getComponent(): Promise> { + const { CoreSitePluginsWorkshopAssessmentStrategyComponent } = + await import('@features/siteplugins/components/workshop-assessment-strategy/workshop-assessment-strategy'); + return CoreSitePluginsWorkshopAssessmentStrategyComponent; } diff --git a/src/core/features/siteplugins/components/assign-feedback/assign-feedback.ts b/src/core/features/siteplugins/components/assign-feedback/assign-feedback.ts index 26050d2d632..1780c3c122c 100644 --- a/src/core/features/siteplugins/components/assign-feedback/assign-feedback.ts +++ b/src/core/features/siteplugins/components/assign-feedback/assign-feedback.ts @@ -18,6 +18,8 @@ import { AddonModAssignAssign, AddonModAssignPlugin, AddonModAssignSubmission } import { AddonModAssignFeedbackDelegate } from '@addons/mod/assign/services/feedback-delegate'; import { CoreSitePluginsCompileInitComponent } from '@features/siteplugins/classes/compile-init-component'; import { toBoolean } from '@/core/transforms/boolean'; +import { CoreCompileHtmlComponentModule } from '@features/compile/components/compile-html/compile-html.module'; +import { CoreSharedModule } from '@/core/shared.module'; /** * Component that displays an assign feedback plugin created using a site plugin. @@ -26,6 +28,11 @@ import { toBoolean } from '@/core/transforms/boolean'; selector: 'core-site-plugins-assign-feedback', templateUrl: 'core-siteplugins-assign-feedback.html', styles: [':host { display: contents; }'], + standalone: true, + imports: [ + CoreSharedModule, + CoreCompileHtmlComponentModule, + ], }) export class CoreSitePluginsAssignFeedbackComponent extends CoreSitePluginsCompileInitComponent implements OnInit { diff --git a/src/core/features/siteplugins/components/assign-submission/assign-submission.ts b/src/core/features/siteplugins/components/assign-submission/assign-submission.ts index 6fdf3d7bec9..f6780ab71af 100644 --- a/src/core/features/siteplugins/components/assign-submission/assign-submission.ts +++ b/src/core/features/siteplugins/components/assign-submission/assign-submission.ts @@ -18,6 +18,8 @@ import { AddonModAssignAssign, AddonModAssignPlugin, AddonModAssignSubmission } import { AddonModAssignSubmissionDelegate } from '@addons/mod/assign/services/submission-delegate'; import { CoreSitePluginsCompileInitComponent } from '@features/siteplugins/classes/compile-init-component'; import { toBoolean } from '@/core/transforms/boolean'; +import { CoreCompileHtmlComponentModule } from '@features/compile/components/compile-html/compile-html.module'; +import { CoreSharedModule } from '@/core/shared.module'; /** * Component that displays an assign submission plugin created using a site plugin. @@ -26,6 +28,11 @@ import { toBoolean } from '@/core/transforms/boolean'; selector: 'core-site-plugins-assign-submission', templateUrl: 'core-siteplugins-assign-submission.html', styles: [':host { display: contents; }'], + standalone: true, + imports: [ + CoreSharedModule, + CoreCompileHtmlComponentModule, + ], }) export class CoreSitePluginsAssignSubmissionComponent extends CoreSitePluginsCompileInitComponent implements OnInit { diff --git a/src/core/features/siteplugins/components/block/block.ts b/src/core/features/siteplugins/components/block/block.ts index 624924ec104..70f703550b0 100644 --- a/src/core/features/siteplugins/components/block/block.ts +++ b/src/core/features/siteplugins/components/block/block.ts @@ -18,6 +18,8 @@ import { CoreBlockBaseComponent } from '@features/block/classes/base-block-compo import { CoreBlockDelegate } from '@features/block/services/block-delegate'; import { CoreSitePlugins, CoreSitePluginsContent } from '@features/siteplugins/services/siteplugins'; import { CoreSitePluginsPluginContentComponent } from '../plugin-content/plugin-content'; +import { CoreCompileHtmlComponentModule } from '@features/compile/components/compile-html/compile-html.module'; +import { CoreSharedModule } from '@/core/shared.module'; /** * Component that displays the index of a course format site plugin. @@ -26,6 +28,12 @@ import { CoreSitePluginsPluginContentComponent } from '../plugin-content/plugin- selector: 'core-site-plugins-block', templateUrl: 'core-siteplugins-block.html', styles: [':host { display: contents; }'], + standalone: true, + imports: [ + CoreSharedModule, + CoreCompileHtmlComponentModule, + CoreSitePluginsPluginContentComponent, + ], }) export class CoreSitePluginsBlockComponent extends CoreBlockBaseComponent implements OnChanges { diff --git a/src/core/features/siteplugins/components/components.module.ts b/src/core/features/siteplugins/components/components.module.ts index b9482e300f4..d78b787ce57 100644 --- a/src/core/features/siteplugins/components/components.module.ts +++ b/src/core/features/siteplugins/components/components.module.ts @@ -16,53 +16,13 @@ import { NgModule } from '@angular/core'; import { CoreSharedModule } from '@/core/shared.module'; import { CoreCompileHtmlComponentModule } from '@features/compile/components/compile-html/compile-html.module'; -import { CoreSitePluginsPluginContentComponent } from './plugin-content/plugin-content'; -import { CoreSitePluginsModuleIndexComponent } from './module-index/module-index'; -import { CoreSitePluginsCourseFormatComponent } from './course-format/course-format'; -import { CoreSitePluginsUserProfileFieldComponent } from './user-profile-field/user-profile-field'; -import { CoreSitePluginsQuestionComponent } from './question/question'; -import { CoreSitePluginsQuestionBehaviourComponent } from './question-behaviour/question-behaviour'; -import { CoreSitePluginsQuizAccessRuleComponent } from './quiz-access-rule/quiz-access-rule'; -import { CoreSitePluginsAssignFeedbackComponent } from './assign-feedback/assign-feedback'; -import { CoreSitePluginsAssignSubmissionComponent } from './assign-submission/assign-submission'; -import { CoreSitePluginsWorkshopAssessmentStrategyComponent } from './workshop-assessment-strategy/workshop-assessment-strategy'; -import { CoreSitePluginsBlockComponent } from './block/block'; -import { CoreSitePluginsOnlyTitleBlockComponent } from './only-title-block/only-title-block'; import { CoreCourseComponentsModule } from '@features/course/components/components.module'; @NgModule({ - declarations: [ - CoreSitePluginsPluginContentComponent, - CoreSitePluginsModuleIndexComponent, - CoreSitePluginsBlockComponent, - CoreSitePluginsOnlyTitleBlockComponent, - CoreSitePluginsCourseFormatComponent, - CoreSitePluginsUserProfileFieldComponent, - CoreSitePluginsQuestionComponent, - CoreSitePluginsQuestionBehaviourComponent, - CoreSitePluginsQuizAccessRuleComponent, - CoreSitePluginsAssignFeedbackComponent, - CoreSitePluginsAssignSubmissionComponent, - CoreSitePluginsWorkshopAssessmentStrategyComponent, - ], imports: [ CoreSharedModule, CoreCompileHtmlComponentModule, CoreCourseComponentsModule, ], - exports: [ - CoreSitePluginsPluginContentComponent, - CoreSitePluginsModuleIndexComponent, - CoreSitePluginsBlockComponent, - CoreSitePluginsOnlyTitleBlockComponent, - CoreSitePluginsCourseFormatComponent, - CoreSitePluginsUserProfileFieldComponent, - CoreSitePluginsQuestionComponent, - CoreSitePluginsQuestionBehaviourComponent, - CoreSitePluginsQuizAccessRuleComponent, - CoreSitePluginsAssignFeedbackComponent, - CoreSitePluginsAssignSubmissionComponent, - CoreSitePluginsWorkshopAssessmentStrategyComponent, - ], }) export class CoreSitePluginsComponentsModule {} diff --git a/src/core/features/siteplugins/components/course-format/course-format.ts b/src/core/features/siteplugins/components/course-format/course-format.ts index 26e2de463cb..ac3e78a03e4 100644 --- a/src/core/features/siteplugins/components/course-format/course-format.ts +++ b/src/core/features/siteplugins/components/course-format/course-format.ts @@ -20,6 +20,8 @@ import { CoreCourseFormatDelegate } from '@features/course/services/format-deleg import { CoreCourseAnyCourseData } from '@features/courses/services/courses'; import { CoreSitePlugins, CoreSitePluginsContent } from '@features/siteplugins/services/siteplugins'; import { CoreSitePluginsPluginContentComponent } from '../plugin-content/plugin-content'; +import { CoreCompileHtmlComponentModule } from '@features/compile/components/compile-html/compile-html.module'; +import { CoreSharedModule } from '@/core/shared.module'; /** * Component that displays the index of a course format site plugin. @@ -28,6 +30,12 @@ import { CoreSitePluginsPluginContentComponent } from '../plugin-content/plugin- selector: 'core-site-plugins-course-format', templateUrl: 'core-siteplugins-course-format.html', styles: [':host { display: contents; }'], + standalone: true, + imports: [ + CoreSharedModule, + CoreCompileHtmlComponentModule, + CoreSitePluginsPluginContentComponent, + ], }) export class CoreSitePluginsCourseFormatComponent implements OnChanges { diff --git a/src/core/features/siteplugins/components/module-index/module-index.ts b/src/core/features/siteplugins/components/module-index/module-index.ts index 8e870403051..4ffd03c4132 100644 --- a/src/core/features/siteplugins/components/module-index/module-index.ts +++ b/src/core/features/siteplugins/components/module-index/module-index.ts @@ -29,8 +29,11 @@ import { CoreSitePluginsCourseModuleHandlerData, } from '@features/siteplugins/services/siteplugins'; import { CoreModals } from '@services/modals'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { CoreSitePluginsPluginContentComponent, CoreSitePluginsPluginContentLoadedData } from '../plugin-content/plugin-content'; +import { CoreCompileHtmlComponentModule } from '@features/compile/components/compile-html/compile-html.module'; +import { CoreCourseComponentsModule } from '@features/course/components/components.module'; +import { CoreSharedModule } from '@/core/shared.module'; /** * Component that displays the index of a module site plugin. @@ -39,6 +42,13 @@ import { CoreSitePluginsPluginContentComponent, CoreSitePluginsPluginContentLoad selector: 'core-site-plugins-module-index', templateUrl: 'core-siteplugins-module-index.html', styles: [':host { display: contents; }'], + standalone: true, + imports: [ + CoreSharedModule, + CoreCompileHtmlComponentModule, + CoreCourseComponentsModule, + CoreSitePluginsPluginContentComponent, + ], }) export class CoreSitePluginsModuleIndexComponent implements OnInit, OnDestroy, CoreCourseModuleMainComponent { diff --git a/src/core/features/siteplugins/components/only-title-block/only-title-block.ts b/src/core/features/siteplugins/components/only-title-block/only-title-block.ts index 34a6ce1dcdb..58019088045 100644 --- a/src/core/features/siteplugins/components/only-title-block/only-title-block.ts +++ b/src/core/features/siteplugins/components/only-title-block/only-title-block.ts @@ -19,6 +19,7 @@ import { CoreBlockBaseComponent } from '@features/block/classes/base-block-compo import { CoreBlockDelegate } from '@features/block/services/block-delegate'; import { CoreSitePlugins, CoreSitePluginsUserHandlerData } from '@features/siteplugins/services/siteplugins'; import { CoreNavigator } from '@services/navigator'; +import { CoreSharedModule } from '@/core/shared.module'; /** * Component to render blocks with only a title and link. @@ -27,6 +28,8 @@ import { CoreNavigator } from '@services/navigator'; selector: 'core-siteplugins-only-title-block', templateUrl: 'core-siteplugins-only-title-block.html', styles: [':host { display: contents; }'], + standalone: true, + imports: [CoreSharedModule], }) export class CoreSitePluginsOnlyTitleBlockComponent extends CoreBlockBaseComponent implements OnInit { diff --git a/src/core/features/siteplugins/components/plugin-content/plugin-content.ts b/src/core/features/siteplugins/components/plugin-content/plugin-content.ts index 6a501b8d80c..3081f82e8ca 100644 --- a/src/core/features/siteplugins/components/plugin-content/plugin-content.ts +++ b/src/core/features/siteplugins/components/plugin-content/plugin-content.ts @@ -35,6 +35,8 @@ import { CoreDomUtils } from '@services/utils/dom'; import { CoreEvents } from '@singletons/events'; import { CoreSites, CoreSitesReadingStrategy } from '@services/sites'; import { CORE_SITE_PLUGINS_UPDATE_COURSE_CONTENT } from '@features/siteplugins/constants'; +import { CoreCompileHtmlComponentModule } from '@features/compile/components/compile-html/compile-html.module'; +import { CoreSharedModule } from '@/core/shared.module'; /** * Component to render a site plugin content. @@ -43,6 +45,11 @@ import { CORE_SITE_PLUGINS_UPDATE_COURSE_CONTENT } from '@features/siteplugins/c selector: 'core-site-plugins-plugin-content', templateUrl: 'core-siteplugins-plugin-content.html', styles: [':host { display: contents; }'], + standalone: true, + imports: [ + CoreSharedModule, + CoreCompileHtmlComponentModule, + ], }) export class CoreSitePluginsPluginContentComponent implements OnInit, DoCheck { diff --git a/src/core/features/siteplugins/components/question-behaviour/question-behaviour.ts b/src/core/features/siteplugins/components/question-behaviour/question-behaviour.ts index 382051be7b2..206ec157400 100644 --- a/src/core/features/siteplugins/components/question-behaviour/question-behaviour.ts +++ b/src/core/features/siteplugins/components/question-behaviour/question-behaviour.ts @@ -13,8 +13,10 @@ // limitations under the License. import { ContextLevel } from '@/core/constants'; +import { CoreSharedModule } from '@/core/shared.module'; import { toBoolean } from '@/core/transforms/boolean'; import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; +import { CoreCompileHtmlComponentModule } from '@features/compile/components/compile-html/compile-html.module'; import { CoreQuestionBehaviourDelegate } from '@features/question/services/behaviour-delegate'; import { CoreQuestionBehaviourButton, CoreQuestionQuestion } from '@features/question/services/question-helper'; @@ -27,6 +29,11 @@ import { CoreSitePluginsCompileInitComponent } from '@features/siteplugins/class selector: 'core-site-plugins-question-behaviour', templateUrl: 'core-siteplugins-question-behaviour.html', styles: [':host { display: contents; }'], + standalone: true, + imports: [ + CoreSharedModule, + CoreCompileHtmlComponentModule, + ], }) export class CoreSitePluginsQuestionBehaviourComponent extends CoreSitePluginsCompileInitComponent implements OnInit { diff --git a/src/core/features/siteplugins/components/question/question.ts b/src/core/features/siteplugins/components/question/question.ts index 686080ced83..45d0249f183 100644 --- a/src/core/features/siteplugins/components/question/question.ts +++ b/src/core/features/siteplugins/components/question/question.ts @@ -13,8 +13,10 @@ // limitations under the License. import { ContextLevel } from '@/core/constants'; +import { CoreSharedModule } from '@/core/shared.module'; import { toBoolean } from '@/core/transforms/boolean'; import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; +import { CoreCompileHtmlComponentModule } from '@features/compile/components/compile-html/compile-html.module'; import { AddonModQuizQuestion } from '@features/question/classes/base-question-component'; import { CoreQuestionDelegate } from '@features/question/services/question-delegate'; @@ -28,6 +30,11 @@ import { CoreSitePluginsCompileInitComponent } from '@features/siteplugins/class selector: 'core-site-plugins-question', templateUrl: 'core-siteplugins-question.html', styles: [':host { display: contents; }'], + standalone: true, + imports: [ + CoreSharedModule, + CoreCompileHtmlComponentModule, + ], }) export class CoreSitePluginsQuestionComponent extends CoreSitePluginsCompileInitComponent implements OnInit { diff --git a/src/core/features/siteplugins/components/quiz-access-rule/quiz-access-rule.ts b/src/core/features/siteplugins/components/quiz-access-rule/quiz-access-rule.ts index a030e888c60..5f0515b784c 100644 --- a/src/core/features/siteplugins/components/quiz-access-rule/quiz-access-rule.ts +++ b/src/core/features/siteplugins/components/quiz-access-rule/quiz-access-rule.ts @@ -19,6 +19,8 @@ import { AddonModQuizAccessRuleDelegate } from '@addons/mod/quiz/services/access import { AddonModQuizAttemptWSData, AddonModQuizQuizWSData } from '@addons/mod/quiz/services/quiz'; import { CoreSitePluginsCompileInitComponent } from '@features/siteplugins/classes/compile-init-component'; import { toBoolean } from '@/core/transforms/boolean'; +import { CoreCompileHtmlComponentModule } from '@features/compile/components/compile-html/compile-html.module'; +import { CoreSharedModule } from '@/core/shared.module'; /** * Component that displays a quiz access rule created using a site plugin. @@ -27,6 +29,11 @@ import { toBoolean } from '@/core/transforms/boolean'; selector: 'core-site-plugins-quiz-access-rule', templateUrl: 'core-siteplugins-quiz-access-rule.html', styles: [':host { display: contents; }'], + standalone: true, + imports: [ + CoreSharedModule, + CoreCompileHtmlComponentModule, + ], }) export class CoreSitePluginsQuizAccessRuleComponent extends CoreSitePluginsCompileInitComponent implements OnInit { diff --git a/src/core/features/siteplugins/components/user-profile-field/user-profile-field.ts b/src/core/features/siteplugins/components/user-profile-field/user-profile-field.ts index 17a804768a7..d9366fd7a44 100644 --- a/src/core/features/siteplugins/components/user-profile-field/user-profile-field.ts +++ b/src/core/features/siteplugins/components/user-profile-field/user-profile-field.ts @@ -13,9 +13,11 @@ // limitations under the License. import { ContextLevel } from '@/core/constants'; +import { CoreSharedModule } from '@/core/shared.module'; import { toBoolean } from '@/core/transforms/boolean'; import { Component, OnInit, Input } from '@angular/core'; import { FormGroup } from '@angular/forms'; +import { CoreCompileHtmlComponentModule } from '@features/compile/components/compile-html/compile-html.module'; import { AuthEmailSignupProfileField } from '@features/login/services/login-helper'; import { CoreSitePluginsCompileInitComponent } from '@features/siteplugins/classes/compile-init-component'; @@ -29,6 +31,11 @@ import { CoreUserProfileFieldDelegate } from '@features/user/services/user-profi selector: 'core-site-plugins-user-profile-field', templateUrl: 'core-siteplugins-user-profile-field.html', styles: [':host { display: contents; }'], + standalone: true, + imports: [ + CoreSharedModule, + CoreCompileHtmlComponentModule, + ], }) export class CoreSitePluginsUserProfileFieldComponent extends CoreSitePluginsCompileInitComponent implements OnInit { diff --git a/src/core/features/siteplugins/components/workshop-assessment-strategy/workshop-assessment-strategy.ts b/src/core/features/siteplugins/components/workshop-assessment-strategy/workshop-assessment-strategy.ts index db0fc69495e..10bf13cf736 100644 --- a/src/core/features/siteplugins/components/workshop-assessment-strategy/workshop-assessment-strategy.ts +++ b/src/core/features/siteplugins/components/workshop-assessment-strategy/workshop-assessment-strategy.ts @@ -12,11 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. +import { CoreSharedModule } from '@/core/shared.module'; import { toBoolean } from '@/core/transforms/boolean'; import { AddonWorkshopAssessmentStrategyDelegate } from '@addons/mod/workshop/services/assessment-strategy-delegate'; import { AddonModWorkshopGetAssessmentFormFieldsParsedData } from '@addons/mod/workshop/services/workshop'; import { AddonModWorkshopSubmissionAssessmentWithFormData } from '@addons/mod/workshop/services/workshop-helper'; import { Component, OnInit, Input } from '@angular/core'; +import { CoreCompileHtmlComponentModule } from '@features/compile/components/compile-html/compile-html.module'; import { CoreSitePluginsCompileInitComponent } from '@features/siteplugins/classes/compile-init-component'; /** @@ -26,6 +28,11 @@ import { CoreSitePluginsCompileInitComponent } from '@features/siteplugins/class selector: 'core-siteplugins-workshop-assessment-strategy', templateUrl: 'core-siteplugins-workshop-assessment-strategy.html', styles: [':host { display: contents; }'], + standalone: true, + imports: [ + CoreSharedModule, + CoreCompileHtmlComponentModule, + ], }) export class CoreSitePluginsWorkshopAssessmentStrategyComponent extends CoreSitePluginsCompileInitComponent implements OnInit { diff --git a/src/core/features/siteplugins/pages/course-option/core-siteplugins-course-option.html b/src/core/features/siteplugins/pages/course-option/core-siteplugins-course-option.html index d34f71c555f..55f94365432 100644 --- a/src/core/features/siteplugins/pages/course-option/core-siteplugins-course-option.html +++ b/src/core/features/siteplugins/pages/course-option/core-siteplugins-course-option.html @@ -1,6 +1,5 @@ - + { try { - const plugins = await CoreUtils.ignoreErrors(CoreSitePlugins.getPlugins(data.siteId)); + const plugins = await CorePromiseUtils.ignoreErrors(CoreSitePlugins.getPlugins(data.siteId)); // Plugins fetched, check that site hasn't changed. if (data.siteId !== CoreSites.getCurrentSiteId() || !plugins?.length) { @@ -144,7 +140,7 @@ export class CoreSitePluginsInitService { }); // Re-load plugins restricted for courses when the list of user courses changes. - CoreEvents.on(CoreCoursesProvider.EVENT_MY_COURSES_CHANGED, (data) => { + CoreEvents.on(CORE_COURSES_MY_COURSES_CHANGED_EVENT, (data) => { if (data.siteId && data.siteId === CoreSites.getCurrentSiteId() && data.added.length) { this.reloadCourseRestrictHandlers(); } @@ -183,14 +179,14 @@ export class CoreSitePluginsInitService { const componentId = uniqueName + '#main'; // Remove the CSS files for this handler that aren't used anymore. Don't block the call for this. - const files = await CoreUtils.ignoreErrors( + const files = await CorePromiseUtils.ignoreErrors( CoreFilepool.getFilesByComponent(site.getId(), CORE_SITE_PLUGINS_COMPONENT, componentId), ); files?.forEach((file) => { if (file.url !== url) { // It's not the current file, delete it. - CoreUtils.ignoreErrors(CoreFilepool.removeFileByUrl(site.getId(), file.url)); + CorePromiseUtils.ignoreErrors(CoreFilepool.removeFileByUrl(site.getId(), file.url)); } }); @@ -367,7 +363,7 @@ export class CoreSitePluginsInitService { if (plugin.parsedHandlers) { // Register all the handlers. const parsedHandlers = plugin.parsedHandlers; - await CoreUtils.allPromises(Object.keys(parsedHandlers).map(async (name) => { + await CorePromiseUtils.allPromises(Object.keys(parsedHandlers).map(async (name) => { await this.registerHandler(plugin, name, parsedHandlers[name]); })); } @@ -381,7 +377,7 @@ export class CoreSitePluginsInitService { protected async loadSitePlugins(plugins: CoreSitePluginsPlugin[]): Promise { this.courseRestrictHandlers = {}; - await CoreUtils.allPromises(plugins.map(async (plugin) => { + await CorePromiseUtils.allPromises(plugins.map(async (plugin) => { const pluginPromise = this.loadSitePlugin(plugin); CoreSitePlugins.registerSitePluginPromise(plugin.component, pluginPromise); @@ -433,7 +429,7 @@ export class CoreSitePluginsInitService { } // Styles have been loaded, now treat the CSS. - CoreUtils.ignoreErrors( + CorePromiseUtils.ignoreErrors( CoreFilepool.treatCSSCode(siteId, fileUrl, cssCode, CORE_SITE_PLUGINS_COMPONENT, uniqueName, version), ); } diff --git a/src/core/features/siteplugins/services/siteplugins.ts b/src/core/features/siteplugins/services/siteplugins.ts index 0a6fb8e4be3..73c7f77bb8c 100644 --- a/src/core/features/siteplugins/services/siteplugins.ts +++ b/src/core/features/siteplugins/services/siteplugins.ts @@ -14,16 +14,15 @@ import { Injectable } from '@angular/core'; -import { CoreConstants } from '@/core/constants'; +import { CoreCacheUpdateFrequency, CoreConstants } from '@/core/constants'; import { CoreSite } from '@classes/sites/site'; import { CoreCourseAnyModuleData } from '@features/course/services/course'; import { CoreCourses } from '@features/courses/services/courses'; -import { CoreApp } from '@services/app'; import { CoreFilepool } from '@services/filepool'; import { CoreLang, CoreLangFormat } from '@services/lang'; import { CoreSites } from '@services/sites'; import { CoreText } from '@singletons/text'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { CoreWSExternalFile, CoreWSExternalWarning } from '@services/ws'; import { makeSingleton } from '@singletons'; import { CoreEvents } from '@singletons/events'; @@ -35,6 +34,7 @@ import { CoreEnrolAction, CoreEnrolInfoIcon } from '@features/enrol/services/enr import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site'; import { CoreUserProfileHandlerType } from '@features/user/services/user-delegate'; import { CORE_SITE_PLUGINS_COMPONENT, CORE_SITE_PLUGINS_UPDATE_COURSE_CONTENT } from '../constants'; +import { CoreObject } from '@singletons/object'; /** * Service to provide functionalities regarding site plugins. @@ -101,7 +101,7 @@ export class CoreSitePluginsProvider { appcustomurlscheme: CoreConstants.CONFIG.customurlscheme, appisdesktop: false, appismobile: CorePlatform.isMobile(), - appiswide: CoreApp.isWide(), + appiswide: CorePlatform.isWide(), appplatform: 'browser', }; @@ -157,13 +157,13 @@ export class CoreSitePluginsProvider { data = Object.assign(data, initResult.jsResult || {}); // Now add some data returned by the init WS call. - data.INIT_TEMPLATES = CoreUtils.objectToKeyValueMap(initResult.templates, 'id', 'html'); + data.INIT_TEMPLATES = CoreObject.toKeyValueMap(initResult.templates, 'id', 'html'); data.INIT_OTHERDATA = initResult.otherdata; } if (contentResult) { // Now add the data returned by the content WS call. - data.CONTENT_TEMPLATES = CoreUtils.objectToKeyValueMap(contentResult.templates, 'id', 'html'); + data.CONTENT_TEMPLATES = CoreObject.toKeyValueMap(contentResult.templates, 'id', 'html'); data.CONTENT_OTHERDATA = contentResult.otherdata; } @@ -178,7 +178,7 @@ export class CoreSitePluginsProvider { * @returns Cache key. */ getCallWSCacheKey(method: string, data: Record): string { - return this.getCallWSCommonCacheKey(method) + ':' + CoreUtils.sortAndStringify(data); + return this.getCallWSCommonCacheKey(method) + ':' + CoreObject.sortAndStringify(data); } /** @@ -220,18 +220,18 @@ export class CoreSitePluginsProvider { const data: CoreSitePluginsGetContentWSParams = { component: component, method: method, - args: CoreUtils.objectToArrayOfObjects(argsToSend, 'name', 'value', true), + args: CoreObject.toArrayOfObjects(argsToSend, 'name', 'value', true), }; preSets = preSets || {}; preSets.cacheKey = this.getContentCacheKey(component, method, args); - preSets.updateFrequency = preSets.updateFrequency ?? CoreSite.FREQUENCY_OFTEN; + preSets.updateFrequency = preSets.updateFrequency ?? CoreCacheUpdateFrequency.OFTEN; const result = await site.read('tool_mobile_get_content', data, preSets); let otherData: Record = {}; if (result.otherdata) { - otherData = > CoreUtils.objectToKeyValueMap(result.otherdata, 'name', 'value'); + otherData = > CoreObject.toKeyValueMap(result.otherdata, 'name', 'value'); // Try to parse all properties that could be JSON encoded strings. for (const name in otherData) { @@ -256,7 +256,7 @@ export class CoreSitePluginsProvider { */ protected getContentCacheKey(component: string, method: string, args: Record): string { return CoreSitePluginsProvider.ROOT_CACHE_KEY + 'content:' + component + ':' + method + - ':' + CoreUtils.sortAndStringify(args); + ':' + CoreObject.sortAndStringify(args); } /** @@ -347,7 +347,7 @@ export class CoreSitePluginsProvider { * @returns Plugin list ws info. */ getCurrentSitePluginList(): CoreSitePluginsWSPlugin[] { - return CoreUtils.objectToArray(this.sitePlugins).map((plugin) => plugin.plugin); + return CoreObject.toArray(this.sitePlugins).map((plugin) => plugin.plugin); } /** @@ -508,7 +508,7 @@ export class CoreSitePluginsProvider { for (const i in useOtherData) { const name = useOtherData[i]; - if (typeof otherData[name] == 'object' && otherData[name] !== null) { + if (typeof otherData[name] === 'object' && otherData[name] !== null) { // Stringify objects. args[name] = JSON.stringify(otherData[name]); } else { @@ -518,7 +518,7 @@ export class CoreSitePluginsProvider { } else { // Add all the data to args. for (const name in otherData) { - if (typeof otherData[name] == 'object' && otherData[name] !== null) { + if (typeof otherData[name] === 'object' && otherData[name] !== null) { // Stringify objects. args[name] = JSON.stringify(otherData[name]); } else { diff --git a/src/core/features/siteplugins/siteplugins.module.ts b/src/core/features/siteplugins/siteplugins.module.ts index 187e3633742..b479602df3c 100644 --- a/src/core/features/siteplugins/siteplugins.module.ts +++ b/src/core/features/siteplugins/siteplugins.module.ts @@ -12,22 +12,40 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { APP_INITIALIZER, NgModule } from '@angular/core'; +import { APP_INITIALIZER, NgModule, Type } from '@angular/core'; import { Routes } from '@angular/router'; import { CoreCourseIndexRoutingModule } from '@features/course/course-routing.module'; import { CoreMainMenuTabRoutingModule } from '@features/mainmenu/mainmenu-tab-routing.module'; import { CoreMainMenuHomeRoutingModule } from '@features/mainmenu/mainmenu-home-routing.module'; import { CoreSitePreferencesRoutingModule } from '@features/settings/settings-site-routing.module'; -import { CoreSitePluginsComponentsModule } from './components/components.module'; -import { CoreSitePluginsInit } from './services/siteplugins-init'; -import { CoreSharedModule } from '@/core/shared.module'; -import { CoreSitePluginsPluginPage } from '@features/siteplugins/pages/plugin/plugin'; import { canLeaveGuard } from '@guards/can-leave'; -import { CoreSitePluginsCourseOptionPage } from '@features/siteplugins/pages/course-option/course-option'; -import { CoreSitePluginsModuleIndexPage } from '@features/siteplugins/pages/module-index/module-index'; import { CORE_SITE_PLUGINS_PATH } from './constants'; +/** + * Get site plugins directives modules. + * + * @returns Site plugins exported directives. + */ +export async function getSitePluginsDirectives(): Promise { + const { CoreSitePluginsDirectivesModule } = await import('./directives/directives.module'); + + return [CoreSitePluginsDirectivesModule]; +} + +/** + * Get shared files services. + * + * @returns Returns shared files services. + */ +export async function getSitePluginsServices(): Promise[]> { + const { CoreSitePluginsProvider } = await import('@features/siteplugins/services/siteplugins'); + + return [ + CoreSitePluginsProvider, + ]; +} + /** * Get site plugins exported objects. * @@ -67,7 +85,7 @@ export async function getSitePluginsExportedObjects(): Promise import('@features/siteplugins/pages/plugin/plugin'), canDeactivate: [canLeaveGuard], }, ]; @@ -75,7 +93,7 @@ const routes: Routes = [ const homeRoutes: Routes = [ { path: `${CORE_SITE_PLUGINS_PATH}/homecontent/:component/:method`, - component: CoreSitePluginsPluginPage, + loadComponent: () => import('@features/siteplugins/pages/plugin/plugin'), canDeactivate: [canLeaveGuard], }, ]; @@ -83,7 +101,7 @@ const homeRoutes: Routes = [ const courseIndexRoutes: Routes = [ { path: `${CORE_SITE_PLUGINS_PATH}/:handlerUniqueName`, - component: CoreSitePluginsCourseOptionPage, + loadComponent: () => import('@features/siteplugins/pages/course-option/course-option'), canDeactivate: [canLeaveGuard], }, ]; @@ -91,7 +109,7 @@ const courseIndexRoutes: Routes = [ const moduleRoutes: Routes = [ { path: `${CORE_SITE_PLUGINS_PATH}/module/:courseId/:cmId`, - component: CoreSitePluginsModuleIndexPage, + loadComponent: () => import('@features/siteplugins/pages/module-index/module-index'), canDeactivate: [canLeaveGuard], }, ]; @@ -102,19 +120,14 @@ const moduleRoutes: Routes = [ CoreCourseIndexRoutingModule.forChild({ children: courseIndexRoutes }), CoreMainMenuHomeRoutingModule.forChild({ children: homeRoutes }), CoreSitePreferencesRoutingModule.forChild(routes), - CoreSitePluginsComponentsModule, - CoreSharedModule, - ], - declarations: [ - CoreSitePluginsPluginPage, - CoreSitePluginsCourseOptionPage, - CoreSitePluginsModuleIndexPage, ], providers: [ { provide: APP_INITIALIZER, multi: true, - useValue: () => { + useValue: async () => { + const { CoreSitePluginsInit } = await import('./services/siteplugins-init'); + CoreSitePluginsInit.init(); }, }, diff --git a/src/core/features/styles/services/styles.ts b/src/core/features/styles/services/styles.ts index a62ee264e45..d83c4774f6d 100644 --- a/src/core/features/styles/services/styles.ts +++ b/src/core/features/styles/services/styles.ts @@ -17,7 +17,7 @@ import { CoreError } from '@classes/errors/error'; import { CoreSitePublicConfigResponse } from '@classes/sites/unauthenticated-site'; import { CoreApp } from '@services/app'; import { CoreSites } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { makeSingleton } from '@singletons'; import { CoreEvents } from '@singletons/events'; import { Md5 } from 'ts-md5'; @@ -373,7 +373,7 @@ export class CoreStylesService { this.disableStyleElementByName(siteIdentifier, sourceName, disabled); } - await CoreUtils.allPromises(this.styleHandlers.map(async (handler) => { + await CorePromiseUtils.allPromises(this.styleHandlers.map(async (handler) => { await this.setStyle(siteIdentifier, handler, disabled); })); @@ -392,7 +392,7 @@ export class CoreStylesService { // Create the style and add it to the header. this.createStyleElements(CoreStylesService.TMP_SITE_ID, true); - await CoreUtils.allPromises(this.styleHandlers.map(async (handler) => { + await CorePromiseUtils.allPromises(this.styleHandlers.map(async (handler) => { await this.setStyle(CoreStylesService.TMP_SITE_ID, handler, false, config); })); @@ -405,7 +405,7 @@ export class CoreStylesService { * @returns Promise resolved when loaded. */ protected async preloadCurrentSite(): Promise { - const siteId = await CoreUtils.ignoreErrors(CoreSites.getStoredCurrentSiteId()); + const siteId = await CorePromiseUtils.ignoreErrors(CoreSites.getStoredCurrentSiteId()); if (!siteId) { // No current site stored. @@ -423,7 +423,7 @@ export class CoreStylesService { protected async preloadSites(): Promise { const ids = await CoreSites.getSitesIds(); - await CoreUtils.allPromises(ids.map((siteId) => this.addSite(siteId))); + await CorePromiseUtils.allPromises(ids.map((siteId) => this.addSite(siteId))); } /** diff --git a/src/core/features/tag/components/list/list.ts b/src/core/features/tag/components/list/list.ts index 561db82bf9c..29897b7db95 100644 --- a/src/core/features/tag/components/list/list.ts +++ b/src/core/features/tag/components/list/list.ts @@ -23,7 +23,7 @@ import { CoreNavigator } from '@services/navigator'; @Component({ selector: 'core-tag-list', templateUrl: 'core-tag-list.html', - styleUrls: ['list.scss'], + styleUrl: 'list.scss', }) export class CoreTagListComponent { diff --git a/src/core/features/tag/pages/search/search.ts b/src/core/features/tag/pages/search/search.ts index 7387dd3a84d..878ca174bbb 100644 --- a/src/core/features/tag/pages/search/search.ts +++ b/src/core/features/tag/pages/search/search.ts @@ -15,7 +15,7 @@ import { Component, OnInit } from '@angular/core'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreUrl } from '@singletons/url'; import { CoreTagCloud, CoreTagCollection, CoreTagCloudTag, CoreTag } from '@features/tag/services/tag'; import { Translate } from '@singletons'; @@ -32,7 +32,7 @@ import { CoreSites } from '@services/sites'; @Component({ selector: 'page-core-tag-search', templateUrl: 'search.html', - styleUrls: ['search.scss'], + styleUrl: 'search.scss', }) export class CoreTagSearchPage implements OnInit { @@ -129,7 +129,7 @@ export class CoreTagSearchPage implements OnInit { * @param refresher Refresher event. */ refreshData(refresher?: HTMLIonRefresherElement): void { - CoreUtils.allPromises([ + CorePromiseUtils.allPromises([ CoreTag.invalidateTagCollections(), CoreTag.invalidateTagCloud(this.collectionId, undefined, undefined, this.query), ]).finally(() => this.fetchData().finally(() => { diff --git a/src/core/features/tag/services/tag.ts b/src/core/features/tag/services/tag.ts index ccbc08d6a9e..0a7c1ac98e5 100644 --- a/src/core/features/tag/services/tag.ts +++ b/src/core/features/tag/services/tag.ts @@ -19,6 +19,7 @@ import { CoreWSExternalWarning } from '@services/ws'; import { makeSingleton, Translate } from '@singletons'; import { CoreError } from '@classes/errors/error'; import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site'; +import { CoreCacheUpdateFrequency } from '@/core/constants'; const ROOT_CACHE_KEY = 'CoreTag:'; @@ -100,7 +101,7 @@ export class CoreTagProvider { rec: recursive, }; const preSets: CoreSiteWSPreSets = { - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + updateFrequency: CoreCacheUpdateFrequency.SOMETIMES, cacheKey: this.getTagCloudKey(collectionId, isStandard, sort, search, fromContextId, contextId, recursive), getFromCache: search != '', // Try to get updated data when searching. }; @@ -118,7 +119,7 @@ export class CoreTagProvider { async getTagCollections(siteId?: string): Promise { const site = await CoreSites.getSite(siteId); const preSets: CoreSiteWSPreSets = { - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, cacheKey: this.getTagCollectionsKey(), }; @@ -172,7 +173,7 @@ export class CoreTagProvider { }, }; const preSets: CoreSiteWSPreSets = { - updateFrequency: CoreSite.FREQUENCY_OFTEN, + updateFrequency: CoreCacheUpdateFrequency.OFTEN, cacheKey: this.getTagIndexPerAreaKey(id, name, collectionId, areaId, fromContextId, contextId, recursive), }; diff --git a/src/core/features/tag/tag-lazy.module.ts b/src/core/features/tag/tag-lazy.module.ts index 12b563ee251..74622429900 100644 --- a/src/core/features/tag/tag-lazy.module.ts +++ b/src/core/features/tag/tag-lazy.module.ts @@ -95,4 +95,4 @@ function buildRoutes(injector: Injector): Routes { }, ], }) -export class CoreTagLazyModule {} +export default class CoreTagLazyModule {} diff --git a/src/core/features/tag/tag.module.ts b/src/core/features/tag/tag.module.ts index b641463809a..9a19ace853e 100644 --- a/src/core/features/tag/tag.module.ts +++ b/src/core/features/tag/tag.module.ts @@ -43,7 +43,7 @@ export async function getTagServices(): Promise[]> { const routes: Routes = [ { path: CoreTagMainMenuHandlerService.PAGE_NAME, - loadChildren: () => import('./tag-lazy.module').then(m => m.CoreTagLazyModule), + loadChildren: () => import('./tag-lazy.module'), }, ]; diff --git a/src/core/features/user/classes/support/guest-support-config.ts b/src/core/features/user/classes/support/guest-support-config.ts index f60277e0d49..ff43c6cc168 100644 --- a/src/core/features/user/classes/support/guest-support-config.ts +++ b/src/core/features/user/classes/support/guest-support-config.ts @@ -19,7 +19,7 @@ import { } from '@classes/sites/unauthenticated-site'; import { CoreUserNullSupportConfig } from '@features/user/classes/support/null-support-config'; import { CoreSites } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreUserSupportConfig } from './support-config'; import { CoreSitesFactory } from '@services/sites-factory'; @@ -35,7 +35,7 @@ export class CoreUserGuestSupportConfig extends CoreUserSupportConfig { * @returns Support config. */ static async forSite(siteUrl: string): Promise { - const siteConfig = await CoreUtils.ignoreErrors(CoreSites.getPublicSiteConfigByUrl(siteUrl)); + const siteConfig = await CorePromiseUtils.ignoreErrors(CoreSites.getPublicSiteConfigByUrl(siteUrl)); if (!siteConfig) { return new CoreUserNullSupportConfig(); diff --git a/src/core/features/user/components/user-profile-field/user-profile-field.ts b/src/core/features/user/components/user-profile-field/user-profile-field.ts index 9a33ee6edce..bfbdae44bf4 100644 --- a/src/core/features/user/components/user-profile-field/user-profile-field.ts +++ b/src/core/features/user/components/user-profile-field/user-profile-field.ts @@ -18,7 +18,7 @@ import { CoreLang } from '@services/lang'; import { AuthEmailSignupProfileField } from '@features/login/services/login-helper'; import { CoreUserProfileField } from '@features/user/services/user'; import { CoreUserProfileFieldDelegate } from '@features/user/services/user-profile-field-delegate'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { ContextLevel } from '@/core/constants'; import { toBoolean } from '@/core/transforms/boolean'; diff --git a/src/core/features/user/constants.ts b/src/core/features/user/constants.ts index 82edcc1d567..a29ad985446 100644 --- a/src/core/features/user/constants.ts +++ b/src/core/features/user/constants.ts @@ -13,3 +13,5 @@ // limitations under the License. export const PARTICIPANTS_PAGE_NAME = 'participants'; + +export const CORE_USER_AUTO_SYNCED = 'core_user_autom_synced'; diff --git a/src/core/features/user/pages/about/about.ts b/src/core/features/user/pages/about/about.ts index 54ff793a975..a0b3b092da3 100644 --- a/src/core/features/user/pages/about/about.ts +++ b/src/core/features/user/pages/about/about.ts @@ -16,7 +16,7 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreUser, @@ -25,7 +25,6 @@ import { USER_PROFILE_REFRESHED, USER_PROFILE_SERVER_TIMEZONE, } from '@features/user/services/user'; -import { CoreUserHelper } from '@features/user/services/user-helper'; import { CoreNavigator } from '@services/navigator'; import { CoreIonLoadingElement } from '@classes/ion-loading'; import { CoreSite } from '@classes/sites/site'; @@ -34,6 +33,7 @@ import { CoreMimetypeUtils } from '@services/utils/mimetype'; import { Translate } from '@singletons'; import { CoreUrl } from '@singletons/url'; import { CoreLoadings } from '@services/loadings'; +import { CoreTime } from '@singletons/time'; /** * Page that displays info about a user. @@ -41,7 +41,7 @@ import { CoreLoadings } from '@services/loadings'; @Component({ selector: 'page-core-user-about', templateUrl: 'about.html', - styleUrls: ['about.scss'], + styleUrl: 'about.scss', }) export class CoreUserAboutPage implements OnInit, OnDestroy { @@ -203,7 +203,7 @@ export class CoreUserAboutPage implements OnInit, OnDestroy { * @returns Promise resolved when done. */ async refreshUser(event?: HTMLIonRefresherElement): Promise { - await CoreUtils.ignoreErrors(CoreUser.invalidateUserCache(this.userId)); + await CorePromiseUtils.ignoreErrors(CoreUser.invalidateUserCache(this.userId)); await this.fetchUser(); @@ -275,7 +275,7 @@ export class CoreUserAboutPage implements OnInit, OnDestroy { } if (this.user.timezone) { - this.user.timezone = CoreUserHelper.translateLegacyTimezone(this.user.timezone); + this.user.timezone = CoreTime.translateLegacyTimezone(this.user.timezone); } } diff --git a/src/core/features/user/pages/complete-profile/complete-profile.ts b/src/core/features/user/pages/complete-profile/complete-profile.ts index 5e3ad563fec..4e930652ed2 100644 --- a/src/core/features/user/pages/complete-profile/complete-profile.ts +++ b/src/core/features/user/pages/complete-profile/complete-profile.ts @@ -19,8 +19,8 @@ import { CoreLoginHelper } from '@features/login/services/login-helper'; import { Translate } from '@singletons'; import { CoreNavigator } from '@services/navigator'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; -import { CoreUtils } from '@services/utils/utils'; import { CoreUserSupport } from '@features/user/services/support'; +import { CoreOpener } from '@singletons/opener'; /** * Page that shows instructions to complete the profile. @@ -93,7 +93,7 @@ export class CoreUserCompleteProfilePage implements OnDestroy { this.urlLoadedObserver = CoreEvents.on(CoreEvents.IAB_LOAD_START, (event) => { if (event.url.match(/\/user\/preferences.php/)) { // Profile should be complete now. - CoreUtils.closeInAppBrowser(); + CoreOpener.closeInAppBrowser(); this.login(); } }); diff --git a/src/core/features/user/pages/participants/participants.page.ts b/src/core/features/user/pages/participants/participants.page.ts index c8d93275e06..0ec5c1b3a02 100644 --- a/src/core/features/user/pages/participants/participants.page.ts +++ b/src/core/features/user/pages/participants/participants.page.ts @@ -19,7 +19,7 @@ import { CoreNavigator } from '@services/navigator'; import { CoreListItemsManager } from '@classes/items-management/list-items-manager'; import { CoreSplitViewComponent } from '@components/split-view/split-view'; import { CoreUser, CoreUserParticipant, CoreUserData } from '@features/user/services/user'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreUserParticipantsSource } from '@features/user/classes/participants-source'; import { CoreRoutedItemsManagerSourcesTracker } from '@classes/items-management/routed-items-manager-sources-tracker'; import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics'; @@ -136,8 +136,8 @@ export class CoreUserParticipantsPage implements OnInit, AfterViewInit, OnDestro * @param refresher Refresher. */ async refreshParticipants(refresher: HTMLIonRefresherElement): Promise { - await CoreUtils.ignoreErrors(CoreUser.invalidateParticipantsList(this.courseId)); - await CoreUtils.ignoreErrors(this.fetchParticipants(true)); + await CorePromiseUtils.ignoreErrors(CoreUser.invalidateParticipantsList(this.courseId)); + await CorePromiseUtils.ignoreErrors(this.fetchParticipants(true)); refresher?.complete(); } @@ -196,7 +196,7 @@ class CoreUserParticipantsManager extends CoreListItemsManager { - await CoreUtils.ignoreErrors(CoreUser.logParticipantsView(this.getSource().COURSE_ID)); + await CorePromiseUtils.ignoreErrors(CoreUser.logParticipantsView(this.getSource().COURSE_ID)); CoreAnalytics.logEvent({ type: CoreAnalyticsEventType.VIEW_ITEM_LIST, diff --git a/src/core/features/user/pages/profile/profile.ts b/src/core/features/user/pages/profile/profile.ts index 1e5538dbb76..5d623b98db7 100644 --- a/src/core/features/user/pages/profile/profile.ts +++ b/src/core/features/user/pages/profile/profile.ts @@ -28,7 +28,7 @@ import { CoreUserProfileHandlerType, CoreUserProfileHandlerData, } from '@features/user/services/user-delegate'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreNavigator } from '@services/navigator'; import { CoreCourses } from '@features/courses/services/courses'; import { CoreSwipeNavigationItemsManager } from '@classes/items-management/swipe-navigation-items-manager'; @@ -41,7 +41,7 @@ import { Translate } from '@singletons'; @Component({ selector: 'page-core-user-profile', templateUrl: 'profile.html', - styleUrls: ['profile.scss'], + styleUrl: 'profile.scss', }) export class CoreUserProfilePage implements OnInit, OnDestroy { @@ -186,7 +186,7 @@ export class CoreUserProfilePage implements OnInit, OnDestroy { * @returns Promise resolved when done. */ async refreshUser(event?: HTMLIonRefresherElement): Promise { - await CoreUtils.ignoreErrors(Promise.all([ + await CorePromiseUtils.ignoreErrors(Promise.all([ CoreUser.invalidateUserCache(this.userId), CoreCourses.invalidateUserNavigationOptions(), CoreCourses.invalidateUserAdministrationOptions(), diff --git a/src/core/features/user/services/handlers/course-option.ts b/src/core/features/user/services/handlers/course-option.ts index 43166821725..af685989cb2 100644 --- a/src/core/features/user/services/handlers/course-option.ts +++ b/src/core/features/user/services/handlers/course-option.ts @@ -13,7 +13,7 @@ // limitations under the License. import { Injectable } from '@angular/core'; -import { CoreCourseAccessDataType } from '@features/course/services/course'; +import { CoreCourseAccessDataType } from '@features/course/constants'; import { CoreCourseAccess, CoreCourseOptionsHandler, diff --git a/src/core/features/user/services/handlers/profile-mail.ts b/src/core/features/user/services/handlers/profile-mail.ts index 7a23a58ef4e..4f966f3a7e8 100644 --- a/src/core/features/user/services/handlers/profile-mail.ts +++ b/src/core/features/user/services/handlers/profile-mail.ts @@ -20,7 +20,7 @@ import { CoreUserProfileHandlerData, } from '../user-delegate'; import { CoreSites } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreOpener } from '@singletons/opener'; import { CoreUserProfile } from '../user'; import { makeSingleton } from '@singletons'; @@ -60,7 +60,7 @@ export class CoreUserProfileMailHandlerService implements CoreUserProfileHandler event.preventDefault(); event.stopPropagation(); - CoreUtils.openInBrowser('mailto:' + user.email, { showBrowserWarning: false }); + CoreOpener.openInBrowser('mailto:' + user.email, { showBrowserWarning: false }); }, }; } diff --git a/src/core/features/user/services/handlers/tag-area.ts b/src/core/features/user/services/handlers/tag-area.ts index 4e5c6761fa0..d6a1d04f131 100644 --- a/src/core/features/user/services/handlers/tag-area.ts +++ b/src/core/features/user/services/handlers/tag-area.ts @@ -16,7 +16,6 @@ import { Injectable, Type } from '@angular/core'; import { convertTextToHTMLElement } from '@/core/utils/create-html-element'; import { CoreTagAreaHandler } from '@features/tag/services/tag-area-delegate'; -import { CoreUserTagAreaComponent } from '@features/user/components/tag-area/tag-area'; import { CoreTagFeedElement } from '@features/tag/services/tag-helper'; import { CoreUserBasicData } from '../user'; import { makeSingleton } from '@singletons'; @@ -81,12 +80,12 @@ export class CoreUserTagAreaHandlerService implements CoreTagAreaHandler { } /** - * Get the component to use to display items. - * - * @returns The component (or promise resolved with component) to use, undefined if not found. + * @inheritdoc */ - getComponent(): Type | Promise> { - return CoreUserTagAreaComponent; + async getComponent(): Promise> { + const { CoreTagFeedComponent } = await import('@features/tag/components/feed/feed'); + + return CoreTagFeedComponent; } } diff --git a/src/core/features/user/services/support.ts b/src/core/features/user/services/support.ts index 1e85f58487b..f76c4a2de7a 100644 --- a/src/core/features/user/services/support.ts +++ b/src/core/features/user/services/support.ts @@ -18,7 +18,6 @@ import { CoreUserAuthenticatedSupportConfig } from '@features/user/classes/suppo import { InAppBrowserObject } from '@awesome-cordova-plugins/in-app-browser'; import { CorePlatform } from '@services/platform'; import { CoreSites } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; import { makeSingleton, Translate } from '@singletons'; import { CoreEvents } from '@singletons/events'; import { CoreSubscriptions } from '@singletons/subscriptions'; @@ -26,6 +25,7 @@ import { AlertButton } from '@ionic/angular'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreLang } from '@services/lang'; import { CoreUserNullSupportConfig } from '@features/user/classes/support/null-support-config'; +import { CoreOpener } from '@singletons/opener'; /** * Handle site support. @@ -42,7 +42,7 @@ export class CoreUserSupportService { const supportConfig = options.supportConfig ?? CoreUserAuthenticatedSupportConfig.forCurrentSite(); const supportPageUrl = supportConfig.getSupportPageUrl(); const autoLoginUrl = await CoreSites.getCurrentSite()?.getAutoLoginUrl(supportPageUrl, false); - const browser = CoreUtils.openInApp(autoLoginUrl ?? supportPageUrl); + const browser = CoreOpener.openInApp(autoLoginUrl ?? supportPageUrl); if (supportPageUrl.endsWith('/user/contactsitesupport.php')) { this.populateSupportForm(browser, options.subject, options.message); diff --git a/src/core/features/user/services/user-delegate.ts b/src/core/features/user/services/user-delegate.ts index 60bd7f31ce4..9e0e9ac2500 100644 --- a/src/core/features/user/services/user-delegate.ts +++ b/src/core/features/user/services/user-delegate.ts @@ -16,7 +16,7 @@ import { Injectable } from '@angular/core'; import { Subject, BehaviorSubject } from 'rxjs'; import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreEvents } from '@singletons/events'; import { CoreUserProfile, USER_PROFILE_REFRESHED } from './user'; import { makeSingleton } from '@singletons'; @@ -334,7 +334,7 @@ export class CoreUserDelegateService extends CoreDelegate { + await CorePromiseUtils.allPromises(Object.keys(this.enabledHandlers).map(async (name) => { // Checks if the handler is enabled for the user. const handler = this.handlers[name]; diff --git a/src/core/features/user/services/user-helper.ts b/src/core/features/user/services/user-helper.ts index 4d2e5336720..fd790ec5036 100644 --- a/src/core/features/user/services/user-helper.ts +++ b/src/core/features/user/services/user-helper.ts @@ -18,6 +18,7 @@ import { CoreSites } from '@services/sites'; import { makeSingleton, Translate } from '@singletons'; import { CoreUser, CoreUserProfile, CoreUserRole } from './user'; +import { CoreTime } from '@singletons/time'; /** * Service that provides some features regarding users information. @@ -25,63 +26,6 @@ import { CoreUser, CoreUserProfile, CoreUserRole } from './user'; @Injectable({ providedIn: 'root' }) export class CoreUserHelperProvider { - protected static readonly LEGACY_TIMEZONES = { - '-13.0': 'Australia/Perth', - '-12.5': 'Etc/GMT+12', - '-12.0': 'Etc/GMT+12', - '-11.5': 'Etc/GMT+11', - '-11.0': 'Etc/GMT+11', - '-10.5': 'Etc/GMT+10', - '-10.0': 'Etc/GMT+10', - '-9.5': 'Etc/GMT+9', - '-9.0': 'Etc/GMT+9', - '-8.5': 'Etc/GMT+8', - '-8.0': 'Etc/GMT+8', - '-7.5': 'Etc/GMT+7', - '-7.0': 'Etc/GMT+7', - '-6.5': 'Etc/GMT+6', - '-6.0': 'Etc/GMT+6', - '-5.5': 'Etc/GMT+5', - '-5.0': 'Etc/GMT+5', - '-4.5': 'Etc/GMT+4', - '-4.0': 'Etc/GMT+4', - '-3.5': 'Etc/GMT+3', - '-3.0': 'Etc/GMT+3', - '-2.5': 'Etc/GMT+2', - '-2.0': 'Etc/GMT+2', - '-1.5': 'Etc/GMT+1', - '-1.0': 'Etc/GMT+1', - '-0.5': 'Etc/GMT', - '0': 'Etc/GMT', - '0.0': 'Etc/GMT', - '0.5': 'Etc/GMT', - '1.0': 'Etc/GMT-1', - '1.5': 'Etc/GMT-1', - '2.0': 'Etc/GMT-2', - '2.5': 'Etc/GMT-2', - '3.0': 'Etc/GMT-3', - '3.5': 'Etc/GMT-3', - '4.0': 'Etc/GMT-4', - '4.5': 'Asia/Kabul', - '5.0': 'Etc/GMT-5', - '5.5': 'Asia/Kolkata', - '6.0': 'Etc/GMT-6', - '6.5': 'Asia/Rangoon', - '7.0': 'Etc/GMT-7', - '7.5': 'Etc/GMT-7', - '8.0': 'Etc/GMT-8', - '8.5': 'Etc/GMT-8', - '9.0': 'Etc/GMT-9', - '9.5': 'Australia/Darwin', - '10.0': 'Etc/GMT-10', - '10.5': 'Etc/GMT-10', - '11.0': 'Etc/GMT-11', - '11.5': 'Etc/GMT-11', - '12.0': 'Etc/GMT-12', - '12.5': 'Etc/GMT-12', - '13.0': 'Etc/GMT-13', - }; - /** * Formats a user address, concatenating address, city and country. * @@ -192,9 +136,10 @@ export class CoreUserHelperProvider { * * @param tz Timezone name. * @returns Readable timezone name. + * @deprecated since 5.0. Use CoreTime.translateLegacyTimezone instead. */ translateLegacyTimezone(tz: string): string { - return CoreUserHelperProvider.LEGACY_TIMEZONES[tz] ?? tz; + return CoreTime.translateLegacyTimezone(tz); } } diff --git a/src/core/features/user/services/user-offline.ts b/src/core/features/user/services/user-offline.ts index 09d0606853c..e308b8ccfd8 100644 --- a/src/core/features/user/services/user-offline.ts +++ b/src/core/features/user/services/user-offline.ts @@ -15,7 +15,7 @@ import { Injectable } from '@angular/core'; import { CoreSites } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { makeSingleton } from '@singletons'; import { PREFERENCES_TABLE_NAME, CoreUserPreferenceDBRecord } from './database/user'; @@ -73,7 +73,7 @@ export class CoreUserOfflineProvider { if (onlineValue === undefined) { // Keep online value already stored (if any). - const entry = await CoreUtils.ignoreErrors( + const entry = await CorePromiseUtils.ignoreErrors( site.getDb().getRecord(PREFERENCES_TABLE_NAME, { name }), null, ); diff --git a/src/core/features/user/services/user-sync.ts b/src/core/features/user/services/user-sync.ts index f6742229210..d7836502db2 100644 --- a/src/core/features/user/services/user-sync.ts +++ b/src/core/features/user/services/user-sync.ts @@ -16,11 +16,12 @@ import { Injectable } from '@angular/core'; import { CoreSites } from '@services/sites'; import { CoreErrorHelper } from '@services/error-helper'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreWSError } from '@classes/errors/wserror'; import { CoreSyncBaseProvider } from '@classes/base-sync'; import { makeSingleton } from '@singletons'; import { CoreUserOffline } from './user-offline'; import { CoreUser } from './user'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Service to sync user preferences. @@ -28,8 +29,6 @@ import { CoreUser } from './user'; @Injectable({ providedIn: 'root' }) export class CoreUserSyncProvider extends CoreSyncBaseProvider { - static readonly AUTO_SYNCED = 'core_user_autom_synced'; - constructor() { super('CoreUserSync'); } @@ -79,7 +78,7 @@ export class CoreUserSyncProvider extends CoreSyncBaseProvider { const preferences = await CoreUserOffline.getChangedPreferences(siteId); - await CoreUtils.allPromises(preferences.map(async (preference) => { + await CorePromiseUtils.allPromises(preferences.map(async (preference) => { const onlineValue = await CoreUser.getUserPreferenceOnline(preference.name, siteId); if (onlineValue !== null && preference.onlinevalue != onlineValue) { @@ -90,7 +89,7 @@ export class CoreUserSyncProvider extends CoreSyncBaseProvider { try { await CoreUser.setUserPreference(preference.name, preference.value, siteId); } catch (error) { - if (CoreUtils.isWebServiceError(error)) { + if (CoreWSError.isWebServiceError(error)) { const warning = CoreErrorHelper.getErrorMessageFromError(error); if (warning) { warnings.push(warning); diff --git a/src/core/features/user/services/user.ts b/src/core/features/user/services/user.ts index e837294d5d1..84d4a7ee924 100644 --- a/src/core/features/user/services/user.ts +++ b/src/core/features/user/services/user.ts @@ -17,7 +17,7 @@ import { Injectable } from '@angular/core'; import { CoreNetwork } from '@services/network'; import { CoreFilepool } from '@services/filepool'; import { CoreSites } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreCountries } from '@singletons/countries'; import { CoreUserOffline } from './user-offline'; import { CoreLogger } from '@singletons/logger'; import { CoreSite } from '@classes/sites/site'; @@ -28,7 +28,8 @@ import { CoreError } from '@classes/errors/error'; import { USERS_TABLE_NAME, CoreUserDBRecord } from './database/user'; import { CoreUrl } from '@singletons/url'; import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site'; -import { CoreConstants } from '@/core/constants'; +import { CoreCacheUpdateFrequency, CoreConstants } from '@/core/constants'; +import { CorePromiseUtils } from '@singletons/promise-utils'; const ROOT_CACHE_KEY = 'mmUser:'; @@ -223,7 +224,7 @@ export class CoreUserProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getParticipantsListCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, }; if (ignoreCache) { @@ -291,7 +292,7 @@ export class CoreUserProvider { * @returns Starting week day. */ async getStartingWeekDay(): Promise { - const preference = await CoreUtils.ignoreErrors(this.getUserPreference('calendar_startwday')); + const preference = await CorePromiseUtils.ignoreErrors(this.getUserPreference('calendar_startwday')); if (preference && !isNaN(Number(preference))) { return Number(preference); @@ -362,7 +363,7 @@ export class CoreUserProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getUserCacheKey(userId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, }; let users: CoreUserData[] | CoreUserCourseProfile[] | undefined; @@ -398,7 +399,7 @@ export class CoreUserProvider { const user: CoreUserData | CoreUserCourseProfile = users[0]; if (user.country) { - user.country = CoreUtils.getCountryName(user.country); + user.country = CoreCountries.getCountryName(user.country); } this.storeUser(user.id, user.fullname, user.profileimageurl); @@ -415,7 +416,7 @@ export class CoreUserProvider { async getUserPreference(name: string, siteId?: string): Promise { siteId = siteId || CoreSites.getCurrentSiteId(); - const preference = await CoreUtils.ignoreErrors(CoreUserOffline.getPreference(name, siteId)); + const preference = await CorePromiseUtils.ignoreErrors(CoreUserOffline.getPreference(name, siteId)); if (preference && !CoreNetwork.isOnline()) { // Offline, return stored value. @@ -463,7 +464,7 @@ export class CoreUserProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getUserPreferenceCacheKey(name), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + updateFrequency: CoreCacheUpdateFrequency.SOMETIMES, }; const result = await site.read('core_user_get_user_preferences', params, preSets); @@ -547,7 +548,7 @@ export class CoreUserProvider { } // Retrieving one participant will fail if browsing users is disabled by capabilities. - return CoreUtils.promiseWorks(this.getParticipants(courseId, 0, 1, siteId)); + return CorePromiseUtils.promiseWorks(this.getParticipants(courseId, 0, 1, siteId)); } /** @@ -791,7 +792,7 @@ export class CoreUserProvider { // Update preference and invalidate data. await Promise.all([ CoreUserOffline.setPreference(name, value, value), - CoreUtils.ignoreErrors(this.invalidateUserPreference(name)), + CorePromiseUtils.ignoreErrors(this.invalidateUserPreference(name)), ]); } catch (error) { // Preference not saved online. Update the offline one. diff --git a/src/core/features/user/user-app-lazy.module.ts b/src/core/features/user/user-app-lazy.module.ts index e5126a789eb..5424acbeace 100644 --- a/src/core/features/user/user-app-lazy.module.ts +++ b/src/core/features/user/user-app-lazy.module.ts @@ -33,4 +33,4 @@ const routes: Routes = [ CoreUserCompleteProfilePage, ], }) -export class CoreUserAppLazyModule {} +export default class CoreUserAppLazyModule {} diff --git a/src/core/features/user/user-course-lazy.module.ts b/src/core/features/user/user-course-lazy.module.ts index a0a9cb34cca..eebc44954b1 100644 --- a/src/core/features/user/user-course-lazy.module.ts +++ b/src/core/features/user/user-course-lazy.module.ts @@ -27,7 +27,7 @@ const routes: Routes = [ children: conditionalRoutes([ { path: ':userId', - loadChildren: () => import('@features/user/user-profile-lazy.module').then(m => m.CoreUserProfileLazyModule), + loadChildren: () => import('@features/user/user-profile-lazy.module'), data: { swipeManagerSource: 'participants' }, }, ], () => CoreScreen.isTablet), @@ -40,4 +40,4 @@ const routes: Routes = [ CoreUserParticipantsPageModule, ], }) -export class CoreUserCourseLazyModule {} +export default class CoreUserCourseLazyModule {} diff --git a/src/core/features/user/user-lazy.module.ts b/src/core/features/user/user-lazy.module.ts index f76ce4e06a7..76284afc73b 100644 --- a/src/core/features/user/user-lazy.module.ts +++ b/src/core/features/user/user-lazy.module.ts @@ -26,7 +26,7 @@ const routes: Routes = [ }, { path: 'profile', - loadChildren: () => import('./user-profile-lazy.module').then( m => m.CoreUserProfileLazyModule), + loadChildren: () => import('./user-profile-lazy.module'), }, { path: 'about', @@ -44,4 +44,4 @@ const routes: Routes = [ CoreUserAboutPage, ], }) -export class CoreUserLazyModule {} +export default class CoreUserLazyModule {} diff --git a/src/core/features/user/user-profile-lazy.module.ts b/src/core/features/user/user-profile-lazy.module.ts index 5a184f5e8b0..c3257aacacf 100644 --- a/src/core/features/user/user-profile-lazy.module.ts +++ b/src/core/features/user/user-profile-lazy.module.ts @@ -34,4 +34,4 @@ const routes: Routes = [ CoreUserProfilePage, ], }) -export class CoreUserProfileLazyModule {} +export default class CoreUserProfileLazyModule {} diff --git a/src/core/features/user/user.module.ts b/src/core/features/user/user.module.ts index b40bce2bb96..1916281ae98 100644 --- a/src/core/features/user/user.module.ts +++ b/src/core/features/user/user.module.ts @@ -34,7 +34,7 @@ import { CoreUserHelper } from './services/user-helper'; import { AppRoutingModule, conditionalRoutes } from '@/app/app-routing.module'; import { CoreScreen } from '@services/screen'; import { CoreEvents } from '@singletons/events'; -import { COURSE_PAGE_NAME, COURSE_INDEX_PATH } from '@features/course/constants'; +import { CORE_COURSE_PAGE_NAME, CORE_COURSE_INDEX_PATH } from '@features/course/constants'; import { PARTICIPANTS_PAGE_NAME } from './constants'; /** @@ -63,19 +63,19 @@ export async function getUsersServices(): Promise[]> { const appRoutes: Routes = [ { path: 'user', - loadChildren: () => import('@features/user/user-app-lazy.module').then(m => m.CoreUserAppLazyModule), + loadChildren: () => import('@features/user/user-app-lazy.module'), }, ]; const routes: Routes = [ { path: 'user', - loadChildren: () => import('@features/user/user-lazy.module').then(m => m.CoreUserLazyModule), + loadChildren: () => import('@features/user/user-lazy.module'), }, ...conditionalRoutes([ { - path: `${COURSE_PAGE_NAME}/${COURSE_INDEX_PATH}/${PARTICIPANTS_PAGE_NAME}/:userId`, - loadChildren: () => import('@features/user/user-profile-lazy.module').then(m => m.CoreUserProfileLazyModule), + path: `${CORE_COURSE_PAGE_NAME}/${CORE_COURSE_INDEX_PATH}/${PARTICIPANTS_PAGE_NAME}/:userId`, + loadChildren: () => import('@features/user/user-profile-lazy.module'), data: { swipeManagerSource: 'participants', }, @@ -86,7 +86,7 @@ const routes: Routes = [ const courseIndexRoutes: Routes = [ { path: PARTICIPANTS_PAGE_NAME, - loadChildren: () => import('@features/user/user-course-lazy.module').then(m => m.CoreUserCourseLazyModule), + loadChildren: () => import('@features/user/user-course-lazy.module'), }, ]; diff --git a/src/core/features/usertours/components/user-tour/user-tour.ts b/src/core/features/usertours/components/user-tour/user-tour.ts index 0d0aacce63b..3b829ef7278 100644 --- a/src/core/features/usertours/components/user-tour/user-tour.ts +++ b/src/core/features/usertours/components/user-tour/user-tour.ts @@ -33,8 +33,8 @@ import { AngularFrameworkDelegate } from '@singletons'; import { CoreDirectivesRegistry } from '@singletons/directives-registry'; import { CoreDom } from '@singletons/dom'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; -import { CoreMainMenuProvider } from '@features/mainmenu/services/mainmenu'; import { COLLAPSIBLE_HEADER_UPDATED } from '@directives/collapsible-header'; +import { MAIN_MENU_VISIBILITY_UPDATED_EVENT } from '@features/mainmenu/constants'; const ANIMATION_DURATION = 200; const USER_TOURS_BACK_BUTTON_PRIORITY = 100; @@ -48,7 +48,7 @@ const BACKDROP_DISMISS_SAFETY_TRESHOLD = 1000; @Component({ selector: 'core-user-tours-user-tour', templateUrl: 'core-user-tours-user-tour.html', - styleUrls: ['user-tour.scss'], + styleUrl: 'user-tour.scss', }) export class CoreUserToursUserTourComponent implements AfterViewInit, OnDestroy { @@ -264,7 +264,7 @@ export class CoreUserToursUserTourComponent implements AfterViewInit, OnDestroy this.collapsibleHeaderListener = this.collapsibleHeaderListener ?? CoreEvents.on(COLLAPSIBLE_HEADER_UPDATED, () => this.calculateStyles()); this.mainMenuListener = this.mainMenuListener ?? - CoreEvents.on(CoreMainMenuProvider.MAIN_MENU_VISIBILITY_UPDATED, () => this.calculateStyles()); + CoreEvents.on(MAIN_MENU_VISIBILITY_UPDATED_EVENT, () => this.calculateStyles()); this.resizeListener = this.resizeListener ?? CoreDom.onWindowResize(() => this.calculateStyles()); this.content = this.content ?? CoreDom.closest(this.focus, 'ion-content'); diff --git a/src/core/features/usertours/services/database/user-tours.ts b/src/core/features/usertours/services/database/user-tours.ts index c2252df0d38..9977a0ae5a2 100644 --- a/src/core/features/usertours/services/database/user-tours.ts +++ b/src/core/features/usertours/services/database/user-tours.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { CoreAppSchema } from '@services/app'; +import { CoreAppSchema } from '@services/app-db'; /** * Database variables for CoreUserTours service. diff --git a/src/core/features/usertours/services/user-tours.ts b/src/core/features/usertours/services/user-tours.ts index 463f7928711..b6537ba9a81 100644 --- a/src/core/features/usertours/services/user-tours.ts +++ b/src/core/features/usertours/services/user-tours.ts @@ -18,13 +18,12 @@ import { Injectable } from '@angular/core'; import { CoreCancellablePromise } from '@classes/cancellable-promise'; import { CoreDatabaseTable } from '@classes/database/database-table'; import { CoreDatabaseCachingStrategy, CoreDatabaseTableProxy } from '@classes/database/database-table-proxy'; -import { CoreApp } from '@services/app'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreAppDB } from '@services/app-db'; import { AngularFrameworkDelegate, makeSingleton } from '@singletons'; import { CoreDirectivesRegistry } from '@singletons/directives-registry'; import { CoreDom } from '@singletons/dom'; import { CoreSubscriptions } from '@singletons/subscriptions'; -import { CoreUserToursUserTourComponent } from '../components/user-tour/user-tour'; +import type { CoreUserToursUserTourComponent } from '../components/user-tour/user-tour'; import { APP_SCHEMA, CoreUserToursDBEntry, USER_TOURS_TABLE_NAME } from './database/user-tours'; import { CorePromisedValue } from '@classes/promised-value'; import { CoreWait } from '@singletons/wait'; @@ -43,12 +42,12 @@ export class CoreUserToursService { * Initialize database. */ async initializeDatabase(): Promise { - await CoreUtils.ignoreErrors(CoreApp.createTablesFromSchema(APP_SCHEMA)); + await CoreAppDB.createTablesFromSchema(APP_SCHEMA); this.table.setLazyConstructor(async () => { const table = new CoreDatabaseTableProxy( { cachingStrategy: CoreDatabaseCachingStrategy.Eager }, - CoreApp.getDB(), + CoreAppDB.getDB(), USER_TOURS_TABLE_NAME, ); @@ -112,6 +111,8 @@ export class CoreUserToursService { protected async show(options: CoreUserToursBasicOptions): Promise; protected async show(options: CoreUserToursFocusedOptions): Promise; protected async show(options: CoreUserToursBasicOptions | CoreUserToursFocusedOptions): Promise { + const { CoreUserToursUserTourComponent } = await import('../components/user-tour/user-tour'); + const { delay, ...componentOptions } = options; await CoreWait.wait(delay ?? 200); diff --git a/src/core/features/xapi/services/offline.ts b/src/core/features/xapi/services/offline.ts index 732fdffd6f8..cf109d70fcd 100644 --- a/src/core/features/xapi/services/offline.ts +++ b/src/core/features/xapi/services/offline.ts @@ -15,7 +15,7 @@ import { Injectable } from '@angular/core'; import { CoreSites } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { makeSingleton } from '@singletons'; import { CoreXAPIStateDBRecord, CoreXAPIStatementDBRecord, STATEMENTS_TABLE_NAME, STATES_TABLE_NAME } from './database/xapi'; import { CoreXAPIStateOptions } from './xapi'; @@ -234,7 +234,7 @@ export class CoreXAPIOfflineProvider { ): Promise { const db = await CoreSites.getSiteDb(options?.siteId); - const storedState = await CoreUtils.ignoreErrors(this.getState(component, itemId, stateId, options)); + const storedState = await CorePromiseUtils.ignoreErrors(this.getState(component, itemId, stateId, options)); if (storedState) { const newData: Partial = { diff --git a/src/core/features/xapi/services/xapi.ts b/src/core/features/xapi/services/xapi.ts index a48ab6b7ed7..4167abd7169 100644 --- a/src/core/features/xapi/services/xapi.ts +++ b/src/core/features/xapi/services/xapi.ts @@ -16,7 +16,7 @@ import { Injectable } from '@angular/core'; import { CoreNetwork } from '@services/network'; import { CoreSites, CoreSitesCommonWSOptions } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreWSError } from '@classes/errors/wserror'; import { CoreSite } from '@classes/sites/site'; import { CoreXAPIOffline, CoreXAPIOfflineSaveStatementsOptions } from './offline'; import { makeSingleton } from '@singletons'; @@ -25,6 +25,7 @@ import { CoreXAPIIRI } from '../classes/iri'; import { CoreError } from '@classes/errors/error'; import { CoreLogger } from '@singletons/logger'; import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site'; +import { CorePromiseUtils } from '@singletons/promise-utils'; export const XAPI_STATE_DELETED = 'STATE_DELETED'; @@ -34,7 +35,7 @@ export const XAPI_STATE_DELETED = 'STATE_DELETED'; @Injectable({ providedIn: 'root' }) export class CoreXAPIProvider { - static readonly ROOT_CACHE_KEY = 'CoreXAPI:'; + protected static readonly ROOT_CACHE_KEY = 'CoreXAPI:'; protected logger = CoreLogger.getInstance('CoreXAPIProvider'); @@ -110,7 +111,7 @@ export class CoreXAPIProvider { if (!isNaN(itemId)) { // Delete offline state if it exists. - await CoreUtils.ignoreErrors(CoreXAPIOffline.deleteStates(component, { + await CorePromiseUtils.ignoreErrors(CoreXAPIOffline.deleteStates(component, { itemId, stateId, registration: options.registration, @@ -120,7 +121,7 @@ export class CoreXAPIProvider { return true; } catch (error) { - if (CoreUtils.isWebServiceError(error)) { + if (CoreWSError.isWebServiceError(error)) { // The WebService has thrown an error, this means that the state cannot be deleted. throw error; } @@ -287,19 +288,6 @@ export class CoreXAPIProvider { return site.write('core_xapi_get_states', data); } - /** - * Get URL for XAPI events. - * - * @param contextId Context ID. - * @param type Type (e.g. 'activity'). - * @param siteId Site ID. If not defined, current site. - * @returns Promise resolved when done. - * @deprecated since 4.2. Use CoreXAPIIRI.generate instead. - */ - async getUrl(contextId: number, type: string, siteId?: string): Promise { - return CoreXAPIIRI.generate(contextId, type, siteId); - } - /** * Invalidates a state. * @@ -359,7 +347,7 @@ export class CoreXAPIProvider { return true; } catch (error) { - if (CoreUtils.isWebServiceError(error)) { + if (CoreWSError.isWebServiceError(error)) { // The WebService has thrown an error, this means that statements cannot be submitted. throw error; } else { @@ -433,7 +421,7 @@ export class CoreXAPIProvider { return true; } catch (error) { - if (CoreUtils.isWebServiceError(error)) { + if (CoreWSError.isWebServiceError(error)) { // The WebService has thrown an error, this means that state cannot be submitted. throw error; } diff --git a/src/core/guards/can-leave.ts b/src/core/guards/can-leave.ts index 527e702706d..62728850e65 100644 --- a/src/core/guards/can-leave.ts +++ b/src/core/guards/can-leave.ts @@ -13,7 +13,7 @@ // limitations under the License. import { CanDeactivateFn } from '@angular/router'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Check if a component implements the canLeave interface. @@ -34,7 +34,7 @@ export const canLeaveGuard: CanDeactivateFn = async (component: unknown return true; } - return CoreUtils.ignoreErrors(component.canLeave(), false); + return CorePromiseUtils.ignoreErrors(component.canLeave(), false); }; export interface CanLeave { diff --git a/src/core/guards/redirect.ts b/src/core/guards/redirect.ts index 55bf4996fa2..3900183a87a 100644 --- a/src/core/guards/redirect.ts +++ b/src/core/guards/redirect.ts @@ -13,7 +13,7 @@ // limitations under the License. import { CanActivateFn } from '@angular/router'; -import { CoreApp } from '@services/app'; +import { CoreRedirects } from '@singletons/redirects'; import { CoreRedirectPayload } from '@services/navigator'; import { CoreSites } from '@services/sites'; import { Router } from '@singletons'; @@ -25,7 +25,7 @@ import { CoreConstants } from '../constants'; * @returns True if there's no redirect, redirection route otherwise. */ export const redirectGuard: CanActivateFn = async () => { - const redirect = CoreApp.consumeMemoryRedirect(); + const redirect = CoreRedirects.consumeMemoryRedirect(); if (!redirect) { return true; } diff --git a/src/core/initializers/app.ts b/src/core/initializers/app.ts index 837caf88971..cd9a1d67ba9 100644 --- a/src/core/initializers/app.ts +++ b/src/core/initializers/app.ts @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +import { CoreApp } from '@services/app'; import { CoreHTMLClasses } from '@singletons/html-classes'; /** @@ -19,4 +20,5 @@ import { CoreHTMLClasses } from '@singletons/html-classes'; */ export default async function(): Promise { CoreHTMLClasses.initialize(); + CoreApp.initialize(); } diff --git a/src/core/initializers/consume-storage-redirect.ts b/src/core/initializers/consume-storage-redirect.ts index 20b776ae33b..48489043e39 100644 --- a/src/core/initializers/consume-storage-redirect.ts +++ b/src/core/initializers/consume-storage-redirect.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { CoreApp } from '@services/app'; +import { CoreRedirects } from '@singletons/redirects'; import { CoreUpdateManager } from '@services/update-manager'; /** @@ -21,5 +21,5 @@ import { CoreUpdateManager } from '@services/update-manager'; export default async function(): Promise { await CoreUpdateManager.donePromise; - CoreApp.consumeStorageRedirect(); + CoreRedirects.consumeStorageRedirect(); } diff --git a/src/core/initializers/initialize-databases.ts b/src/core/initializers/initialize-databases.ts index 1a0eb20ee78..96727a108fd 100644 --- a/src/core/initializers/initialize-databases.ts +++ b/src/core/initializers/initialize-databases.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { CoreApp } from '@services/app'; +import { CoreAppDB } from '@services/app-db'; import { CoreConfig } from '@services/config'; import { CoreCronDelegate } from '@services/cron'; import { CoreFilepool } from '@services/filepool'; @@ -25,7 +25,7 @@ import { CoreStorage } from '@services/storage'; */ export default async function(): Promise { await Promise.all([ - CoreApp.initializeDatabase(), + CoreAppDB.initializeDatabase(), CoreConfig.initializeDatabase(), CoreCronDelegate.initializeDatabase(), CoreFilepool.initializeDatabase(), diff --git a/src/core/initializers/prepare-devtools.ts b/src/core/initializers/prepare-devtools.ts index f2442ebaddd..f584984e459 100644 --- a/src/core/initializers/prepare-devtools.ts +++ b/src/core/initializers/prepare-devtools.ts @@ -13,16 +13,16 @@ // limitations under the License. import { CorePushNotifications, CorePushNotificationsProvider } from '@features/pushnotifications/services/pushnotifications'; -import { CoreApp, CoreAppProvider } from '@services/app'; import { CoreConfig, CoreConfigProvider } from '@services/config'; import { CoreDB, CoreDbProvider } from '@services/db'; import { CoreCustomURLSchemes, CoreCustomURLSchemesProvider } from '@services/urlschemes'; import { CoreBrowser } from '@singletons/browser'; import { CoreConstants } from '../constants'; +import { CoreAppDB, CoreAppDBService } from '@services/app-db'; type DevelopmentWindow = Window & { browser?: typeof CoreBrowser; - appProvider?: CoreAppProvider; + appDBService?: CoreAppDBService; configProvider?: CoreConfigProvider; dbProvider?: CoreDbProvider; urlSchemes?: CoreCustomURLSchemesProvider; @@ -36,7 +36,7 @@ type DevelopmentWindow = Window & { */ function initializeDevelopmentWindow(window: DevelopmentWindow) { window.browser = CoreBrowser; - window.appProvider = CoreApp.instance; + window.appDBService = CoreAppDB.instance; window.configProvider = CoreConfig.instance; window.dbProvider = CoreDB.instance; window.urlSchemes = CoreCustomURLSchemes.instance; diff --git a/src/core/initializers/prepare-inapp-browser.ts b/src/core/initializers/prepare-inapp-browser.ts index f35e1eb2d5b..30ec93d5559 100644 --- a/src/core/initializers/prepare-inapp-browser.ts +++ b/src/core/initializers/prepare-inapp-browser.ts @@ -20,7 +20,7 @@ import { CoreSites } from '@services/sites'; import { CoreCustomURLSchemes } from '@services/urlschemes'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreUrl } from '@singletons/url'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreOpener } from '@singletons/opener'; import { Translate } from '@singletons'; import { CoreEvents } from '@singletons/events'; @@ -43,14 +43,14 @@ export default function(): void { CoreCustomURLSchemes.handleCustomURL(url).catch((error) => { CoreCustomURLSchemes.treatHandleCustomURLError(error); }); - CoreUtils.closeInAppBrowser(); + CoreOpener.closeInAppBrowser(); return; } if (isExternalApp && url.includes('://token=')) { // It's an SSO token for another app. Close the IAB and show an error. - CoreUtils.closeInAppBrowser(); + CoreOpener.closeInAppBrowser(); CoreDomUtils.showErrorModal(new CoreSiteError({ supportConfig: CoreSites.getCurrentSite() ? CoreUserAuthenticatedSupportConfig.forCurrentSite() @@ -68,15 +68,15 @@ export default function(): void { } // Open in browser should launch the right app if found and do nothing if not found. - CoreUtils.openInBrowser(url, { showBrowserWarning: false }); + CoreOpener.openInBrowser(url, { showBrowserWarning: false }); // At this point, URL schemes will stop working in IAB, and in Android the IAB is showing a "Webpage not available" error. // Re-loading the page inside the existing IAB doesn't fix it, we need to re-load the whole IAB. if (lastInAppUrl) { - CoreUtils.openInApp(lastInAppUrl); + CoreOpener.openInApp(lastInAppUrl); } else { // No last URL loaded, close the InAppBrowser. - CoreUtils.closeInAppBrowser(); + CoreOpener.closeInAppBrowser(); } }); diff --git a/src/core/services/app-db.ts b/src/core/services/app-db.ts new file mode 100644 index 00000000000..e420ca5de43 --- /dev/null +++ b/src/core/services/app-db.ts @@ -0,0 +1,188 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Injectable } from '@angular/core'; + +import { CoreDB } from '@services/db'; +import { SQLiteDB, SQLiteDBTableSchema } from '@classes/sqlitedb'; + +import { makeSingleton } from '@singletons'; +import { CoreLogger } from '@singletons/logger'; +import { DBNAME, SCHEMA_VERSIONS_TABLE_NAME, SCHEMA_VERSIONS_TABLE_SCHEMA, SchemaVersionsDBEntry } from '@services/database/app'; +import { CoreDatabaseCachingStrategy, CoreDatabaseTableProxy } from '@classes/database/database-table-proxy'; +import { asyncInstance } from '../utils/async-instance'; +import { CoreDatabaseTable } from '@classes/database/database-table'; + +/** + * Factory to provide access to the global app database. + * + * @description + * Each service or component should be responsible of creating their own database tables. Example: + * + * ```ts + * CoreAppDB.getDB(); + * CoreAppDB.createTableFromSchema(this.tableSchema); + * ``` + */ +@Injectable({ providedIn: 'root' }) +export class CoreAppDBService { + + protected db?: SQLiteDB; + protected logger: CoreLogger; + protected schemaVersionsTable = asyncInstance>(); + + constructor() { + this.logger = CoreLogger.getInstance('CoreAppDB'); + } + + /** + * Initialize database. + */ + async initializeDatabase(): Promise { + const database = this.getDB(); + + await database.createTableFromSchema(SCHEMA_VERSIONS_TABLE_SCHEMA); + + const schemaVersionsTable = new CoreDatabaseTableProxy( + { cachingStrategy: CoreDatabaseCachingStrategy.Eager }, + database, + SCHEMA_VERSIONS_TABLE_NAME, + ['name'], + ); + + await schemaVersionsTable.initialize(); + + this.schemaVersionsTable.setInstance(schemaVersionsTable); + } + + /** + * Install and upgrade a certain schema. + * + * @param schema The schema to create. + */ + async createTablesFromSchema(schema: CoreAppSchema): Promise { + this.logger.debug(`Apply schema to app DB: ${schema.name}`); + + try { + const oldVersion = await this.getInstalledSchemaVersion(schema); + + if (oldVersion >= schema.version) { + // Version already installed, nothing else to do. + return; + } + + this.logger.debug(`Migrating schema '${schema.name}' of app DB from version ${oldVersion} to ${schema.version}`); + + if (schema.tables) { + await this.getDB().createTablesFromSchema(schema.tables); + } + if (schema.install && oldVersion === 0) { + await schema.install(this.getDB()); + } + if (schema.migrate && oldVersion > 0) { + await schema.migrate(this.getDB(), oldVersion); + } + + // Set installed version. + await this.schemaVersionsTable.insert({ name: schema.name, version: schema.version }); + } catch (error) { + // Only log the error, don't throw it. + this.logger.error(`Error applying schema to app DB: ${schema.name}`, error); + } + } + + /** + * Delete table schema. + * + * @param name Schema name. + */ + async deleteTableSchema(name: string): Promise { + await this.schemaVersionsTable.deleteByPrimaryKey({ name }); + } + + /** + * Get the application global database. + * + * @returns App's DB. + */ + getDB(): SQLiteDB { + if (!this.db) { + this.db = CoreDB.getDB(DBNAME); + } + + return this.db; + } + + /** + * Get the installed version for the given schema. + * + * @param schema App schema. + * @returns Installed version number, or 0 if the schema is not installed. + */ + protected async getInstalledSchemaVersion(schema: CoreAppSchema): Promise { + try { + // Fetch installed version of the schema. + const entry = await this.schemaVersionsTable.getOneByPrimaryKey({ name: schema.name }); + + return entry.version; + } catch { + // No installed version yet. + return 0; + } + } + +} + +export const CoreAppDB = makeSingleton(CoreAppDBService); + +/** + * App DB schema and migration function. + */ +export type CoreAppSchema = { + /** + * Name of the schema. + */ + name: string; + + /** + * Latest version of the schema (integer greater than 0). + */ + version: number; + + /** + * Tables to create when installing or upgrading the schema. + */ + tables?: SQLiteDBTableSchema[]; + + /** + * Migrates the schema to the latest version. + * + * Called when upgrading the schema, after creating the defined tables. + * + * @param db The affected DB. + * @param oldVersion Old version of the schema or 0 if not installed. + * @returns Promise resolved when done. + */ + migrate?(db: SQLiteDB, oldVersion: number): Promise; + + /** + * Make changes to install the schema. + * + * Called when installing the schema, after creating the defined tables. + * + * @param db Site database. + * @returns Promise resolved when done. + */ + install?(db: SQLiteDB): Promise | void; +}; diff --git a/src/core/services/app.ts b/src/core/services/app.ts index 88031995fdd..5da40dbb97c 100644 --- a/src/core/services/app.ts +++ b/src/core/services/app.ts @@ -14,62 +14,43 @@ import { Injectable } from '@angular/core'; -import { CoreDB } from '@services/db'; -import { CoreEventObserver, CoreEvents } from '@singletons/events'; -import { SQLiteDB, SQLiteDBTableSchema } from '@classes/sqlitedb'; - +import { CoreAppDB, CoreAppSchema } from './app-db'; +import { CoreEvents } from '@singletons/events'; +import { SQLiteDB } from '@classes/sqlitedb'; import { makeSingleton, StatusBar } from '@singletons'; import { CoreLogger } from '@singletons/logger'; import { CoreColors } from '@singletons/colors'; -import { DBNAME, SCHEMA_VERSIONS_TABLE_NAME, SCHEMA_VERSIONS_TABLE_SCHEMA, SchemaVersionsDBEntry } from '@services/database/app'; -import { CoreObject } from '@singletons/object'; import { CoreRedirectPayload } from './navigator'; -import { CoreDatabaseCachingStrategy, CoreDatabaseTableProxy } from '@classes/database/database-table-proxy'; -import { asyncInstance } from '../utils/async-instance'; -import { CoreDatabaseTable } from '@classes/database/database-table'; import { CorePromisedValue } from '@classes/promised-value'; import { Subscription } from 'rxjs'; import { CorePlatform } from '@services/platform'; -import { CoreMainMenuProvider } from '@features/mainmenu/services/mainmenu'; import { CoreKeyboard } from '@singletons/keyboard'; import { CoreNetwork } from './network'; +import { CoreSSO } from '@singletons/sso'; +import { CoreRedirectData, CoreRedirects } from '@singletons/redirects'; +import { MAIN_MENU_VISIBILITY_UPDATED_EVENT } from '@features/mainmenu/constants'; /** - * Factory to provide some global functionalities, like access to the global app database. - * - * @description - * Each service or component should be responsible of creating their own database tables. Example: - * - * ```ts - * constructor(appProvider: CoreAppProvider) { - * this.appDB = appProvider.getDB(); - * this.appDB.createTableFromSchema(this.tableSchema); - * } - * ``` + * Factory to provide some global functionalities. */ @Injectable({ providedIn: 'root' }) export class CoreAppProvider { - protected db?: SQLiteDB; - protected logger: CoreLogger; - protected ssoAuthenticationDeferred?: CorePromisedValue; - protected redirect?: CoreRedirectData; - protected schemaVersionsTable = asyncInstance>(); - protected mainMenuListener?: CoreEventObserver; - - constructor() { - this.logger = CoreLogger.getInstance('CoreAppProvider'); - if (CorePlatform.isAndroid()) { - this.mainMenuListener = - CoreEvents.on(CoreMainMenuProvider.MAIN_MENU_VISIBILITY_UPDATED, () => this.setAndroidNavigationBarColor()); + protected logger: CoreLogger = CoreLogger.getInstance('CoreApp'); + + initialize(): void { + if (!CorePlatform.isAndroid()) { + return; } + + CoreEvents.on(MAIN_MENU_VISIBILITY_UPDATED_EVENT, () => this.setAndroidNavigationBarColor()); } /** * Returns whether the user agent is controlled by automation. I.e. Behat testing. * - * @deprecated since 4.4. Use CorePlatform.isAutomated() instead. * @returns True if the user agent is controlled by automation, false otherwise. + * @deprecated since 4.4. Use CorePlatform.isAutomated() instead. */ static isAutomated(): boolean { return CorePlatform.isAutomated(); @@ -79,38 +60,18 @@ export class CoreAppProvider { * Returns the forced timezone to use. Timezone is forced for automated tests. * * @returns Timezone. Undefined to use the user's timezone. + * @deprecated since 5.0. Use CoreTime.getForcedTimezone() instead. */ static getForcedTimezone(): string | undefined { - if (CorePlatform.isAutomated()) { - // Use the same timezone forced for LMS in tests. - return 'Australia/Perth'; - } - } - - /** - * Initialize database. - */ - async initializeDatabase(): Promise { - const database = this.getDB(); - - await database.createTableFromSchema(SCHEMA_VERSIONS_TABLE_SCHEMA); - - const schemaVersionsTable = new CoreDatabaseTableProxy( - { cachingStrategy: CoreDatabaseCachingStrategy.Eager }, - database, - SCHEMA_VERSIONS_TABLE_NAME, - ['name'], - ); - - await schemaVersionsTable.initialize(); - - this.schemaVersionsTable.setInstance(schemaVersionsTable); + // Use the same timezone forced for LMS in tests. + return CorePlatform.isAutomated() ? 'Australia/Perth' : undefined; } /** * Check if the browser supports mediaDevices.getUserMedia. * * @returns Whether the function is supported. + * @deprecated since 5.0. Use CoreMedia.canGetUserMedia() instead. */ canGetUserMedia(): boolean { return !!(navigator && navigator.mediaDevices && navigator.mediaDevices.getUserMedia); @@ -120,6 +81,7 @@ export class CoreAppProvider { * Check if the browser supports MediaRecorder. * * @returns Whether the function is supported. + * @deprecated since 5.0. Use CoreMedia.canRecordMedia() instead. */ canRecordMedia(): boolean { return !!window.MediaRecorder; @@ -134,60 +96,6 @@ export class CoreAppProvider { CoreKeyboard.close(); } - /** - * Install and upgrade a certain schema. - * - * @param schema The schema to create. - * @returns Promise resolved when done. - */ - async createTablesFromSchema(schema: CoreAppSchema): Promise { - this.logger.debug(`Apply schema to app DB: ${schema.name}`); - - const oldVersion = await this.getInstalledSchemaVersion(schema); - - if (oldVersion >= schema.version) { - // Version already installed, nothing else to do. - return; - } - - this.logger.debug(`Migrating schema '${schema.name}' of app DB from version ${oldVersion} to ${schema.version}`); - - if (schema.tables) { - await this.getDB().createTablesFromSchema(schema.tables); - } - if (schema.install && oldVersion === 0) { - await schema.install(this.getDB()); - } - if (schema.migrate && oldVersion > 0) { - await schema.migrate(this.getDB(), oldVersion); - } - - // Set installed version. - await this.schemaVersionsTable.insert({ name: schema.name, version: schema.version }); - } - - /** - * Delete table schema. - * - * @param name Schema name. - */ - async deleteTableSchema(name: string): Promise { - await this.schemaVersionsTable.deleteByPrimaryKey({ name }); - } - - /** - * Get the application global database. - * - * @returns App's DB. - */ - getDB(): SQLiteDB { - if (!this.db) { - this.db = CoreDB.getDB(DBNAME); - } - - return this.db; - } - /** * Get app store URL. * @@ -244,9 +152,11 @@ export class CoreAppProvider { * Checks if the current window is wider than a mobile. * * @returns Whether the app the current window is wider than a mobile. + * + * @deprecated since 5.0. Use CorePlatform.isWide() instead. */ isWide(): boolean { - return CorePlatform.width() > 768; + return CorePlatform.isWide(); } /** @@ -310,44 +220,40 @@ export class CoreAppProvider { * Start an SSO authentication process. * Please notice that this function should be called when the app receives the new token from the browser, * NOT when the browser is opened. + * + * @deprecated since 5.0. Use CoreSSO.startSSOAuthentication instead. */ startSSOAuthentication(): void { - this.ssoAuthenticationDeferred = new CorePromisedValue(); - - // Resolve it automatically after 10 seconds (it should never take that long). - const cancelTimeout = setTimeout(() => this.finishSSOAuthentication(), 10000); - - // If the promise is resolved because finishSSOAuthentication is called, stop the cancel promise. - // eslint-disable-next-line promise/catch-or-return - this.ssoAuthenticationDeferred.then(() => clearTimeout(cancelTimeout)); + CoreSSO.startSSOAuthentication(); } /** * Finish an SSO authentication process. + * + * @deprecated since 5.0. Use CoreSSO.finishSSOAuthentication instead. */ finishSSOAuthentication(): void { - if (this.ssoAuthenticationDeferred) { - this.ssoAuthenticationDeferred.resolve(); - this.ssoAuthenticationDeferred = undefined; - } + CoreSSO.finishSSOAuthentication(); } /** * Check if there's an ongoing SSO authentication process. * * @returns Whether there's a SSO authentication ongoing. + * @deprecated since 5.0. Use CoreSSO.isSSOAuthenticationOngoing instead. */ isSSOAuthenticationOngoing(): boolean { - return !!this.ssoAuthenticationDeferred; + return CoreSSO.isSSOAuthenticationOngoing(); } /** * Returns a promise that will be resolved once SSO authentication finishes. * * @returns Promise resolved once SSO authentication finishes. + * @deprecated since 5.0. Use CoreSSO.waitForSSOAuthentication instead. */ async waitForSSOAuthentication(): Promise { - await this.ssoAuthenticationDeferred; + return CoreSSO.waitForSSOAuthentication(); } /** @@ -375,55 +281,25 @@ export class CoreAppProvider { resumeSubscription = CorePlatform.resume.subscribe(stopWaiting); timeoutId = timeout ? window.setTimeout(stopWaiting, timeout) : null; - await deferred; - } + await deferred; } /** * Read redirect data from local storage and clear it if it existed. + * + * @deprecated since 5.0. Use CoreRedirects.consumeStorageRedirect instead. */ consumeStorageRedirect(): void { - if (!localStorage?.getItem) { - return; - } - - try { - // Read data from storage. - const jsonData = localStorage.getItem('CoreRedirect'); - - if (!jsonData) { - return; - } - - // Clear storage. - localStorage.removeItem('CoreRedirect'); - - // Remember redirect data. - const data: CoreRedirectData = JSON.parse(jsonData); - - if (!CoreObject.isEmpty(data)) { - this.redirect = data; - } - } catch (error) { - this.logger.error('Error loading redirect data:', error); - } + CoreRedirects.consumeStorageRedirect(); } /** * Retrieve and forget redirect data. * * @returns Redirect data if any. + * @deprecated since 5.0. Use CoreRedirects.consumeMemoryRedirect instead. */ consumeMemoryRedirect(): CoreRedirectData | null { - const redirect = this.getRedirect(); - - this.forgetRedirect(); - - if (redirect && (!redirect.timemodified || Date.now() - redirect.timemodified > 300000)) { - // Redirect data is only valid for 5 minutes, discard it. - return null; - } - - return redirect; + return CoreRedirects.consumeMemoryRedirect(); } /** @@ -436,18 +312,21 @@ export class CoreAppProvider { /** * Forget redirect data. + * + * @deprecated since 5.0. Use CoreRedirects.forgetRedirect instead. */ forgetRedirect(): void { - delete this.redirect; + CoreRedirects.forgetRedirect(); } /** * Retrieve redirect data. * * @returns Redirect data if any. + * @deprecated since 5.0. Use CoreRedirects.getRedirect instead. */ getRedirect(): CoreRedirectData | null { - return this.redirect || null; + return CoreRedirects.getRedirect(); } /** @@ -455,23 +334,11 @@ export class CoreAppProvider { * * @param siteId Site ID. * @param redirectData Redirect data. + * + * @deprecated since 5.0. Use CoreRedirects.storeRedirect instead. */ storeRedirect(siteId: string, redirectData: CoreRedirectPayload = {}): void { - if (!redirectData.redirectPath && !redirectData.urlToOpen) { - return; - } - - try { - const redirect: CoreRedirectData = { - siteId, - timemodified: Date.now(), - ...redirectData, - }; - - localStorage.setItem('CoreRedirect', JSON.stringify(redirect)); - } catch { - // Ignore errors. - } + CoreRedirects.storeRedirect(siteId, redirectData); } /** @@ -479,8 +346,7 @@ export class CoreAppProvider { */ setSystemUIColors(): void { this.setStatusBarColor(); - this.setAndroidNavigationBarColor(); - } + this.setAndroidNavigationBarColor(); } /** * Set StatusBar color depending on platform. @@ -502,24 +368,6 @@ export class CoreAppProvider { StatusBar.backgroundColorByHexString(color); } - /** - * Get the installed version for the given schema. - * - * @param schema App schema. - * @returns Installed version number, or 0 if the schema is not installed. - */ - protected async getInstalledSchemaVersion(schema: CoreAppSchema): Promise { - try { - // Fetch installed version of the schema. - const entry = await this.schemaVersionsTable.getOneByPrimaryKey({ name: schema.name }); - - return entry.version; - } catch { - // No installed version yet. - return 0; - } - } - /** * Set NavigationBar color for Android * @@ -541,17 +389,48 @@ export class CoreAppProvider { ( window).StatusBar.navigationBackgroundColorByHexString(color); } -} + /** + * Initialize database. + * + * @deprecated since 5.0. Use CoreAppDB.initialize instead. + */ + async initializeDatabase(): Promise { + await CoreAppDB.initializeDatabase(); + } -export const CoreApp = makeSingleton(CoreAppProvider); + /** + * Install and upgrade a certain schema. + * + * @param schema The schema to create. + * @deprecated since 5.0. Use CoreAppDB.createTablesFromSchema instead. + */ + async createTablesFromSchema(schema: CoreAppSchema): Promise { + await CoreAppDB.createTablesFromSchema(schema); -/** - * Data stored for a redirect to another page/site. - */ -export type CoreRedirectData = CoreRedirectPayload & { - siteId?: string; // ID of the site to load. - timemodified?: number; // Timestamp when this redirect was last modified. -}; + } + + /** + * Delete table schema. + * + * @param name Schema name. + * @deprecated since 5.0. Use CoreAppDB.deleteTableSchema instead. + */ + async deleteTableSchema(name: string): Promise { + await CoreAppDB.deleteTableSchema(name); + } + + /** + * Get the application global database. + * + * @returns App's DB. + * @deprecated since 5.0. Use CoreAppDB.getDB instead. + */ + getDB(): SQLiteDB { + return CoreAppDB.getDB(); + } + +} +export const CoreApp = makeSingleton(CoreAppProvider); /** * Store config data. @@ -577,44 +456,3 @@ export type CoreStoreConfig = { */ default?: string; }; - -/** - * App DB schema and migration function. - */ -export type CoreAppSchema = { - /** - * Name of the schema. - */ - name: string; - - /** - * Latest version of the schema (integer greater than 0). - */ - version: number; - - /** - * Tables to create when installing or upgrading the schema. - */ - tables?: SQLiteDBTableSchema[]; - - /** - * Migrates the schema to the latest version. - * - * Called when upgrading the schema, after creating the defined tables. - * - * @param db The affected DB. - * @param oldVersion Old version of the schema or 0 if not installed. - * @returns Promise resolved when done. - */ - migrate?(db: SQLiteDB, oldVersion: number): Promise; - - /** - * Make changes to install the schema. - * - * Called when installing the schema, after creating the defined tables. - * - * @param db Site database. - * @returns Promise resolved when done. - */ - install?(db: SQLiteDB): Promise | void; -}; diff --git a/src/core/services/config.ts b/src/core/services/config.ts index 7546785845f..aa27a80f2f3 100644 --- a/src/core/services/config.ts +++ b/src/core/services/config.ts @@ -15,7 +15,7 @@ import { EnvironmentConfig } from '@/types/config'; import { Injectable } from '@angular/core'; import { CoreDatabaseCachingStrategy, CoreDatabaseTableProxy } from '@classes/database/database-table-proxy'; -import { CoreApp } from '@services/app'; +import { CoreAppDB } from './app-db'; import { APP_SCHEMA, ConfigDBEntry, CONFIG_TABLE_NAME } from '@services/database/config'; import { makeSingleton } from '@singletons'; import { CoreConstants } from '../constants'; @@ -73,15 +73,11 @@ export class CoreConfigProvider { * Initialize database. */ async initializeDatabase(): Promise { - try { - await CoreApp.createTablesFromSchema(APP_SCHEMA); - } catch { - // Ignore errors. - } + await CoreAppDB.createTablesFromSchema(APP_SCHEMA); const table = new CoreDatabaseTableProxy( { cachingStrategy: CoreDatabaseCachingStrategy.Eager }, - CoreApp.getDB(), + CoreAppDB.getDB(), CONFIG_TABLE_NAME, ['name'], ); @@ -129,7 +125,7 @@ export class CoreConfigProvider { * @returns Resolves upon success along with the config data. Reject on failure. */ async getFromDB(name: string): Promise { - const db = CoreApp.getDB(); + const db = CoreAppDB.getDB(); const record = await db.getRecord(CONFIG_TABLE_NAME, { name }); return record.value; diff --git a/src/core/services/cron.ts b/src/core/services/cron.ts index 01c3505b918..1940b19508a 100644 --- a/src/core/services/cron.ts +++ b/src/core/services/cron.ts @@ -14,10 +14,10 @@ import { Injectable } from '@angular/core'; -import { CoreApp } from '@services/app'; +import { CoreAppDB } from '@services/app-db'; import { CoreNetwork } from '@services/network'; import { CoreConfig } from '@services/config'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreConstants } from '@/core/constants'; import { CoreError } from '@classes/errors/error'; @@ -52,15 +52,11 @@ export class CoreCronDelegateService { * Initialize database. */ async initializeDatabase(): Promise { - try { - await CoreApp.createTablesFromSchema(APP_SCHEMA); - } catch { - // Ignore errors. - } + await CoreAppDB.createTablesFromSchema(APP_SCHEMA); const table = new CoreDatabaseTableProxy( { cachingStrategy: CoreDatabaseCachingStrategy.Eager }, - CoreApp.getDB(), + CoreAppDB.getDB(), CRON_TABLE_NAME, ); @@ -113,13 +109,13 @@ export class CoreCronDelegateService { } // Add the execution to the queue. - this.queuePromise = CoreUtils.ignoreErrors(this.queuePromise).then(async () => { + this.queuePromise = CorePromiseUtils.ignoreErrors(this.queuePromise).then(async () => { try { await this.executeHandler(name, force, siteId); this.logger.debug(`Cron job '${name}' was successfully executed.`); - await CoreUtils.ignoreErrors(this.setHandlerLastExecutionTime(name, Date.now())); + await CorePromiseUtils.ignoreErrors(this.setHandlerLastExecutionTime(name, Date.now())); this.scheduleNextExecution(name); @@ -151,7 +147,7 @@ export class CoreCronDelegateService { // Wrap the call in Promise.resolve to make sure it's a promise. const promise = Promise.resolve(this.handlers[name].execute?.(siteId, force)); - await CoreUtils.timeoutPromise(promise, CoreCronDelegateService.MAX_TIME_PROCESS); + await CorePromiseUtils.timeoutPromise(promise, CoreCronDelegateService.MAX_TIME_PROCESS); } catch (error) { if (error.timeout) { // The handler took too long. Resolve because we don't want to retry soon. @@ -181,7 +177,7 @@ export class CoreCronDelegateService { } } - await CoreUtils.allPromises(promises); + await CorePromiseUtils.allPromises(promises); } /** @@ -377,7 +373,7 @@ export class CoreCronDelegateService { this.handlers[name].timeout = window.setTimeout(() => { delete this.handlers[name].timeout; - CoreUtils.ignoreErrors(this.checkAndExecuteHandler(name)); + CorePromiseUtils.ignoreErrors(this.checkAndExecuteHandler(name)); }, timeToNextExecution); } diff --git a/src/core/services/database/config.ts b/src/core/services/database/config.ts index b441dc78ede..37364a8f2cf 100644 --- a/src/core/services/database/config.ts +++ b/src/core/services/database/config.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { CoreAppSchema } from '@services/app'; +import { CoreAppSchema } from '@services/app-db'; /** * Database variables for for CoreConfig service. diff --git a/src/core/services/database/cron.ts b/src/core/services/database/cron.ts index d96d2aba1fb..cc6b6b2816d 100644 --- a/src/core/services/database/cron.ts +++ b/src/core/services/database/cron.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { CoreAppSchema } from '@services/app'; +import { CoreAppSchema } from '@services/app-db'; /** * Database variables for CoreCron service. diff --git a/src/core/services/database/filepool.ts b/src/core/services/database/filepool.ts index 52c701e4b6c..d896865eae5 100644 --- a/src/core/services/database/filepool.ts +++ b/src/core/services/database/filepool.ts @@ -13,7 +13,7 @@ // limitations under the License. import { DownloadStatus } from '@/core/constants'; -import { CoreAppSchema } from '@services/app'; +import { CoreAppSchema } from '@services/app-db'; import { CoreSiteSchema } from '@services/sites'; /** diff --git a/src/core/services/database/local-notifications.ts b/src/core/services/database/local-notifications.ts index 80326c6f383..d28a4324d2b 100644 --- a/src/core/services/database/local-notifications.ts +++ b/src/core/services/database/local-notifications.ts @@ -13,7 +13,7 @@ // limitations under the License. import { CorePromisedValue } from '@classes/promised-value'; -import { CoreAppSchema } from '@services/app'; +import { CoreAppSchema } from '@services/app-db'; /** * Database variables for CoreLocalNotifications service. diff --git a/src/core/services/database/sites.ts b/src/core/services/database/sites.ts index 8f5bb55b1f6..6dcd0df53b8 100644 --- a/src/core/services/database/sites.ts +++ b/src/core/services/database/sites.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { CoreAppSchema } from '@services/app'; +import { CoreAppSchema } from '@services/app-db'; import { CoreSiteSchema } from '@services/sites'; import { SQLiteDB, SQLiteDBTableSchema } from '@classes/sqlitedb'; diff --git a/src/core/services/database/storage.ts b/src/core/services/database/storage.ts index 6744b673da7..dd9a248a160 100644 --- a/src/core/services/database/storage.ts +++ b/src/core/services/database/storage.ts @@ -13,7 +13,7 @@ // limitations under the License. import { SQLiteDBTableSchema } from '@classes/sqlitedb'; -import { CoreAppSchema } from '@services/app'; +import { CoreAppSchema } from '@services/app-db'; import { CoreSiteSchema } from '@services/sites'; export const TABLE_NAME = 'core_storage'; diff --git a/src/core/services/error-helper.ts b/src/core/services/error-helper.ts index daadb2382dc..3d82a767262 100644 --- a/src/core/services/error-helper.ts +++ b/src/core/services/error-helper.ts @@ -16,6 +16,7 @@ import { Injectable } from '@angular/core'; import { CoreAnyError, CoreError } from '@classes/errors/error'; import { makeSingleton, Translate } from '@singletons'; import { AlertButton } from '@ionic/angular'; +import { CoreWSError } from '@classes/errors/wserror'; /** * Provider to provide some helper functions regarding files and packages. @@ -23,6 +24,24 @@ import { AlertButton } from '@ionic/angular'; @Injectable({ providedIn: 'root' }) export class CoreErrorHelperService { + /** + * Given an error, add an extra warning to the error message and return the new error message. + * + * @param error Error object or message. + * @param defaultError Message to show if the error is not a string. + * @returns New error message. + */ + addDataNotDownloadedError(error: Error | string, defaultError?: string): string { + const errorMessage = CoreErrorHelper.getErrorMessageFromError(error) || defaultError || ''; + + if (CoreWSError.isWebServiceError(error)) { + return errorMessage; + } + + // Local error. Add an extra warning. + return errorMessage + '

' + Translate.instant('core.errorsomedatanotdownloaded'); + } + /** * Add some text to an error message. * @@ -158,6 +177,17 @@ export class CoreErrorHelperService { return element?.innerText.trim() ?? ''; } + /** + * Log an unhandled error. + * + * @param message Message to contextualize the error. + * @param error Error to log. + */ + logUnhandledError(message: string, error: unknown): void { + // eslint-disable-next-line no-console + console.error('Unhandled error: '+message, error); + } + } export const CoreErrorHelper = makeSingleton(CoreErrorHelperService); diff --git a/src/core/services/file-helper.ts b/src/core/services/file-helper.ts index 29b2aa70657..9dd331ad796 100644 --- a/src/core/services/file-helper.ts +++ b/src/core/services/file-helper.ts @@ -17,12 +17,13 @@ import { FileEntry } from '@awesome-cordova-plugins/file/ngx'; import { CoreNetwork } from '@services/network'; import { CoreFile } from '@services/file'; +import { CoreFileUtils } from '@singletons/file-utils'; import { CoreFilepool } from '@services/filepool'; import { CoreSites } from '@services/sites'; import { CoreWS, CoreWSFile } from '@services/ws'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreUrl } from '@singletons/url'; -import { CoreUtils, CoreUtilsOpenFileOptions, OpenFileAction } from '@services/utils/utils'; +import { CoreOpener, CoreOpenerOpenFileOptions, OpenFileAction } from '@singletons/opener'; import { CoreConstants, DownloadStatus } from '@/core/constants'; import { CoreError } from '@classes/errors/error'; import { makeSingleton, Translate } from '@singletons'; @@ -68,7 +69,7 @@ export class CoreFileHelperProvider { state?: DownloadStatus, onProgress?: CoreFileHelperOnProgress, siteId?: string, - options: CoreUtilsOpenFileOptions = {}, + options: CoreOpenerOpenFileOptions = {}, ): Promise { siteId = siteId || CoreSites.getCurrentSiteId(); @@ -101,7 +102,7 @@ export class CoreFileHelperProvider { url = url + '#moodlemobile-embedded'; try { - await CoreUtils.openOnlineFile(url); + await CoreOpener.openOnlineFile(url); return; } catch (error) { @@ -129,7 +130,7 @@ export class CoreFileHelperProvider { } } - return CoreUtils.openFile(url, options); + return CoreOpener.openFile(url, options); } /** @@ -155,7 +156,7 @@ export class CoreFileHelperProvider { state?: DownloadStatus, onProgress?: CoreFileHelperOnProgress, siteId?: string, - options: CoreUtilsOpenFileOptions = {}, + options: CoreOpenerOpenFileOptions = {}, ): Promise { siteId = siteId || CoreSites.getCurrentSiteId(); @@ -489,7 +490,7 @@ export class CoreFileHelperProvider { * @returns The file name. */ getFilenameFromPath(file: CoreFileEntry): string | undefined { - const path = CoreUtils.isFileEntry(file) ? file.fullPath : file.filepath; + const path = CoreFileUtils.isFileEntry(file) ? file.fullPath : file.filepath; if (path === undefined || path.length == 0) { return; diff --git a/src/core/services/file.ts b/src/core/services/file.ts index 07c11679ebe..9e3f4f19bae 100644 --- a/src/core/services/file.ts +++ b/src/core/services/file.ts @@ -17,7 +17,7 @@ import { Injectable } from '@angular/core'; import { FileEntry, DirectoryEntry, Entry, Metadata, IFile } from '@awesome-cordova-plugins/file/ngx'; import { CoreMimetypeUtils } from '@services/utils/mimetype'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreFileUtils } from '@singletons/file-utils'; import { CoreConstants } from '@/core/constants'; import { CoreError } from '@classes/errors/error'; @@ -29,6 +29,7 @@ import { CorePlatform } from '@services/platform'; import { CorePath } from '@singletons/path'; import { Zip } from '@features/native/plugins'; import { CoreUrl } from '@singletons/url'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Progress event used when writing a file data into a file. @@ -79,15 +80,11 @@ export class CoreFileProvider { static readonly CHUNK_SIZE = 1048576; // 1 MB. Same chunk size as Ionic Native. - protected logger: CoreLogger; + protected logger = CoreLogger.getInstance('CoreFileProvider'); protected initialized = false; protected basePath = ''; protected isHTMLAPI = false; - constructor() { - this.logger = CoreLogger.getInstance('CoreFileProvider'); - } - /** * Sets basePath to use with HTML API. Reserved for core use. * @@ -437,11 +434,11 @@ export class CoreFileProvider { * * @param filename The file name. * @returns The file name normalized. + * + * @deprecated since 5.0. Not used anymore. */ normalizeFileName(filename: string): string { - filename = CoreUrl.decodeURIComponent(filename); - - return filename; + return CoreUrl.decodeURIComponent(filename); } /** @@ -819,7 +816,7 @@ export class CoreFileProvider { from = this.removeBasePath(from); to = this.removeBasePath(to); - const toFileAndDir = this.getFileAndDirectoryFromPath(to); + const toFileAndDir = CoreFileUtils.getFileAndDirectoryFromPath(to); if (toFileAndDir.directory && !destDirExists) { // Create the target directory if it doesn't exist. @@ -850,23 +847,10 @@ export class CoreFileProvider { * * @param path Path to be extracted. * @returns Plain object containing the file name and directory. - * @description - * file.pdf -> directory: '', name: 'file.pdf' - * /file.pdf -> directory: '', name: 'file.pdf' - * path/file.pdf -> directory: 'path', name: 'file.pdf' - * path/ -> directory: 'path', name: '' - * path -> directory: '', name: 'path' + * @deprecated since 5.0. Use CoreFileUtils.getFileAndDirectoryFromPath instead. */ getFileAndDirectoryFromPath(path: string): {directory: string; name: string} { - const file = { - directory: '', - name: '', - }; - - file.directory = path.substring(0, path.lastIndexOf('/')); - file.name = path.substring(path.lastIndexOf('/') + 1); - - return file; + return CoreFileUtils.getFileAndDirectoryFromPath(path); } /** @@ -947,7 +931,7 @@ export class CoreFileProvider { if (destFolder && recreateDir) { // Make sure the dest dir doesn't exist already. - await CoreUtils.ignoreErrors(this.removeDir(destFolder)); + await CorePromiseUtils.ignoreErrors(this.removeDir(destFolder)); // Now create the dir, otherwise if any of the ancestor dirs doesn't exist the unzip would fail. await this.createDir(destFolder); @@ -1027,7 +1011,7 @@ export class CoreFileProvider { const fileEntry = await this.getExternalFile(from); // Create the destination dir if it doesn't exist. - const dirAndFile = this.getFileAndDirectoryFromPath(to); + const dirAndFile = CoreFileUtils.getFileAndDirectoryFromPath(to); const dirEntry = await this.createDir(dirAndFile.directory); @@ -1138,7 +1122,7 @@ export class CoreFileProvider { */ async clearTmpFolder(): Promise { // Ignore errors because the folder might not exist. - await CoreUtils.ignoreErrors(this.removeDir(CoreFileProvider.TMPFOLDER)); + await CorePromiseUtils.ignoreErrors(this.removeDir(CoreFileProvider.TMPFOLDER)); } /** @@ -1162,7 +1146,7 @@ export class CoreFileProvider { if (file.isDirectory) { if (!existingSiteNames.includes(file.name)) { // Site does not exist... delete it. - await CoreUtils.ignoreErrors(this.removeDir(this.getSiteFolder(file.name))); + await CorePromiseUtils.ignoreErrors(this.removeDir(this.getSiteFolder(file.name))); } } }); @@ -1303,7 +1287,7 @@ export class CoreFileProvider { * @returns The file name. */ getFileName(file: CoreFileEntry): string | undefined { - return CoreUtils.isFileEntry(file) ? file.name : file.filename; + return CoreFileUtils.isFileEntry(file) ? file.name : file.filename; } } diff --git a/src/core/services/filepool.ts b/src/core/services/filepool.ts index e0417317fae..5151214d28f 100644 --- a/src/core/services/filepool.ts +++ b/src/core/services/filepool.ts @@ -15,7 +15,7 @@ import { Injectable } from '@angular/core'; import { Md5 } from 'ts-md5/dist/md5'; -import { CoreApp } from '@services/app'; +import { CoreAppDB } from '@services/app-db'; import { CoreNetwork } from '@services/network'; import { CoreEventPackageStatusChanged, CoreEvents } from '@singletons/events'; import { CoreFile } from '@services/file'; @@ -27,7 +27,7 @@ import { CoreMimetypeUtils } from '@services/utils/mimetype'; import { CoreText } from '@singletons/text'; import { CoreTimeUtils } from '@services/utils/time'; import { CoreUrl, CoreUrlPartNames } from '@singletons/url'; -import { CoreUtils, CoreUtilsOpenFileOptions } from '@services/utils/utils'; +import { CoreArray } from '@singletons/array'; import { CoreError } from '@classes/errors/error'; import { DownloadStatus } from '@/core/constants'; import { ApplicationInit, makeSingleton, NgZone, Translate } from '@singletons'; @@ -59,6 +59,8 @@ import { CorePath } from '@singletons/path'; import { CorePromisedValue } from '@classes/promised-value'; import { CoreAnalytics, CoreAnalyticsEventType } from './analytics'; import { convertTextToHTMLElement } from '../utils/create-html-element'; +import { CorePromiseUtils } from '@singletons/promise-utils'; +import { CoreOpener, CoreOpenerOpenFileOptions } from '@singletons/opener'; /* * Factory for handling downloading files and retrieve downloaded files. @@ -175,15 +177,11 @@ export class CoreFilepoolProvider { * Initialize database. */ async initializeDatabase(): Promise { - try { - await CoreApp.createTablesFromSchema(APP_SCHEMA); - } catch (e) { - // Ignore errors. - } + await CoreAppDB.createTablesFromSchema(APP_SCHEMA); const queueTable = new CoreDatabaseTableProxy( { cachingStrategy: CoreDatabaseCachingStrategy.Lazy }, - CoreApp.getDB(), + CoreAppDB.getDB(), QUEUE_TABLE_NAME, [...QUEUE_TABLE_PRIMARY_KEYS], ); @@ -577,7 +575,7 @@ export class CoreFilepoolProvider { if (timemodified > 0 && !entry.timemodified) { // Entry is not outdated but it doesn't have timemodified. Update it. - await CoreUtils.ignoreErrors(this.filesTables[siteId].update({ timemodified }, { fileId: entry.fileId })); + await CorePromiseUtils.ignoreErrors(this.filesTables[siteId].update({ timemodified }, { fileId: entry.fileId })); entry.timemodified = timemodified; } @@ -652,7 +650,7 @@ export class CoreFilepoolProvider { ]); // Notify now. - const filesLinksMap = CoreUtils.arrayToObjectMultiple(filesLinks, 'fileId'); + const filesLinksMap = CoreArray.toObjectMultiple(filesLinks, 'fileId'); filesEntries.forEach(entry => this.notifyFileDeleted(siteId, entry.fileId, filesLinksMap[entry.fileId] || [])); } @@ -887,7 +885,7 @@ export class CoreFilepoolProvider { } }); - return CoreUtils.allPromises(promises); + return CorePromiseUtils.allPromises(promises); } /** @@ -1092,7 +1090,7 @@ export class CoreFilepoolProvider { const finishSuccessfulDownload = (url: string): string => { if (component !== undefined) { - CoreUtils.ignoreErrors(this.addFileLink(siteId, fileId, component, componentId)); + CorePromiseUtils.ignoreErrors(this.addFileLink(siteId, fileId, component, componentId)); } if (!alreadyDownloaded) { @@ -1632,7 +1630,7 @@ export class CoreFilepoolProvider { ): Promise { const addToQueue = (fileUrl: string): void => { // Add the file to queue if needed and ignore errors. - CoreUtils.ignoreErrors(this.addToQueueIfNeeded( + CorePromiseUtils.ignoreErrors(this.addToQueueIfNeeded( siteId, fileUrl, component, @@ -2270,7 +2268,7 @@ export class CoreFilepoolProvider { await Promise.all([ this.filesTables[siteId].update({ fileId: newFileId }, { fileId: buggedFileId }), - CoreUtils.ignoreErrors(this.linksTables[siteId].update({ fileId: newFileId }, { fileId: buggedFileId })), + CorePromiseUtils.ignoreErrors(this.linksTables[siteId].update({ fileId: newFileId }, { fileId: buggedFileId })), ]); fileEntry.fileId = newFileId; @@ -2718,11 +2716,11 @@ export class CoreFilepoolProvider { await this.downloadForPoolByUrl(siteId, fileUrl, options, filePath, onProgress, entry); // Success, we add links and remove from queue. - CoreUtils.ignoreErrors(this.addFileLinks(siteId, fileId, links)); + CorePromiseUtils.ignoreErrors(this.addFileLinks(siteId, fileId, links)); // Wait for the item to be removed from queue before resolving the promise. // If the item could not be removed from queue we still resolve the promise. - await CoreUtils.ignoreErrors(this.removeFromQueue(siteId, fileId)); + await CorePromiseUtils.ignoreErrors(this.removeFromQueue(siteId, fileId)); this.treatQueueDeferred(siteId, fileId, true); this.notifyFileDownloaded(siteId, fileId, links); @@ -2765,7 +2763,7 @@ export class CoreFilepoolProvider { if (dropFromQueue) { this.logger.debug('Item dropped from queue due to error: ' + fileUrl, errorObject); - await CoreUtils.ignoreErrors(this.removeFromQueue(siteId, fileId)); + await CorePromiseUtils.ignoreErrors(this.removeFromQueue(siteId, fileId)); this.treatQueueDeferred(siteId, fileId, false, errorMessage); this.notifyFileDownloadError(siteId, fileId, links); @@ -2844,7 +2842,7 @@ export class CoreFilepoolProvider { this.notifyFileDeleted(siteId, fileId, links); if (fileUrl) { - await CoreUtils.ignoreErrors(CorePluginFileDelegate.fileDeleted(fileUrl, path, siteId)); + await CorePromiseUtils.ignoreErrors(CorePluginFileDelegate.fileDeleted(fileUrl, path, siteId)); } } @@ -2952,18 +2950,18 @@ export class CoreFilepoolProvider { * - The file cannot be streamed. * If the file is big and can be streamed, the promise returned by this function will be rejected. */ - async shouldDownloadFileBeforeOpen(url: string, size: number, options: CoreUtilsOpenFileOptions = {}): Promise { + async shouldDownloadFileBeforeOpen(url: string, size: number, options: CoreOpenerOpenFileOptions = {}): Promise { if (size >= 0 && size <= CoreFilepoolProvider.DOWNLOAD_THRESHOLD) { // The file is small, download it. return true; } - if (CoreUtils.shouldOpenWithDialog(options)) { + if (CoreOpener.shouldOpenWithDialog(options)) { // Open with dialog needs a local file. return true; } - const mimetype = await CoreUtils.getMimeTypeFromUrl(url); + const mimetype = await CoreMimetypeUtils.getMimeTypeFromUrl(url); // If the file is streaming (audio or video), return false. return !CoreMimetypeUtils.isStreamedMimetype(mimetype); diff --git a/src/core/services/geolocation.ts b/src/core/services/geolocation.ts index 29f2600d0ea..e14b37acb85 100644 --- a/src/core/services/geolocation.ts +++ b/src/core/services/geolocation.ts @@ -18,7 +18,7 @@ import { Coordinates } from '@awesome-cordova-plugins/geolocation'; import { CoreApp } from '@services/app'; import { CoreAnyError, CoreError } from '@classes/errors/error'; import { Geolocation, makeSingleton } from '@singletons'; -import { CoreUtils } from './utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; import { CorePlatform } from './platform'; import { CoreSilentError } from '@classes/errors/silenterror'; import { CoreSubscriptions } from '@singletons/subscriptions'; @@ -154,7 +154,7 @@ export class CoreGeolocationProvider { */ protected isCordovaPermissionDeniedError(error?: CoreAnyError | GeolocationPositionError): boolean { return !!error && - typeof error == 'object' && + typeof error === 'object' && 'code' in error && 'PERMISSION_DENIED' in error && error.code === error.PERMISSION_DENIED; @@ -169,7 +169,7 @@ export class CoreGeolocationProvider { const diagnostic = CoreNative.plugin('diagnostic'); if (diagnostic) { - return CoreUtils.promiseWorks(diagnostic.getLocationAuthorizationStatus()); + return CorePromiseUtils.promiseWorks(diagnostic.getLocationAuthorizationStatus()); } return false; diff --git a/src/core/services/groups.ts b/src/core/services/groups.ts index 960ae0d865d..b2c6ef5c1e9 100644 --- a/src/core/services/groups.ts +++ b/src/core/services/groups.ts @@ -15,12 +15,12 @@ import { Injectable } from '@angular/core'; import { CoreSites } from '@services/sites'; -import { CoreSite } from '@classes/sites/site'; import { CoreError } from '@classes/errors/error'; import { makeSingleton, Translate } from '@singletons'; import { CoreWSExternalWarning } from '@services/ws'; import { CoreCourses } from '@features/courses/services/courses'; import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site'; +import { CoreCacheUpdateFrequency } from '../constants'; const ROOT_CACHE_KEY = 'mmGroups:'; @@ -78,7 +78,7 @@ export class CoreGroupsProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getActivityAllowedGroupsCacheKey(cmId, userId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, }; if (ignoreCache) { @@ -202,7 +202,7 @@ export class CoreGroupsProvider { }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getActivityGroupModeCacheKey(cmId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, }; if (ignoreCache) { @@ -285,7 +285,7 @@ export class CoreGroupsProvider { }; const preSets = { cacheKey: this.getUserGroupsInCourseCacheKey(courseId, userId), - updateFrequency: CoreSite.FREQUENCY_RARELY, + updateFrequency: CoreCacheUpdateFrequency.RARELY, }; const response: CoreGroupGetCourseUserGroupsWSResponse = diff --git a/src/core/services/handlers/site-info-cron.ts b/src/core/services/handlers/site-info-cron.ts index e751a61c0ef..f27b2a57173 100644 --- a/src/core/services/handlers/site-info-cron.ts +++ b/src/core/services/handlers/site-info-cron.ts @@ -26,11 +26,7 @@ export class CoreSiteInfoCronHandlerService implements CoreCronHandler { name = 'CoreSiteInfoCronHandler'; /** - * Execute the process. - * Receives the ID of the site affected, undefined for all sites. - * - * @param siteId ID of the site affected, undefined for all sites. - * @returns Promise resolved when done, rejected on failure. + * @inheritdoc */ async execute(siteId?: string): Promise { if (!siteId) { @@ -43,18 +39,14 @@ export class CoreSiteInfoCronHandlerService implements CoreCronHandler { } /** - * Returns handler's interval in milliseconds. Defaults to CoreCronDelegateService.DEFAULT_INTERVAL. - * - * @returns Interval time (in milliseconds). + * @inheritdoc */ getInterval(): number { return 10800000; // 3 hours. } /** - * Check whether it's a synchronization process or not. True if not defined. - * - * @returns Whether it's a synchronization process or not. + * @inheritdoc */ isSync(): boolean { return false; diff --git a/src/core/services/lang.ts b/src/core/services/lang.ts index 21bea66baf0..822ffdfce9e 100644 --- a/src/core/services/lang.ts +++ b/src/core/services/lang.ts @@ -23,8 +23,6 @@ import { makeSingleton, Translate, Http } from '@singletons'; import moment from 'moment-timezone'; import { CoreSite } from '../classes/sites/site'; import { CorePlatform } from '@services/platform'; -import { AddonFilterMultilangHandler } from '@addons/filter/multilang/services/handlers/multilang'; -import { AddonFilterMultilang2Handler } from '@addons/filter/multilang2/services/handlers/multilang2'; import { firstValueFrom } from 'rxjs'; import { CoreLogger } from '@singletons/logger'; import { CoreSites } from './sites'; @@ -547,6 +545,9 @@ export class CoreLangProvider { * @returns Filtered string. */ async filterMultilang(text: string): Promise { + const { AddonFilterMultilangHandler } = await import('@addons/filter/multilang/services/handlers/multilang'); + const { AddonFilterMultilang2Handler } = await import('@addons/filter/multilang2/services/handlers/multilang2'); + return Promise.resolve(text) .then(text => AddonFilterMultilangHandler.filter(text)) .then(text => AddonFilterMultilang2Handler.filter(text)); diff --git a/src/core/services/local-notifications.ts b/src/core/services/local-notifications.ts index 06daa1eeafa..f566855a8ec 100644 --- a/src/core/services/local-notifications.ts +++ b/src/core/services/local-notifications.ts @@ -16,7 +16,7 @@ import { Injectable } from '@angular/core'; import { Subject, Subscription } from 'rxjs'; import { ILocalNotification } from '@awesome-cordova-plugins/local-notifications'; -import { CoreApp } from '@services/app'; +import { CoreAppDB } from '@services/app-db'; import { CoreConfig } from '@services/config'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreText } from '@singletons/text'; @@ -185,13 +185,9 @@ export class CoreLocalNotificationsProvider { * Initialize database. */ async initializeDatabase(): Promise { - try { - await CoreApp.createTablesFromSchema(APP_SCHEMA); - } catch { - // Ignore errors. - } + await CoreAppDB.createTablesFromSchema(APP_SCHEMA); - const database = CoreApp.getDB(); + const database = CoreAppDB.getDB(); const sitesTable = new CoreDatabaseTableProxy( { cachingStrategy: CoreDatabaseCachingStrategy.None }, database, @@ -294,7 +290,7 @@ export class CoreLocalNotificationsProvider { scheduled.forEach((notif) => { notif.data = this.parseNotificationData(notif.data); - if (notif.id && typeof notif.data == 'object' && notif.data.siteId === siteId) { + if (notif.id && typeof notif.data === 'object' && notif.data.siteId === siteId) { ids.push(notif.id); } }); @@ -567,7 +563,7 @@ export class CoreLocalNotificationsProvider { try { // Check if request is valid. - if (typeof request != 'object' || request.table === undefined || request.id === undefined) { + if (typeof request !== 'object' || request.table === undefined || request.id === undefined) { return; } diff --git a/src/core/services/navigator.ts b/src/core/services/navigator.ts index caf6e364246..096aa1956bc 100644 --- a/src/core/services/navigator.ts +++ b/src/core/services/navigator.ts @@ -21,7 +21,7 @@ import { CoreConstants } from '@/core/constants'; import { CoreMainMenu } from '@features/mainmenu/services/mainmenu'; import { CoreObject } from '@singletons/object'; import { CoreSites } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { CoreUrl, CoreUrlPartNames } from '@singletons/url'; import { CoreText } from '@singletons/text'; import { makeSingleton, NavController, Router } from '@singletons'; @@ -33,6 +33,7 @@ import { filter } from 'rxjs/operators'; import { CorePromisedValue } from '@classes/promised-value'; import { BehaviorSubject } from 'rxjs'; import { CoreLoadings } from './loadings'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Redirect payload. @@ -460,7 +461,7 @@ export class CoreNavigatorService { return route; } - if (routeData && CoreUtils.basicLeftCompare(routeData, this.getRouteData(route), 3)) { + if (routeData && CoreObject.basicLeftCompare(routeData, this.getRouteData(route), 3)) { return route; } @@ -549,7 +550,7 @@ export class CoreNavigatorService { const currentMainMenuTab = this.getCurrentMainMenuTab(); const isMainMenuTab = pathRoot === currentMainMenuTab || (!currentMainMenuTab && path === this.getLandingTabPage()) || - await CoreUtils.ignoreErrors(CoreMainMenu.isMainMenuTab(pathRoot), false); + await CorePromiseUtils.ignoreErrors(CoreMainMenu.isMainMenuTab(pathRoot), false); if (!options.preferCurrentTab && isMainMenuTab) { return this.navigate(`/main/${path}`, options); @@ -606,7 +607,7 @@ export class CoreNavigatorService { protected replaceObjectParams(queryParams?: Params | null): void { for (const name in queryParams) { const value = queryParams[name]; - if (typeof value != 'object' || value === null) { + if (typeof value !== 'object' || value === null) { continue; } diff --git a/src/core/services/platform.ts b/src/core/services/platform.ts index 61b1d7194f0..39658961d6a 100644 --- a/src/core/services/platform.ts +++ b/src/core/services/platform.ts @@ -91,6 +91,15 @@ export class CorePlatformService extends Platform { return this.is('cordova'); } + /** + * Checks if the current window is wider than a mobile. + * + * @returns Whether the app the current window is wider than a mobile. + */ + isWide(): boolean { + return this.width() > 768; + } + /** * Check whether the device is configured to reduce motion. * diff --git a/src/core/services/sites.ts b/src/core/services/sites.ts index f8ffdc56e60..b402491b3f0 100644 --- a/src/core/services/sites.ts +++ b/src/core/services/sites.ts @@ -21,8 +21,7 @@ import { CoreEvents } from '@singletons/events'; import { CoreWS } from '@services/ws'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreUrl, CoreUrlPartNames } from '@singletons/url'; -import { CoreUtils } from '@services/utils/utils'; -import { CoreConstants } from '@/core/constants'; +import { CoreConstants, MINIMUM_MOODLE_VERSION, MOODLE_RELEASES } from '@/core/constants'; import { CoreSite, CoreSiteConfig, @@ -68,6 +67,10 @@ import { CoreHTMLClasses } from '@singletons/html-classes'; import { CoreSiteErrorDebug } from '@classes/errors/siteerror'; import { CoreErrorHelper } from './error-helper'; import { CoreQueueRunner } from '@classes/queue-runner'; +import { CoreAppDB } from './app-db'; +import { CoreRedirects } from '@singletons/redirects'; +import { CorePromiseUtils } from '@singletons/promise-utils'; +import { CoreOpener } from '@singletons/opener'; export const CORE_SITE_SCHEMAS = new InjectionToken('CORE_SITE_SCHEMAS'); export const CORE_SITE_CURRENT_SITE_ID_CONFIG = 'current_site_id'; @@ -201,15 +204,11 @@ export class CoreSitesProvider { * Initialize database. */ async initializeDatabase(): Promise { - try { - await CoreApp.createTablesFromSchema(APP_SCHEMA); - } catch { - // Ignore errors. - } + await CoreAppDB.createTablesFromSchema(APP_SCHEMA); const sitesTable = new CoreDatabaseTableProxy( { cachingStrategy: CoreDatabaseCachingStrategy.Eager }, - CoreApp.getDB(), + CoreAppDB.getDB(), SITES_TABLE_NAME, ); @@ -466,7 +465,7 @@ export class CoreSitesProvider { if (error.debug?.code === 'codingerror') { // This could be caused by a redirect. Check if it's the case. - const redirect = await CoreUtils.checkRedirect(siteUrl); + const redirect = await CoreRedirects.checkRedirect(siteUrl); options.message = Translate.instant('core.siteunavailablehelp', { site: siteUrl }); @@ -482,7 +481,7 @@ export class CoreSitesProvider { options.message = Translate.instant('core.siteunavailablehelp', { site: siteUrl }); options.debug = { code: 'invalidmoodleversion', - details: Translate.instant('core.login.invalidmoodleversion', { $a: CoreSite.MINIMUM_MOODLE_VERSION }), + details: Translate.instant('core.login.invalidmoodleversion', { $a: MINIMUM_MOODLE_VERSION }), }; } else if (error.debug?.code === 'redirecterrordetected') { options.critical = false; // Keep checking fallback URLs. @@ -557,9 +556,9 @@ export class CoreSitesProvider { return this.getUserToken(siteUrl, username, password, service, true); } - if (data.errorcode == 'missingparam') { + if (data.errorcode === 'missingparam') { // It seems the server didn't receive all required params, it could be due to a redirect. - const redirect = await CoreUtils.checkRedirect(loginUrl); + const redirect = await CoreRedirects.checkRedirect(loginUrl); if (redirect) { throw this.createCannotConnectLoginError(siteUrl, { @@ -617,7 +616,7 @@ export class CoreSitesProvider { const siteId = this.createSiteID(info.siteurl, info.username); // Check if the site already exists. - const storedSite = await CoreUtils.ignoreErrors(this.getSite(siteId)); + const storedSite = await CorePromiseUtils.ignoreErrors(this.getSite(siteId)); let site: CoreSite; if (storedSite) { @@ -710,7 +709,7 @@ export class CoreSitesProvider { break; default: errorKey = 'core.login.invalidmoodleversion'; - translateParams = { $a: CoreSite.MINIMUM_MOODLE_VERSION }; + translateParams = { $a: MINIMUM_MOODLE_VERSION }; debug = { code: 'invalidmoodleversion', details: 'Cannot connect to app', @@ -788,7 +787,7 @@ export class CoreSitesProvider { if (info.version) { const version = parseInt(info.version, 10); if (!isNaN(version)) { - if (version >= CoreSite.MOODLE_RELEASES[CoreSite.MINIMUM_MOODLE_VERSION]) { + if (version >= MOODLE_RELEASES[MINIMUM_MOODLE_VERSION]) { return this.validateWorkplaceVersion(info); } } @@ -797,7 +796,7 @@ export class CoreSitesProvider { // We couldn't validate by version number. Let's try to validate by release number. const release = this.getReleaseNumber(info.release || ''); if (release) { - if (release >= CoreSite.MINIMUM_MOODLE_VERSION) { + if (release >= MINIMUM_MOODLE_VERSION) { return this.validateWorkplaceVersion(info); } } @@ -948,7 +947,7 @@ export class CoreSitesProvider { Translate.instant('core.updaterequired'), Translate.instant('core.download'), Translate.instant(siteId ? 'core.mainmenu.logout' : 'core.cancel'), - ).then(() => CoreUtils.openInBrowser(downloadUrl, { showBrowserWarning: false })).catch(() => { + ).then(() => CoreOpener.openInBrowser(downloadUrl, { showBrowserWarning: false })).catch(() => { // Do nothing. }); } else { @@ -1132,12 +1131,12 @@ export class CoreSitesProvider { delete this.sites[siteId]; // DB remove shouldn't fail, but we'll go ahead even if it does. - await CoreUtils.ignoreErrors(this.sitesTable.deleteByPrimaryKey({ id: siteId })); + await CorePromiseUtils.ignoreErrors(this.sitesTable.deleteByPrimaryKey({ id: siteId })); // Site deleted from sites list, now delete the folder. await site.deleteFolder(); - await CoreUtils.ignoreErrors(CoreNative.plugin('secureStorage')?.deleteCollection(siteId)); + await CorePromiseUtils.ignoreErrors(CoreNative.plugin('secureStorage')?.deleteCollection(siteId)); CoreEvents.trigger(CoreEvents.SITE_DELETED, site, siteId); } @@ -1198,7 +1197,7 @@ export class CoreSitesProvider { * @returns Site. */ async getSiteFromDB(siteId: string): Promise { - const db = CoreApp.getDB(); + const db = CoreAppDB.getDB(); try { const record = await db.getRecord(SITES_TABLE_NAME, { id: siteId }); @@ -1292,7 +1291,7 @@ export class CoreSitesProvider { return !!this.currentSite; } - const siteId = typeof site == 'object' ? site.getId() : site; + const siteId = typeof site === 'object' ? site.getId() : site; return this.currentSite.getId() === siteId; } @@ -1477,7 +1476,7 @@ export class CoreSitesProvider { promises.push(this.removeStoredCurrentSite()); - await CoreUtils.ignoreErrors(Promise.all(promises)); + await CorePromiseUtils.ignoreErrors(Promise.all(promises)); if (options.removeAccount) { await CoreSites.deleteSite(siteId); @@ -1500,7 +1499,7 @@ export class CoreSitesProvider { if (CoreSitePlugins.hasSitePluginsLoaded) { // The site has site plugins so the app will be restarted. Store the data and logout. - CoreApp.storeRedirect(siteId, redirectData); + CoreRedirects.storeRedirect(siteId, redirectData); } await this.logout(); @@ -1536,7 +1535,7 @@ export class CoreSitesProvider { * Handle auto logout by checking autologout type and time if its required. */ async handleAutoLogout(): Promise { - await CoreUtils.ignoreErrors(( async () => { + await CorePromiseUtils.ignoreErrors(( async () => { const siteId = await this.getStoredCurrentSiteId(); const site = await this.getSite(siteId); const autoLogoutType = Number(site.getStoredConfig('tool_mobile_autologout')); @@ -2052,12 +2051,12 @@ export class CoreSitesProvider { } try { - const db = CoreApp.getDB(); + const db = CoreAppDB.getDB(); const { siteId } = await db.getRecord<{ siteId: string }>('current_site'); await CoreConfig.set(CORE_SITE_CURRENT_SITE_ID_CONFIG, siteId); - await CoreApp.deleteTableSchema('current_site'); + await CoreAppDB.deleteTableSchema('current_site'); await db.dropTable('current_site'); } catch { // There was no current site, silence the error. @@ -2217,6 +2216,47 @@ export class CoreSitesProvider { this.afterLoginNavigationQueue = []; } + /** + * Filter the list of site IDs based on a isEnabled function. + * + * @param siteIds Site IDs to filter. + * @param isEnabledFn Function to call for each site. It receives a siteId param and all the params sent to this function + * after 'checkAll'. + * @param checkAll True if it should check all the sites, false if it should check only 1 and treat them all + * depending on this result. + * @returns Promise resolved with the list of enabled sites. + */ + async filterEnabledSites

( + siteIds: string[], + isEnabledFn: (siteId: string, ...args: P) => boolean | Promise, + checkAll?: boolean, + ...args: P + ): Promise { + const promises: Promise[] = []; + const enabledSites: string[] = []; + + for (const i in siteIds) { + const siteId = siteIds[i]; + const pushIfEnabled = enabled => enabled && enabledSites.push(siteId); + if (checkAll || !promises.length) { + promises.push( + Promise + .resolve(isEnabledFn(siteId, ...args)) + .then(pushIfEnabled), + ); + } + } + + await CorePromiseUtils.allPromisesIgnoringErrors(promises); + + if (!checkAll) { + // Checking 1 was enough, so it will either return all the sites or none. + return enabledSites.length ? siteIds : []; + } + + return enabledSites; + } + } export const CoreSites = makeSingleton(CoreSitesProvider); diff --git a/src/core/services/storage.ts b/src/core/services/storage.ts index 4191f80608e..f16fb41f0bc 100644 --- a/src/core/services/storage.ts +++ b/src/core/services/storage.ts @@ -15,7 +15,7 @@ import { Inject, Injectable, Optional } from '@angular/core'; import { AsyncInstance, asyncInstance } from '@/core/utils/async-instance'; -import { CoreApp } from '@services/app'; +import { CoreAppDB } from './app-db'; import { CoreDatabaseCachingStrategy, CoreDatabaseTableProxy } from '@classes/database/database-table-proxy'; import { CoreDatabaseTable } from '@classes/database/database-table'; import { makeSingleton } from '@singletons'; @@ -32,7 +32,7 @@ import { NULL_INJECTION_TOKEN } from '@/core/constants'; * The data can be scoped to a single site using CoreStorage.forSite(site), and it will be automatically cleared * when the site is deleted. * - * For tabular data, use CoreAppProvider.getDB() or CoreSite.getDb(). + * For tabular data, use CoreAppDB.getDB() or CoreSite.getDb(). */ @Injectable({ providedIn: 'root' }) export class CoreStorageService { @@ -47,13 +47,9 @@ export class CoreStorageService { * Initialize database. */ async initializeDatabase(): Promise { - try { - await CoreApp.createTablesFromSchema(APP_SCHEMA); - } catch { - // Ignore errors. - } + await CoreAppDB.createTablesFromSchema(APP_SCHEMA); - await this.initializeTable(CoreApp.getDB()); + await this.initializeTable(CoreAppDB.getDB()); } /** @@ -97,7 +93,7 @@ export class CoreStorageService { async getFromDB(key: string, defaultValue: T): Promise; async getFromDB(key: string, defaultValue: T | null = null): Promise { try { - const db = CoreApp.getDB(); + const db = CoreAppDB.getDB(); const { value } = await db.getRecord(TABLE_NAME, { key }); return JSON.parse(value); diff --git a/src/core/services/update-manager.ts b/src/core/services/update-manager.ts index e7015b1b661..bfa186140c2 100644 --- a/src/core/services/update-manager.ts +++ b/src/core/services/update-manager.ts @@ -21,8 +21,8 @@ import { makeSingleton } from '@singletons'; import { CoreH5P } from '@features/h5p/services/h5p'; import { CoreLoginHelper } from '@features/login/services/login-helper'; import { CoreSites } from './sites'; -import { CoreUtils } from './utils/utils'; -import { CoreApp } from './app'; +import { CorePromiseUtils } from '@singletons/promise-utils'; +import { CoreRedirects } from '@singletons/redirects'; import { CoreZoomLevel } from '@features/settings/services/settings-helper'; import { CorePromisedValue } from '@classes/promised-value'; import { CoreFile } from './file'; @@ -69,7 +69,7 @@ export class CoreUpdateManagerProvider { const [versionApplied, previousAppFolder, currentAppFolder] = await Promise.all([ CoreConfig.get(CoreUpdateManagerProvider.VERSION_APPLIED, 0), CoreConfig.get(CoreUpdateManagerProvider.PREVIOUS_APP_FOLDER, ''), - CorePlatform.isMobile() ? CoreUtils.ignoreErrors(CoreFile.getBasePath(), '') : '', + CorePlatform.isMobile() ? CorePromiseUtils.ignoreErrors(CoreFile.getBasePath(), '') : '', ]); if (versionCode > versionApplied) { @@ -119,12 +119,12 @@ export class CoreUpdateManagerProvider { return; } - const currentSiteId = await CoreUtils.ignoreErrors(CoreSites.getStoredCurrentSiteId()); + const currentSiteId = await CorePromiseUtils.ignoreErrors(CoreSites.getStoredCurrentSiteId()); if (!currentSiteId) { return; } - const site = await CoreUtils.ignoreErrors(CoreSites.getSite(currentSiteId)); + const site = await CorePromiseUtils.ignoreErrors(CoreSites.getSite(currentSiteId)); if (!site) { return; } @@ -138,7 +138,7 @@ export class CoreUpdateManagerProvider { await CoreSites.removeStoredCurrentSite(); // Tell the app to open add site so the user can add the new site. - CoreApp.storeRedirect(CoreConstants.NO_SITE_ID, { + CoreRedirects.storeRedirect(CoreConstants.NO_SITE_ID, { redirectPath: '/login/sites', redirectOptions: { params: { diff --git a/src/core/services/urlschemes.ts b/src/core/services/urlschemes.ts index 74a83f90bd1..f7131a788a9 100644 --- a/src/core/services/urlschemes.ts +++ b/src/core/services/urlschemes.ts @@ -23,13 +23,12 @@ import { ApplicationInit, makeSingleton, Translate } from '@singletons'; import { CoreLogger } from '@singletons/logger'; import { CorePath } from '@singletons/path'; import { CoreConstants } from '../constants'; -import { CoreApp } from './app'; +import { CoreSSO } from '@singletons/sso'; import { CoreNavigator, CoreRedirectPayload } from './navigator'; import { CoreSiteCheckResponse, CoreSites } from './sites'; import { CoreDomUtils } from './utils/dom'; import { CoreErrorHelper, CoreErrorObject } from './error-helper'; import { CoreUrl } from '@singletons/url'; -import { CoreUtils } from './utils/utils'; import { CoreLoadings } from './loadings'; /* @@ -217,7 +216,7 @@ export class CoreCustomURLSchemesProvider { modal.dismiss(); if (data.isSSOToken) { - CoreApp.finishSSOAuthentication(); + CoreSSO.finishSSOAuthentication(); } } } @@ -349,13 +348,13 @@ export class CoreCustomURLSchemesProvider { throw new CoreCustomURLSchemesHandleError(null); } - if (CoreApp.isSSOAuthenticationOngoing()) { + if (CoreSSO.isSSOAuthenticationOngoing()) { // Authentication ongoing, probably duplicated request. throw new CoreCustomURLSchemesHandleError('Duplicated'); } // App opened using custom URL scheme. Probably an SSO authentication. - CoreApp.startSSOAuthentication(); + CoreSSO.startSSOAuthentication(); this.logger.debug('App launched by URL with an SSO'); // Delete the sso scheme from the URL. @@ -494,7 +493,7 @@ export class CoreCustomURLSchemesProvider { treatHandleCustomURLError(error: CoreCustomURLSchemesHandleError): void { if (error.error == 'Duplicated') { // Duplicated request - } else if (CoreUtils.isWebServiceError(error.error) && error.data && error.data.isSSOToken) { + } else if (CoreWSError.isWebServiceError(error.error) && error.data && error.data.isSSOToken) { // An error occurred, display the error and logout the user. CoreLoginHelper.treatUserTokenError(error.data.siteUrl, error.error); CoreSites.logout(); diff --git a/src/core/services/utils/dom.ts b/src/core/services/utils/dom.ts index 165c6595bb3..2881e40c67e 100644 --- a/src/core/services/utils/dom.ts +++ b/src/core/services/utils/dom.ts @@ -22,7 +22,7 @@ import { CoreFile } from '@services/file'; import { CoreWSExternalWarning } from '@services/ws'; import { CoreText } from '@singletons/text'; import { CoreUrl, CoreUrlPartNames } from '@singletons/url'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreOpener } from '@singletons/opener'; import { CoreConstants } from '@/core/constants'; import { CoreIonLoadingElement } from '@classes/ion-loading'; import { CoreCanceledError } from '@classes/errors/cancelederror'; @@ -447,7 +447,7 @@ export class CoreDomUtilsProvider { let extraInfo = ''; let errorMessage: string | undefined; - if (typeof error == 'object') { + if (typeof error === 'object') { if (this.debugDisplay) { // Get the debug info. Escape the HTML so it is displayed as it is in the view. if ('debuginfo' in error && error.debuginfo) { @@ -1199,7 +1199,7 @@ export class CoreDomUtilsProvider { buttons.push({ text: Translate.instant('core.download'), handler: (): void => { - CoreUtils.openInBrowser(link, { showBrowserWarning: false }); + CoreOpener.openInBrowser(link, { showBrowserWarning: false }); }, }); } @@ -1428,7 +1428,7 @@ export class CoreDomUtilsProvider { event.preventDefault(); event.stopPropagation(); - CoreUtils.openInBrowser(href); + CoreOpener.openInBrowser(href); } }); }); diff --git a/src/core/services/utils/iframe.ts b/src/core/services/utils/iframe.ts index 3c8f3d84131..71056811978 100644 --- a/src/core/services/utils/iframe.ts +++ b/src/core/services/utils/iframe.ts @@ -22,7 +22,7 @@ import { CoreFileHelper } from '@services/file-helper'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreUrl } from '@singletons/url'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreOpener } from '@singletons/opener'; import { makeSingleton, NgZone, Translate } from '@singletons'; import { CoreLogger } from '@singletons/logger'; @@ -37,6 +37,8 @@ import { CoreFilepool } from '@services/filepool'; import { CoreSite } from '@classes/sites/site'; import { CoreNative } from '@features/native/services/native'; import { CoreLoadings } from '@services/loadings'; +import { CorePromiseUtils } from '@singletons/promise-utils'; +import { CoreFileUtils } from '@singletons/file-utils'; type CoreFrameElement = FrameElement & { window?: Window; @@ -430,7 +432,7 @@ export class CoreIframeUtilsProvider { ? ('src' in element ? element.src : element.data) : null; if (src) { - const dirAndFile = CoreFile.getFileAndDirectoryFromPath(src); + const dirAndFile = CoreFileUtils.getFileAndDirectoryFromPath(src); if (dirAndFile.directory) { url = CorePath.concatenatePaths(dirAndFile.directory, url); } else { @@ -453,7 +455,7 @@ export class CoreIframeUtilsProvider { return; } - if (element.tagName.toLowerCase() == 'object') { + if (element.tagName.toLowerCase() === 'object') { element.setAttribute('data', url); } else { element.setAttribute('src', url); @@ -507,7 +509,7 @@ export class CoreIframeUtilsProvider { (!link.target || link.target == '_self') ) { // Load the link inside the frame itself. - if (element.tagName.toLowerCase() == 'object') { + if (element.tagName.toLowerCase() === 'object') { element.setAttribute('data', link.href); } else { element.setAttribute('src', link.href); @@ -518,7 +520,7 @@ export class CoreIframeUtilsProvider { // The frame is local or the link needs to be opened in a new window. Open in browser. if (!CoreSites.isLoggedIn()) { - CoreUtils.openInBrowser(link.href); + CoreOpener.openInBrowser(link.href); } else { await CoreSites.getCurrentSite()?.openInBrowserWithAutoLogin(link.href); } @@ -537,14 +539,14 @@ export class CoreIframeUtilsProvider { } try { - await CoreUtils.openFile(link.href); + await CoreOpener.openFile(link.href); } catch (error) { CoreDomUtils.showErrorModal(error); } } else if (CorePlatform.isIOS() && (!link.target || link.target == '_self') && element) { // In cordova ios 4.1.0 links inside iframes stopped working. We'll manually treat them. event && event.preventDefault(); - if (element.tagName.toLowerCase() == 'object') { + if (element.tagName.toLowerCase() === 'object') { element.setAttribute('data', link.href); } else { element.setAttribute('src', link.href); @@ -681,11 +683,11 @@ export class CoreIframeUtilsProvider { if (!CoreNetwork.isOnline()) { // User is offline, try to open a local copy of the file if present. const localUrl = options.site ? - await CoreUtils.ignoreErrors(CoreFilepool.getInternalUrlByUrl(options.site.getId(), url)) : + await CorePromiseUtils.ignoreErrors(CoreFilepool.getInternalUrlByUrl(options.site.getId(), url)) : undefined; if (localUrl) { - CoreUtils.openFile(localUrl); + CoreOpener.openFile(localUrl); } else { CoreDomUtils.showErrorModal('core.networkerrormsg', true); } @@ -693,11 +695,11 @@ export class CoreIframeUtilsProvider { return; } - const mimetype = await CoreUtils.ignoreErrors(CoreUtils.getMimeTypeFromUrl(url)); + const mimetype = await CorePromiseUtils.ignoreErrors(CoreMimetypeUtils.getMimeTypeFromUrl(url)); if (!mimetype || mimetype === 'text/html' || mimetype === 'text/plain') { // It's probably a web page, open in browser. - options.site ? options.site.openInBrowserWithAutoLogin(url) : CoreUtils.openInBrowser(url); + options.site ? options.site.openInBrowserWithAutoLogin(url) : CoreOpener.openInBrowser(url); return; } @@ -709,7 +711,7 @@ export class CoreIframeUtilsProvider { url = await options.site.checkAndFixPluginfileURL(url); } - CoreUtils.openOnlineFile(url); + CoreOpener.openOnlineFile(url); } finally { modal.dismiss(); diff --git a/src/core/services/utils/mimetype.ts b/src/core/services/utils/mimetype.ts index 3125c6bf91e..a2f63d1f395 100644 --- a/src/core/services/utils/mimetype.ts +++ b/src/core/services/utils/mimetype.ts @@ -16,11 +16,11 @@ import { Injectable } from '@angular/core'; import { FileEntry } from '@awesome-cordova-plugins/file/ngx'; import { CoreFile } from '@services/file'; +import { CoreFileUtils } from '@singletons/file-utils'; import { CoreText } from '@singletons/text'; import { makeSingleton, Translate } from '@singletons'; import { CoreLogger } from '@singletons/logger'; -import { CoreWSFile } from '@services/ws'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreWS, CoreWSFile } from '@services/ws'; import extToMime from '@/assets/exttomime.json'; import mimeToExt from '@/assets/mimetoext.json'; @@ -168,11 +168,11 @@ export class CoreMimetypeUtilsProvider { * @returns The embedded HTML string. */ getEmbeddedHtml(file: CoreFileEntry, path?: string): string { - const filename = CoreUtils.isFileEntry(file) ? (file as FileEntry).name : file.filename; - const extension = !CoreUtils.isFileEntry(file) && file.mimetype + const filename = CoreFileUtils.isFileEntry(file) ? (file as FileEntry).name : file.filename; + const extension = !CoreFileUtils.isFileEntry(file) && file.mimetype ? this.getExtension(file.mimetype) : (filename && this.getFileExtension(filename)); - const mimeType = !CoreUtils.isFileEntry(file) && file.mimetype + const mimeType = !CoreFileUtils.isFileEntry(file) && file.mimetype ? file.mimetype : (extension && this.getMimeType(extension)); @@ -185,7 +185,7 @@ export class CoreMimetypeUtilsProvider { // @todo linting: See if this can be removed (file as { embedType?: string }).embedType = embedType; - path = path ?? (CoreUtils.isFileEntry(file) ? CoreFile.getFileEntryURL(file) : CoreFileHelper.getFileUrl(file)); + path = path ?? (CoreFileUtils.isFileEntry(file) ? CoreFile.getFileEntryURL(file) : CoreFileHelper.getFileUrl(file)); path = path && CoreFile.convertFileSrc(path); switch (embedType) { @@ -424,10 +424,10 @@ export class CoreMimetypeUtilsProvider { let mimetype: string | undefined = ''; let extension: string | undefined = ''; - if (typeof obj == 'object' && CoreUtils.isFileEntry(obj)) { + if (typeof obj === 'object' && CoreFileUtils.isFileEntry(obj)) { // It's a FileEntry. Don't use the file function because it's asynchronous and the type isn't reliable. filename = obj.name; - } else if (typeof obj == 'object') { + } else if (typeof obj === 'object') { filename = obj.filename || ''; mimetype = obj.mimetype || ''; } else { @@ -538,6 +538,30 @@ export class CoreMimetypeUtilsProvider { return this.getFileIconForType(icon); } + /** + * Get the mimetype of a file given its URL. It'll try to guess it using the URL, if that fails then it'll + * perform a HEAD request to get it. It's done in this order because pluginfile.php can return wrong mimetypes. + * This function is in here instead of MimetypeUtils to prevent circular dependencies. + * + * @param url The URL of the file. + * @returns Promise resolved with the mimetype. + */ + async getMimeTypeFromUrl(url: string): Promise { + // First check if it can be guessed from the URL. + const extension = CoreMimetypeUtils.guessExtensionFromUrl(url); + const mimetype = extension && CoreMimetypeUtils.getMimeType(extension); + + // Ignore PHP extension for now, it could be serving a file. + if (mimetype && extension !== 'php') { + return mimetype; + } + + // Can't be guessed, get the remote mimetype. + const remoteMimetype = await CoreWS.getRemoteFileMimeType(url); + + return remoteMimetype || mimetype || ''; + } + /** * Given a group name, return the translated name. * diff --git a/src/core/services/utils/utils.ts b/src/core/services/utils/utils.ts index 05fe91cece2..df4ca3209eb 100644 --- a/src/core/services/utils/utils.ts +++ b/src/core/services/utils/utils.ts @@ -13,33 +13,26 @@ // limitations under the License. import { Injectable } from '@angular/core'; -import { InAppBrowserObject, InAppBrowserOptions } from '@awesome-cordova-plugins/in-app-browser'; +import { InAppBrowserObject } from '@awesome-cordova-plugins/in-app-browser'; import { FileEntry } from '@awesome-cordova-plugins/file/ngx'; -import { CoreEvents } from '@singletons/events'; -import { CoreFile } from '@services/file'; -import { CoreLang, CoreLangFormat } from '@services/lang'; -import { CoreWS } from '@services/ws'; +import { CoreFileUtils } from '@singletons/file-utils'; +import { CoreRedirects } from '@singletons/redirects'; import { CoreMimetypeUtils } from '@services/utils/mimetype'; -import { makeSingleton, InAppBrowser, FileOpener, WebIntent, Translate, NgZone } from '@singletons'; -import { CoreLogger } from '@singletons/logger'; +import { makeSingleton } from '@singletons'; import { CoreFileEntry } from '@services/file-helper'; -import { CoreConstants } from '@/core/constants'; -import { CoreWindow } from '@singletons/window'; -import { CoreColors } from '@singletons/colors'; -import { CorePlatform } from '@services/platform'; -import { CoreErrorWithOptions } from '@classes/errors/errorwithoptions'; -import { CoreFilepool } from '@services/filepool'; -import { CoreSites } from '@services/sites'; import { CoreCancellablePromise } from '@classes/cancellable-promise'; -import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics'; -import { CoreUrl } from '@singletons/url'; import { CoreArray } from '@singletons/array'; import { CoreText } from '@singletons/text'; import { CoreWait, CoreWaitOptions } from '@singletons/wait'; import { CoreQRScan } from '@services/qrscan'; import { CoreErrorHelper } from '@services/error-helper'; - -export type TreeNode = T & { children: TreeNode[] }; +import { CorePromiseUtils, OrderedPromiseData } from '@singletons/promise-utils'; +import { CoreOpener, CoreOpenerOpenFileOptions, CoreOpenerOpenInBrowserOptions } from '@singletons/opener'; +import { CoreCountries, CoreCountry } from '@singletons/countries'; +import { CoreObject } from '@singletons/object'; +import { CoreSites } from '@services/sites'; +import { CoreMenuItem, CoreUtils as CoreUtilsSingleton, TreeNode } from '@singletons/utils'; +import { CoreWSError } from '@classes/errors/wserror'; /* * "Utils" service with helper functions. @@ -47,69 +40,36 @@ export type TreeNode = T & { children: TreeNode[] }; @Injectable({ providedIn: 'root' }) export class CoreUtilsProvider { - protected readonly DONT_CLONE = ['[object FileEntry]', '[object DirectoryEntry]', '[object DOMFileSystem]']; - - protected logger: CoreLogger; - protected iabInstance?: InAppBrowserObject; - protected uniqueIds: {[name: string]: number} = {}; - - constructor() { - this.logger = CoreLogger.getInstance('CoreUtilsProvider'); - } - /** * Given an error, add an extra warning to the error message and return the new error message. * * @param error Error object or message. * @param defaultError Message to show if the error is not a string. * @returns New error message. + * @deprecated since 5.0. Use CoreErrorHelper.addDataNotDownloadedError instead. */ addDataNotDownloadedError(error: Error | string, defaultError?: string): string { - const errorMessage = CoreErrorHelper.getErrorMessageFromError(error) || defaultError || ''; - - if (this.isWebServiceError(error)) { - return errorMessage; - } - - // Local error. Add an extra warning. - return errorMessage + '

' + Translate.instant('core.errorsomedatanotdownloaded'); + return CoreErrorHelper.addDataNotDownloadedError(error, defaultError); } /** * Similar to Promise.all, but if a promise fails this function's promise won't be rejected until ALL promises have finished. * * @param promises Promises. - * @returns Promise resolved if all promises are resolved and rejected if at least 1 promise fails. + * @deprecated since 5.0. Use CorePromiseUtils.allPromises instead. */ - async allPromises(promises: unknown[]): Promise { - if (!promises || !promises.length) { - return; - } - - const getPromiseError = async (promise: unknown): Promise => { - try { - await promise; - } catch (error) { - return error; - } - }; - - const errors = await Promise.all(promises.map(getPromiseError)); - const error = errors.find(error => !!error); - - if (error) { - throw error; - } + async allPromises(promises: Promise[]): Promise { + await CorePromiseUtils.allPromises(promises); } /** * Combination of allPromises and ignoreErrors functions. * * @param promises Promises. - * @returns Promise resolved if all promises are resolved and rejected if at least 1 promise fails. + * @deprecated since 5.0. Use CorePromiseUtils.allPromisesIgnoringErrors instead. */ async allPromisesIgnoringErrors(promises: Promise[]): Promise { - await CoreUtils.ignoreErrors(this.allPromises(promises)); + await CorePromiseUtils.allPromisesIgnoringErrors(promises); } /** @@ -121,19 +81,14 @@ export class CoreUtilsProvider { * @param propertyName The name of the property to use as the key. If not provided, the whole item will be used. * @param result Object where to put the properties. If not defined, a new object will be created. * @returns The object. + * @deprecated since 5.0. Use CoreArray.toObject instead. */ arrayToObject( array: T[] = [], propertyName?: string, result: Record = {}, ): Record { - for (const entry of array) { - const key = propertyName ? entry[propertyName] : entry; - - result[key] = entry; - } - - return result; + return CoreArray.toObject(array, propertyName, result); } /** @@ -141,9 +96,10 @@ export class CoreUtilsProvider { * * @param message Message to contextualize the error. * @param error Error to log. + * @deprecated since 5.0. Use CoreErrorHelper.logUnhandledError instead. */ logUnhandledError(message: string, error: unknown): void { - this.logger.error(message, error); + CoreErrorHelper.logUnhandledError(message, error); } /** @@ -155,22 +111,14 @@ export class CoreUtilsProvider { * @param propertyName The name of the property to use as the key. If not provided, the whole item will be used. * @param result Object where to put the properties. If not defined, a new object will be created. * @returns The object. + * @deprecated since 5.0. Use CoreArray.toObjectMultiple instead. */ arrayToObjectMultiple( array: T[] = [], propertyName?: string, result: Record = {}, ): Record { - for (const entry of array) { - const key = propertyName ? entry[propertyName] : entry; - if (result[key] === undefined) { - result[key] = []; - } - - result[key].push(entry); - } - - return result; + return CoreArray.toObjectMultiple(array, propertyName, result); } /** @@ -184,6 +132,7 @@ export class CoreUtilsProvider { * @param level Current deep level (when comparing objects). * @param undefinedIsNull True if undefined is equal to null. Defaults to true. * @returns Whether both items are equal. + * @deprecated since 5.0. Use CoreObject.basicLeftCompare instead. */ basicLeftCompare( itemA: any, // eslint-disable-line @typescript-eslint/no-explicit-any @@ -192,43 +141,7 @@ export class CoreUtilsProvider { level: number = 0, undefinedIsNull: boolean = true, ): boolean { - if (typeof itemA == 'function' || typeof itemB == 'function') { - return true; // Don't compare functions. - } else if (typeof itemA == 'object' && typeof itemB == 'object') { - if (level >= maxLevels) { - return true; // Max deep reached. - } - - let equal = true; - for (const name in itemA) { - const value = itemA[name]; - if (name == '$$hashKey') { - // Ignore $$hashKey property since it's a "calculated" property. - continue; - } - - if (!this.basicLeftCompare(value, itemB[name], maxLevels, level + 1)) { - equal = false; - } - } - - return equal; - } else { - if (undefinedIsNull && ( - (itemA === undefined && itemB === null) || (itemA === null && itemB === undefined))) { - return true; - } - - // We'll treat "2" and 2 as the same value. - const floatA = parseFloat(itemA); - const floatB = parseFloat(itemB); - - if (!isNaN(floatA) && !isNaN(floatB)) { - return floatA == floatB; - } - - return itemA === itemB; - } + return CoreObject.basicLeftCompare(itemA, itemB, maxLevels, level, undefinedIsNull); } /** @@ -236,62 +149,39 @@ export class CoreUtilsProvider { * * @param url The URL to check. * @returns Promise resolved with boolean_ whether there is a redirect. + * @deprecated since 5.0. Use CoreRedirects.checkRedirect instead. */ async checkRedirect(url: string): Promise { - if (!window.fetch) { - // Cannot check if there is a redirect, assume it's false. - return false; - } - - const initOptions: RequestInit = { redirect: 'follow' }; - - // Some browsers implement fetch but no AbortController. - const controller = AbortController ? new AbortController() : false; - - if (controller) { - initOptions.signal = controller.signal; - } - - try { - const response = await this.timeoutPromise(window.fetch(url, initOptions), CoreWS.getRequestTimeout()); - - return response.redirected; - } catch (error) { - if (error.timeout && controller) { - // Timeout, abort the request. - controller.abort(); - } - - // There was a timeout, cannot determine if there's a redirect. Assume it's false. - return false; - } + return CoreRedirects.checkRedirect(url); } /** * Close the InAppBrowser window. + * + * @deprecated since 5.0. Use CoreOpener.closeInAppBrowser instead. */ closeInAppBrowser(): void { - if (this.iabInstance) { - this.iabInstance.close(); - } + CoreOpener.closeInAppBrowser(); } /** * Get inapp browser instance (if any). * * @returns IAB instance, undefined if not open. + * @deprecated since 5.0. Use CoreOpener.getInAppBrowserInstance instead. */ getInAppBrowserInstance(): InAppBrowserObject | undefined { - return this.iabInstance; + return CoreOpener.getInAppBrowserInstance(); } /** * Check if inapp browser is open. * * @returns Whether it's open. + * @deprecated since 5.0. Use CoreOpener.isInAppBrowserOpen instead. */ isInAppBrowserOpen(): boolean { - return !!this.iabInstance; + return CoreOpener.isInAppBrowserOpen(); } /** @@ -300,44 +190,10 @@ export class CoreUtilsProvider { * @param source The variable to clone. * @param level Depth we are right now inside a cloned object. It's used to prevent reaching max call stack size. * @returns Cloned variable. + * @deprecated since 5.0. Use CoreUtils.clone instead. */ clone(source: T, level: number = 0): T { - if (level >= 20) { - // Max 20 levels. - this.logger.error('Max depth reached when cloning object.', source); - - return source; - } - - if (this.valueIsFileEntry(source)) { - // Don't clone FileEntry. It has a lot of depth and they shouldn't be modified. - return source; - } else if (Array.isArray(source)) { - // Clone the array and all the entries. - const newArray = [] as unknown as T; - for (let i = 0; i < source.length; i++) { - newArray[i] = this.clone(source[i], level + 1); - } - - return newArray; - } else if (this.isObject(source)) { - // Check if the object shouldn't be copied. - if (source.toString && this.DONT_CLONE.indexOf(source.toString()) != -1) { - // Object shouldn't be copied, return it as it is. - return source; - } - - // Clone the object and all the subproperties. - const newObject = {} as T; - for (const name in source) { - newObject[name] = this.clone(source[name], level + 1); - } - - return newObject; - } else { - // Primitive type or unknown, return it as it is. - return source; - } + return CoreUtilsSingleton.clone(source, level); } /** @@ -351,7 +207,7 @@ export class CoreUtilsProvider { copyProperties(from: Record, to: Record, clone: boolean = true): void { for (const name in from) { if (clone) { - to[name] = this.clone(from[name]); + to[name] = CoreUtilsSingleton.clone(from[name]); } else { to[name] = from[name]; } @@ -398,35 +254,10 @@ export class CoreUtilsProvider { * Execute promises one depending on the previous. * * @param orderedPromisesData Data to be executed. - * @returns Promise resolved when all promises are resolved. - */ - executeOrderedPromises(orderedPromisesData: OrderedPromiseData[]): Promise { - const promises: Promise[] = []; - let dependency = Promise.resolve(); - - // Execute all the processes in order. - for (const i in orderedPromisesData) { - const data = orderedPromisesData[i]; - // Add the process to the dependency stack. - const promise = dependency.finally(() => { - try { - return data.function(); - } catch (e) { - this.logger.error(e.message); - - return; - } - }); - promises.push(promise); - - // If the new process is blocking, we set it as the dependency. - if (data.blocking) { - dependency = promise; - } - } - - // Return when all promises are done. - return this.allPromises(promises); + * @deprecated since 5.0 Use CorePromiseUtils.executeOrderedPromises instead. + */ + async executeOrderedPromises(orderedPromisesData: OrderedPromiseData[]): Promise { + await CorePromiseUtils.executeOrderedPromises(orderedPromisesData); } /** @@ -437,32 +268,10 @@ export class CoreUtilsProvider { * @param obj Object to flatten. * @param useDotNotation Whether to use dot notation '.' or square brackets '['. * @returns Flattened object. + * @deprecated since 5.0. Use CoreObject.flatten instead. */ flattenObject(obj: Record, useDotNotation?: boolean): Record { - const toReturn = {}; - - for (const name in obj) { - if (!Object.prototype.hasOwnProperty.call(obj, name)) { - continue; - } - - const value = obj[name]; - if (typeof value == 'object' && !Array.isArray(value)) { - const flatObject = this.flattenObject(value as Record); - for (const subName in flatObject) { - if (!Object.prototype.hasOwnProperty.call(flatObject, subName)) { - continue; - } - - const newName = useDotNotation ? name + '.' + subName : name + '[' + subName + ']'; - toReturn[newName] = flatObject[subName]; - } - } else { - toReturn[name] = value; - } - } - - return toReturn; + return CoreObject.flatten(obj, useDotNotation); } /** @@ -486,6 +295,7 @@ export class CoreUtilsProvider { * @param checkAll True if it should check all the sites, false if it should check only 1 and treat them all * depending on this result. * @returns Promise resolved with the list of enabled sites. + * @deprecated since 5.0. Use CoreSites.filterEnabledSites instead. */ async filterEnabledSites

( siteIds: string[], @@ -493,29 +303,7 @@ export class CoreUtilsProvider { checkAll?: boolean, ...args: P ): Promise { - const promises: Promise[] = []; - const enabledSites: string[] = []; - - for (const i in siteIds) { - const siteId = siteIds[i]; - const pushIfEnabled = enabled => enabled && enabledSites.push(siteId); - if (checkAll || !promises.length) { - promises.push( - Promise - .resolve(isEnabledFn(siteId, ...args)) - .then(pushIfEnabled), - ); - } - } - - await CoreUtils.ignoreErrors(this.allPromises(promises)); - - if (!checkAll) { - // Checking 1 was enough, so it will either return all the sites or none. - return enabledSites.length ? siteIds : []; - } else { - return enabledSites; - } + return CoreSites.filterEnabledSites(siteIds, isEnabledFn, checkAll, ...args); } /** @@ -524,17 +312,10 @@ export class CoreUtilsProvider { * * @param float The float to print. * @returns Locale float. + * @deprecated since 5.0. Use CoreUtils.formatFloat on singletons instead. */ formatFloat(float: unknown): string { - if (float === undefined || float === null || typeof float == 'boolean') { - return ''; - } - - const localeSeparator = Translate.instant('core.decsep'); - - const floatString = String(float); - - return floatString.replace('.', localeSeparator); + return CoreUtilsSingleton.formatFloat(float); } /** @@ -548,6 +329,7 @@ export class CoreUtilsProvider { * @param rootParentId The id of the root. * @param maxDepth Max Depth to convert to tree. Children found will be in the last level of depth. * @returns Array with the formatted tree, children will be on each node under children field. + * @deprecated since 5.0. Use CoreUtils.formatTree on singletons instead. */ formatTree( list: T[], @@ -556,67 +338,7 @@ export class CoreUtilsProvider { rootParentId: number = 0, maxDepth: number = 5, ): TreeNode[] { - const map = {}; - const mapDepth = {}; - const tree: TreeNode[] = []; - - // Create a map first to avoid problems with not sorted. - list.forEach((node: TreeNode, index): void => { - const id = node[idFieldName]; - - if (id === undefined) { - this.logger.error(`Node with incorrect ${idFieldName}:${id} found on formatTree`); - } - - if (node.children === undefined) { - node.children = []; - } - map[id] = index; - }); - - list.forEach((node: TreeNode): void => { - const id = node[idFieldName]; - const parent = node[parentFieldName]; - - if (id === undefined || parent === undefined) { - this.logger.error(`Node with incorrect ${idFieldName}:${id} or ${parentFieldName}:${parent} found on formatTree`); - } - - // Use map to look-up the parents. - if (parent !== rootParentId) { - const parentNode = list[map[parent]] as TreeNode; - if (parentNode) { - if (mapDepth[parent] == maxDepth) { - // Reached max level of depth. Proceed with flat order. Find parent object of the current node. - const parentOfParent = parentNode[parentFieldName]; - if (parentOfParent) { - // This element will be the child of the node that is two levels up the hierarchy - // (i.e. the child of node.parent.parent). - (list[map[parentOfParent]] as TreeNode).children.push(node); - // Assign depth level to the same depth as the parent (i.e. max depth level). - mapDepth[id] = mapDepth[parent]; - // Change the parent to be the one that is two levels up the hierarchy. - node[parentFieldName] = parentOfParent; - } else { - this.logger.error(`Node parent of parent:${parentOfParent} not found on formatTree`); - } - } else { - parentNode.children.push(node); - // Increase the depth level. - mapDepth[id] = mapDepth[parent] + 1; - } - } else { - this.logger.error(`Node parent:${parent} not found on formatTree`); - } - } else { - tree.push(node); - - // Root elements are the first elements in the tree structure, therefore have the depth level 1. - mapDepth[id] = 1; - } - }); - - return tree; + return CoreUtilsSingleton.formatTree(list, parentFieldName, idFieldName, rootParentId, maxDepth); } /** @@ -624,100 +346,30 @@ export class CoreUtilsProvider { * * @param code Country code (AF, ES, US, ...). * @returns Country name. If the country is not found, return the country code. + * @deprecated since 5.0. Use CoreCountries.getCountryName instead. */ getCountryName(code: string): string { - const countryKey = 'assets.countries.' + code; - const countryName = Translate.instant(countryKey); - - return countryName !== countryKey ? countryName : code; + return CoreCountries.getCountryName(code); } /** * Get list of countries with their code and translated name. * * @returns Promise resolved with the list of countries. + * @deprecated since 5.0. Use CoreCountries.getCountryList instead. */ async getCountryList(): Promise> { - // Get the keys of the countries. - const keys = await this.getCountryKeysList(); - - // Now get the code and the translated name. - const countries: Record = {}; - - keys.forEach((key) => { - if (key.indexOf('assets.countries.') === 0) { - const code = key.replace('assets.countries.', ''); - countries[code] = Translate.instant(key); - } - }); - - return countries; + return CoreCountries.getCountryList(); } /** * Get list of countries with their code and translated name. Sorted by the name of the country. * * @returns Promise resolved with the list of countries. + * @deprecated since 5.0. Use CoreCountries.getCountryListSorted instead. */ async getCountryListSorted(): Promise { - // Get the keys of the countries. - const countries = await this.getCountryList(); - - // Sort translations. - return Object.keys(countries) - .sort((a, b) => countries[a].localeCompare(countries[b])) - .map((code) => ({ code, name: countries[code] })); - } - - /** - * Get the list of language keys of the countries. - * - * @returns Promise resolved with the countries list. Rejected if not translated. - */ - protected async getCountryKeysList(): Promise { - // It's possible that the current language isn't translated, so try with default language first. - const defaultLang = CoreLang.getDefaultLanguage(); - - try { - return await this.getCountryKeysListForLanguage(defaultLang); - } catch { - // Not translated, try to use the fallback language. - const fallbackLang = CoreLang.getFallbackLanguage(); - - if (fallbackLang === defaultLang) { - // Same language, just reject. - throw new Error('Countries not found.'); - } - - return this.getCountryKeysListForLanguage(fallbackLang); - } - } - - /** - * Get the list of language keys of the countries, based on the translation table for a certain language. - * - * @param lang Language to check. - * @returns Promise resolved with the countries list. Rejected if not translated. - */ - protected async getCountryKeysListForLanguage(lang: string): Promise { - // Get the translation table for the language. - const table = await CoreLang.getTranslationTable(lang); - - // Gather all the keys for countries, - const keys: string[] = []; - - for (const name in table) { - if (name.indexOf('assets.countries.') === 0) { - keys.push(name); - } - } - - if (keys.length === 0) { - // Not translated, reject. - throw new Error('Countries not found.'); - } - - return keys; + return CoreCountries.getCountryListSorted(); } /** @@ -727,21 +379,10 @@ export class CoreUtilsProvider { * * @param url The URL of the file. * @returns Promise resolved with the mimetype. + * @deprecated since 5.0. Use CoreMimetypeUtils.getMimeTypeFromUrl instead. */ async getMimeTypeFromUrl(url: string): Promise { - // First check if it can be guessed from the URL. - const extension = CoreMimetypeUtils.guessExtensionFromUrl(url); - const mimetype = extension && CoreMimetypeUtils.getMimeType(extension); - - // Ignore PHP extension for now, it could be serving a file. - if (mimetype && extension !== 'php') { - return mimetype; - } - - // Can't be guessed, get the remote mimetype. - const remoteMimetype = await CoreWS.getRemoteFileMimeType(url); - - return remoteMimetype || mimetype || ''; + return CoreMimetypeUtils.getMimeTypeFromUrl(url); } /** @@ -749,13 +390,10 @@ export class CoreUtilsProvider { * * @param name The name to get the ID for. * @returns Unique ID. + * @deprecated since 5.0. Use CoreUtils.getUniqueId singleton instead. */ getUniqueId(name: string): number { - if (!this.uniqueIds[name]) { - this.uniqueIds[name] = 0; - } - - return ++this.uniqueIds[name]; + return CoreUtilsSingleton.getUniqueId(name); } /** @@ -763,9 +401,10 @@ export class CoreUtilsProvider { * * @param file File. * @returns Type guard indicating if the file is a FileEntry. + * @deprecated since 5.0. Use CoreFile.isFileEntry singleton instead. */ isFileEntry(file: CoreFileEntry): file is FileEntry { - return 'isFile' in file; + return CoreFileUtils.isFileEntry(file); } /** @@ -773,11 +412,10 @@ export class CoreUtilsProvider { * * @param file Object to check. * @returns Type guard indicating if the file is a FileEntry. + * @deprecated since 5.0. Use CoreFile.valueIsFileEntry singleton instead. */ valueIsFileEntry(file: unknown): file is FileEntry { - // We cannot use instanceof because FileEntry is a type. Check some of the properties. - return !!(file && typeof file == 'object' && 'isFile' in file && 'filesystem' in file && - 'toInternalURL' in file && 'copyTo' in file); + return CoreFileUtils.valueIsFileEntry(file); } /** @@ -785,9 +423,10 @@ export class CoreUtilsProvider { * * @param object Variable. * @returns Type guard indicating if this is an object. + * @deprecated since 5.0. Use CoreObject.isObject instead. */ isObject(object: unknown): object is Record { - return typeof object === 'object' && object !== null; + return CoreObject.isObject(object); } /** @@ -795,27 +434,10 @@ export class CoreUtilsProvider { * * @param files List of files. * @returns String with error message if repeated, false if no repeated. + * @deprecated since 5.0. Use CoreFileUtils.hasRepeatedFilenames instead. */ hasRepeatedFilenames(files: CoreFileEntry[]): string | false { - if (!files || !files.length) { - return false; - } - - const names: string[] = []; - - // Check if there are 2 files with the same name. - for (let i = 0; i < files.length; i++) { - const file = files[i]; - const name = (this.isFileEntry(file) ? file.name : file.filename) || ''; - - if (names.indexOf(name) > -1) { - return Translate.instant('core.filenameexist', { $a: name }); - } - - names.push(name); - } - - return false; + return CoreFileUtils.hasRepeatedFilenames(files); } /** @@ -824,22 +446,10 @@ export class CoreUtilsProvider { * @param array Array to search. * @param regex RegExp to apply to each string. * @returns Index of the first string that matches the RegExp. -1 if not found. + * @deprecated since 4.4. Use CoreArray.indexOfRegexp instead. */ indexOfRegexp(array: string[], regex: RegExp): number { - if (!array || !array.length) { - return -1; - } - - for (let i = 0; i < array.length; i++) { - const entry = array[i]; - const matches = entry.match(regex); - - if (matches && matches.length) { - return i; - } - } - - return -1; + return CoreArray.indexOfRegexp(array, regex); } /** @@ -847,10 +457,11 @@ export class CoreUtilsProvider { * * @param value Value to check. * @returns Whether the value is false, 0 or "0". + * @deprecated since 5.0. Use CoreUtils.isFalseOrZero singleton instead. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any isFalseOrZero(value: any): boolean { - return value !== undefined && (value === false || value === 'false' || parseInt(value, 10) === 0); + return CoreUtilsSingleton.isFalseOrZero(value); } /** @@ -858,10 +469,11 @@ export class CoreUtilsProvider { * * @param value Value to check. * @returns Whether the value is true, 1 or "1". + * @deprecated since 5.0. Use CoreUtils.isTrueOrOne singleton instead. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any isTrueOrOne(value: any): boolean { - return value !== undefined && (value === true || value === 'true' || parseInt(value, 10) === 1); + return CoreUtilsSingleton.isTrueOrOne(value); } /** @@ -869,20 +481,11 @@ export class CoreUtilsProvider { * * @param error Error to check. * @returns Whether the error was returned by the WebService. + * @deprecated since 5.0. Use CoreWSError.isWebServiceError instead. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any isWebServiceError(error: any): boolean { - return error && ( - error.warningcode !== undefined || - ( - error.errorcode !== undefined && error.errorcode != 'userdeleted' && error.errorcode != 'upgraderunning' && - error.errorcode != 'forcepasswordchangenotice' && error.errorcode != 'usernotfullysetup' && - error.errorcode != 'sitepolicynotagreed' && error.errorcode != 'sitemaintenance' && - error.errorcode != 'wsaccessusersuspended' && error.errorcode != 'wsaccessuserdeleted' && - !this.isExpiredTokenError(error) - ) || - error.status && error.status >= 400 // CoreHttpError, assume status 400 and above are like WebService errors. - ); + return CoreWSError.isWebServiceError(error); } /** @@ -890,11 +493,11 @@ export class CoreUtilsProvider { * * @param error Error to check. * @returns Whether the error is a token expired error. + * @deprecated since 5.0. Use CoreWSError.isExpiredTokenError instead. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any isExpiredTokenError(error: any): boolean { - return error.errorcode === 'invalidtoken' || - (error.errorcode === 'accessexception' && error.message.includes('Invalid token - token expired')); + return CoreWSError.isExpiredTokenError(error); } /** @@ -906,6 +509,7 @@ export class CoreUtilsProvider { * @param separator The separator used within the list string. Default ','. * @param defaultValue Element that will become default option value. Default 0. * @returns The now assembled array + * @deprecated since 5.0. Use CoreUtils.makeMenuFromList singleton instead. */ makeMenuFromList( list: string, @@ -913,20 +517,7 @@ export class CoreUtilsProvider { separator: string = ',', defaultValue?: T, ): CoreMenuItem[] { - // Split and format the list. - const split = list.split(separator).map((label, index) => ({ - label: label.trim(), - value: index + 1, - })) as { label: string; value: T | number }[]; - - if (defaultLabel) { - split.unshift({ - label: defaultLabel, - value: defaultValue || 0, - }); - } - - return split; + return CoreUtilsSingleton.makeMenuFromList(list, defaultLabel, separator, defaultValue); } /** @@ -936,9 +527,10 @@ export class CoreUtilsProvider { * @param array2 The second array. * @param [key] Key of the property that must be unique. If not specified, the whole entry. * @returns Merged array. + * @deprecated since 5.0. Use CoreArray.mergeWithoutDuplicates instead. */ mergeArraysWithoutDuplicates(array1: T[], array2: T[], key?: string): T[] { - return CoreArray.unique(array1.concat(array2), key) as T[]; + return CoreArray.mergeWithoutDuplicates(array1, array2, key); } /** @@ -946,6 +538,7 @@ export class CoreUtilsProvider { * * @param value Value to check. * @returns True if not null and not undefined. + * @deprecated since 5.0. Use ?? instead. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any notNullOrUndefined(value: any): boolean { @@ -957,230 +550,35 @@ export class CoreUtilsProvider { * * @param path The local path of the file to be open. * @param options Options. - * @returns Promise resolved when done. - */ - async openFile(path: string, options: CoreUtilsOpenFileOptions = {}): Promise { - // Convert the path to a native path if needed. - path = CoreFile.unconvertFileSrc(path); - - const extension = CoreMimetypeUtils.getFileExtension(path); - const mimetype = extension && CoreMimetypeUtils.getMimeType(extension); - - if (mimetype == 'text/html' && CorePlatform.isAndroid()) { - // Open HTML local files in InAppBrowser, in system browser some embedded files aren't loaded. - this.openInApp(path); - - return; - } else if (extension === 'apk' && CorePlatform.isAndroid()) { - const url = await CoreUtils.ignoreErrors( - CoreFilepool.getFileUrlByPath(CoreSites.getCurrentSiteId(), CoreFile.removeBasePath(path)), - ); - - // @todo MOBILE-4167: Handle urls with expired tokens. - - throw new CoreErrorWithOptions( - Translate.instant('core.cannotinstallapkinfo'), - Translate.instant('core.cannotinstallapk'), - url - ? [ - { - text: Translate.instant('core.openinbrowser'), - handler: () => this.openInBrowser(url), - }, - { - text: Translate.instant('core.cancel'), - role: 'cancel', - }, - ] - : undefined, - ); - } - - // Path needs to be decoded, the file won't be opened if the path has %20 instead of spaces and so. - try { - path = decodeURIComponent(path); - } catch (ex) { - // Error, use the original path. - } - - const openFile = async (mimetype?: string) => { - if (this.shouldOpenWithDialog(options)) { - await FileOpener.showOpenWithDialog(path, mimetype || ''); - } else { - await FileOpener.open(path, mimetype || ''); - } - }; - - try { - try { - await openFile(mimetype); - } catch (error) { - if (!extension || !error || Number(error.status) !== 9) { - throw error; - } - - // Cannot open mimetype. Check if there is a deprecated mimetype for the extension. - const deprecatedMimetype = CoreMimetypeUtils.getDeprecatedMimeType(extension); - if (!deprecatedMimetype || deprecatedMimetype === mimetype) { - throw error; - } - - await openFile(deprecatedMimetype); - } - } catch (error) { - this.logger.error('Error opening file ' + path + ' with mimetype ' + mimetype); - this.logger.error('Error: ', JSON.stringify(error)); - - if (!extension || extension.indexOf('/') > -1 || extension.indexOf('\\') > -1) { - // Extension not found. - throw new Error(Translate.instant('core.erroropenfilenoextension')); - } - - throw new Error(Translate.instant('core.erroropenfilenoapp')); - } + * @deprecated since 5.0. Use CoreOpener.openFile instead. + */ + async openFile(path: string, options: CoreOpenerOpenFileOptions = {}): Promise { + await CoreOpener.openFile(path, options); } /** * Open a URL using InAppBrowser. - * Do not use for files, refer to {@link CoreUtilsProvider.openFile}. + * Do not use for files, refer to {@link CoreOpener.openFile}. * * @param url The URL to open. * @param options Override default options passed to InAppBrowser. * @returns The opened window. - */ - openInApp(url: string, options?: CoreUtilsOpenInAppOptions): InAppBrowserObject { - options = options || {}; - options.usewkwebview = 'yes'; // Force WKWebView in iOS. - options.enableViewPortScale = options.enableViewPortScale ?? 'yes'; // Enable zoom on iOS by default. - options.allowInlineMediaPlayback = options.allowInlineMediaPlayback ?? 'yes'; // Allow playing inline videos in iOS. - - if (!options.location && CorePlatform.isIOS() && url.indexOf('file://') === 0) { - // The URL uses file protocol, don't show it on iOS. - // In Android we keep it because otherwise we lose the whole toolbar. - options.location = 'no'; - } - - this.setInAppBrowserToolbarColors(options); - - this.iabInstance = InAppBrowser.create(url, '_blank', options); - - if (CorePlatform.isMobile()) { - const loadStartUrls: string[] = []; - - const loadStartSubscription = this.iabInstance.on('loadstart').subscribe((event) => { - NgZone.run(() => { - // Store the last loaded URLs (max 10). - loadStartUrls.push(event.url); - if (loadStartUrls.length > 10) { - loadStartUrls.shift(); - } - - CoreEvents.trigger(CoreEvents.IAB_LOAD_START, event); - }); - }); - - const loadStopSubscription = this.iabInstance.on('loadstop').subscribe((event) => { - NgZone.run(() => { - CoreEvents.trigger(CoreEvents.IAB_LOAD_STOP, event); - }); - }); - - const messageSubscription = this.iabInstance.on('message').subscribe((event) => { - NgZone.run(() => { - CoreEvents.trigger(CoreEvents.IAB_MESSAGE, event.data); - }); - }); - - const exitSubscription = this.iabInstance.on('exit').subscribe((event) => { - NgZone.run(() => { - loadStartSubscription.unsubscribe(); - loadStopSubscription.unsubscribe(); - messageSubscription.unsubscribe(); - exitSubscription.unsubscribe(); - - this.iabInstance = undefined; - CoreEvents.trigger(CoreEvents.IAB_EXIT, event); - }); - }); - } - - CoreAnalytics.logEvent({ - type: CoreAnalyticsEventType.OPEN_LINK, - link: CoreUrl.unfixPluginfileURL(options.originalUrl ?? url), - }); - - return this.iabInstance; - } - - /** - * Given some IAB options, set the toolbar colors properties to the right values. * - * @param options Options to change. - * @returns Changed options. + * @deprecated since 5.0. Use CoreOpener.openInApp instead. */ - protected setInAppBrowserToolbarColors(options: InAppBrowserOptions): InAppBrowserOptions { - if (options.toolbarcolor) { - // Color already set. - return options; - } - - // Color not set. Check if it needs to be changed automatically. - let bgColor: string | undefined; - let textColor: string | undefined; - - if (CoreConstants.CONFIG.iabToolbarColors === 'auto') { - bgColor = CoreColors.getToolbarBackgroundColor(); - } else if (CoreConstants.CONFIG.iabToolbarColors && typeof CoreConstants.CONFIG.iabToolbarColors === 'object') { - bgColor = CoreConstants.CONFIG.iabToolbarColors.background; - textColor = CoreConstants.CONFIG.iabToolbarColors.text; - } - - if (!bgColor) { - // Use default color. In iOS, use black background color since the default is transparent and doesn't look good. - options.locationcolor = '#000000'; - - return options; - } - - if (!textColor) { - textColor = CoreColors.isWhiteContrastingBetter(bgColor) ? '#ffffff' : '#000000'; - } - - options.toolbarcolor = bgColor; - options.closebuttoncolor = textColor; - options.navigationbuttoncolor = textColor; - options.locationcolor = bgColor; - options.locationtextcolor = textColor; - - return options; + openInApp(url: string, options?: CoreOpenerOpenFileOptions): InAppBrowserObject { + return CoreOpener.openInApp(url, options); } /** * Open a URL using a browser. - * Do not use for files, refer to {@link CoreUtilsProvider.openFile}. * * @param url The URL to open. * @param options Options. + * @deprecated since 5.0. Use CoreOpener.openInBrowser instead. */ - async openInBrowser(url: string, options: CoreUtilsOpenInBrowserOptions = {}): Promise { - // eslint-disable-next-line deprecation/deprecation - const originaUrl = CoreUrl.unfixPluginfileURL(options.originalUrl ?? options.browserWarningUrl ?? url); - if (options.showBrowserWarning || options.showBrowserWarning === undefined) { - try { - await CoreWindow.confirmOpenBrowserIfNeeded(originaUrl); - } catch { - return; // Cancelled, stop. - } - } - - const site = CoreSites.getCurrentSite(); - CoreAnalytics.logEvent({ type: CoreAnalyticsEventType.OPEN_LINK, link: originaUrl }); - window.open( - site?.containsUrl(url) - ? CoreUrl.addParamsToUrl(url, { lang: await CoreLang.getCurrentLanguage(CoreLangFormat.LMS) }) - : url, - '_system', - ); + async openInBrowser(url: string, options: CoreOpenerOpenInBrowserOptions = {}): Promise { + await CoreOpener.openInBrowser(url, options); } /** @@ -1188,43 +586,10 @@ export class CoreUtilsProvider { * Specially useful for audio and video since they can be streamed. * * @param url The URL of the file. - * @returns Promise resolved when opened. + * @deprecated since 5.0. Use CoreOpener.openOnlineFile instead. */ async openOnlineFile(url: string): Promise { - if (CorePlatform.isAndroid()) { - // In Android we need the mimetype to open it. - const mimetype = await this.ignoreErrors(this.getMimeTypeFromUrl(url)); - - if (!mimetype) { - // Couldn't retrieve mimetype. Return error. - throw new Error(Translate.instant('core.erroropenfilenoextension')); - } - - const options = { - action: WebIntent.ACTION_VIEW, - url, - type: mimetype, - }; - - try { - await WebIntent.startActivity(options); - - CoreAnalytics.logEvent({ - type: CoreAnalyticsEventType.OPEN_LINK, - link: CoreUrl.unfixPluginfileURL(url), - }); - - return; - } catch (error) { - this.logger.error('Error opening online file ' + url + ' with mimetype ' + mimetype); - this.logger.error('Error: ', JSON.stringify(error)); - - throw new Error(Translate.instant('core.erroropenfilenoapp')); - } - } - - // In the rest of platforms we need to open them in InAppBrowser. - this.openInApp(url); + await CoreOpener.openOnlineFile(url); } /** @@ -1232,9 +597,10 @@ export class CoreUtilsProvider { * * @param obj Object to convert. * @returns Array with the values of the object but losing the keys. + * @deprecated since 5.0. Use CoreObject.toArray instead. */ objectToArray(obj: Record): T[] { - return Object.keys(obj).map((key) => obj[key]); + return CoreObject.toArray(obj); } /** @@ -1248,6 +614,7 @@ export class CoreUtilsProvider { * @param sortByKey True to sort keys alphabetically, false otherwise. Has priority over sortByValue. * @param sortByValue True to sort values alphabetically, false otherwise. * @returns Array of objects with the name & value of each property. + * @deprecated since 5.0. Use CoreObject.toArrayOfObjects instead. */ objectToArrayOfObjects< A extends Record = Record, @@ -1259,53 +626,7 @@ export class CoreUtilsProvider { sortByKey?: boolean, sortByValue?: boolean, ): A[] { - // Get the entries from an object or primitive value. - const getEntries = (elKey: string, value: unknown): Record[] | unknown => { - if (value === undefined || value == null) { - // Filter undefined and null values. - return; - } else if (this.isObject(value)) { - // It's an object, return at least an entry for each property. - const keys = Object.keys(value); - let entries: unknown[] = []; - - keys.forEach((key) => { - const newElKey = elKey ? elKey + '[' + key + ']' : key; - const subEntries = getEntries(newElKey, value[key]); - - if (subEntries) { - entries = entries.concat(subEntries); - } - }); - - return entries; - } else { - // Not an object, return a single entry. - const entry = {}; - entry[keyName] = elKey; - entry[valueName] = value; - - return entry; - } - }; - - if (!obj) { - return []; - } - - // "obj" will always be an object, so "entries" will always be an array. - const entries = getEntries('', obj) as A[]; - if (sortByKey || sortByValue) { - return entries.sort((a, b) => { - if (sortByKey) { - return (a[keyName] as number) >= (b[keyName] as number) ? 1 : -1; - } else { - return (a[valueName] as number) >= (b[valueName] as number) ? 1 : -1; - } - }); - } - - return entries; + return CoreObject.toArrayOfObjects(obj, keyName, valueName, sortByKey, sortByValue); } /** @@ -1317,6 +638,7 @@ export class CoreUtilsProvider { * @param valueName Name of the properties where the values are stored. * @param keyPrefix Key prefix if neededs to delete it. * @returns Object. + * @deprecated since 5.0. Use CoreObject.toKeyValueMap instead. */ objectToKeyValueMap( objects: Record[], @@ -1324,15 +646,7 @@ export class CoreUtilsProvider { valueName: string, keyPrefix?: string, ): {[name: string]: T} { - const prefixSubstr = keyPrefix ? keyPrefix.length : 0; - const mapped = {}; - objects.forEach((item) => { - const keyValue = item[keyName] as string; - const key = prefixSubstr > 0 ? keyValue.substring(prefixSubstr) : keyValue; - mapped[key] = item[valueName]; - }); - - return mapped; + return CoreObject.toKeyValueMap(objects, keyName, valueName, keyPrefix); } /** @@ -1341,29 +655,10 @@ export class CoreUtilsProvider { * @param object Object to convert. * @param removeEmpty Whether to remove params whose value is null/undefined. * @returns GET params. + * @deprecated since 5.0. Use CoreObject.toGetParams instead. */ objectToGetParams(object: Record, removeEmpty: boolean = true): string { - // First of all, flatten the object so all properties are in the first level. - const flattened = this.flattenObject(object); - let result = ''; - let joinChar = ''; - - for (const name in flattened) { - let value = flattened[name]; - - if (removeEmpty && (value === null || value === undefined)) { - continue; - } - - if (typeof value == 'boolean') { - value = value ? 1 : 0; - } - - result += joinChar + name + '=' + value; - joinChar = '&'; - } - - return result; + return CoreObject.toGetParams(object, removeEmpty); } /** @@ -1372,6 +667,7 @@ export class CoreUtilsProvider { * @param data Object. * @param prefix Prefix to add. * @returns Prefixed object. + * @deprecated since 5.0. Not used anymore */ prefixKeys(data: Record, prefix: string): Record { const newObj = {}; @@ -1389,9 +685,10 @@ export class CoreUtilsProvider { * * @param enumeration Enumeration object. * @returns Keys of the enumeration. + * @deprecated since 5.0. Use CoreObject.enumKeys instead. */ enumKeys(enumeration: O): K[] { - return Object.keys(enumeration).filter(k => Number.isNaN(+k)) as K[]; + return CoreObject.enumKeys(enumeration); } /** @@ -1399,15 +696,10 @@ export class CoreUtilsProvider { * * @param promise Promise to check * @returns Promise resolved with boolean: true if the promise is rejected or false if it's resolved. + * @deprecated since 5.0. Use CorePromiseUtils.promiseFails instead. */ async promiseFails(promise: Promise): Promise { - try { - await promise; - - return false; - } catch { - return true; - } + return CorePromiseUtils.promiseFails(promise); } /** @@ -1415,15 +707,10 @@ export class CoreUtilsProvider { * * @param promise Promise to check * @returns Promise resolved with boolean: true if the promise it's resolved or false if it's rejected. + * @deprecated since 5.0. Use CorePromiseUtils.promiseWorks instead. */ async promiseWorks(promise: Promise): Promise { - try { - await promise; - - return true; - } catch { - return false; - } + return CorePromiseUtils.promiseWorks(promise); } /** @@ -1435,23 +722,14 @@ export class CoreUtilsProvider { * @param obj2 The second object or array. * @param key Key to check. * @returns Whether the two objects/arrays have the same value (or lack of one) for a given key. + * @deprecated since 5.0. Use CoreObject.sameAtKeyMissingIsBlank instead. */ sameAtKeyMissingIsBlank( obj1: Record | unknown[], obj2: Record | unknown[], key: string, ): boolean { - let value1 = obj1[key] !== undefined ? obj1[key] : ''; - let value2 = obj2[key] !== undefined ? obj2[key] : ''; - - if (typeof value1 == 'number' || typeof value1 == 'boolean') { - value1 = '' + value1; - } - if (typeof value2 == 'number' || typeof value2 == 'boolean') { - value2 = '' + value2; - } - - return value1 === value2; + return CoreObject.sameAtKeyMissingIsBlank(obj1, obj2, key); } /** @@ -1460,9 +738,11 @@ export class CoreUtilsProvider { * * @param obj Object to stringify. * @returns Stringified object. + * @deprecated since 5.0. Use CoreObject.sortAndStringify instead. */ sortAndStringify(obj: Record): string { - return JSON.stringify(this.sortProperties(obj)); + return CoreObject.sortAndStringify(obj); + } /** @@ -1470,19 +750,10 @@ export class CoreUtilsProvider { * * @param obj The object to sort. If it isn't an object, the original value will be returned. * @returns Sorted object. + * @deprecated since 5.0. Use CoreObject.sortProperties instead. */ sortProperties(obj: T): T { - if (obj != null && typeof obj == 'object' && !Array.isArray(obj)) { - // It's an object, sort it. - return Object.keys(obj).sort().reduce((accumulator, key) => { - // Always call sort with the value. If it isn't an object, the original value will be returned. - accumulator[key] = this.sortProperties(obj[key]); - - return accumulator; - }, {} as T); - } else { - return obj; - } + return CoreObject.sortProperties(obj); } /** @@ -1490,16 +761,10 @@ export class CoreUtilsProvider { * * @param obj The object to sort. If it isn't an object, the original value will be returned. * @returns Sorted object. + * @deprecated since 5.0. Use CoreObject.sortValues instead. */ sortValues(obj: T): T { - if (typeof obj == 'object' && !Array.isArray(obj)) { - // It's an object, sort it. Convert it to an array to be able to sort it and then convert it back to object. - const array = this.objectToArrayOfObjects(obj as Record, 'name', 'value', false, true); - - return this.objectToKeyValueMap(array, 'name', 'value') as unknown as T; - } else { - return obj; - } + return CoreObject.sortValues(obj); } /** @@ -1509,29 +774,10 @@ export class CoreUtilsProvider { * @param promise The promise to timeout. * @param time Number of milliseconds of the timeout. * @returns Promise with the timeout. + * @deprecated since 5.0. Use CorePromiseUtils.timeoutPromise instead. */ timeoutPromise(promise: Promise, time: number): Promise { - return new Promise((resolve, reject): void => { - let timedOut = false; - const resolveBeforeTimeout = (value: T) => { - if (timedOut) { - return; - } - resolve(value); - }; - const timeout = setTimeout( - () => { - reject({ timeout: true }); - timedOut = true; - }, - time, - ); - - promise - .then(resolveBeforeTimeout) - .catch(reject) - .finally(() => clearTimeout(timeout)); - }); + return CorePromiseUtils.timeoutPromise(promise, time); } /** @@ -1542,39 +788,10 @@ export class CoreUtilsProvider { * @param localeFloat Locale aware float representation. * @param strict If true, then check the input and return false if it is not a valid number. * @returns False if bad format, empty string if empty value or the parsed float if not. + * @deprecated since 5.0. Use CoreUtils.unformatFloat on singletons instead. */ unformatFloat(localeFloat: string | number | null | undefined, strict?: boolean): false | '' | number { - // Bad format on input type number. - if (localeFloat === undefined) { - return false; - } - - // Empty (but not zero). - if (localeFloat == null) { - return ''; - } - - // Convert float to string. - localeFloat = String(localeFloat); - localeFloat = localeFloat.trim(); - - if (localeFloat == '') { - return ''; - } - - localeFloat = localeFloat.replace(' ', ''); // No spaces - those might be used as thousand separators. - localeFloat = localeFloat.replace(Translate.instant('core.decsep'), '.'); - - // Use Number instead of parseFloat because the latter truncates the number when it finds ",", while Number returns NaN. - // If the number still has "," then it means it's not a valid separator. - const parsedFloat = Number(localeFloat); - - // Bad format. - if (strict && (!isFinite(parsedFloat) || isNaN(parsedFloat))) { - return false; - } - - return parsedFloat; + return CoreUtilsSingleton.unformatFloat(localeFloat, strict); } /** @@ -1595,17 +812,10 @@ export class CoreUtilsProvider { * @param fn Function to debounce. * @param delay Time that must pass until the function is called. * @returns Debounced function. + * @deprecated since 5.0. Use CoreUtils.debounce on singletons instead. */ debounce(fn: (...args: T) => unknown, delay: number): (...args: T) => void { - let timeoutID: number; - - const debounced = (...args: T): void => { - clearTimeout(timeoutID); - - timeoutID = window.setTimeout(() => fn.apply(null, args), delay); - }; - - return debounced; + return CoreUtilsSingleton.debounce(fn, delay); } /** @@ -1614,23 +824,10 @@ export class CoreUtilsProvider { * @param fn Function to throttle. * @param duration Time that must pass until the function is called. * @returns Throttled function. + * @deprecated since 5.0. Use CoreUtils.throttle on singletons instead. */ throttle(fn: (...args: T) => unknown, duration: number): (...args: T) => void { - let shouldWait = false; - - const throttled = (...args: T): void => { - if (!shouldWait) { - fn.apply(null, args); - - shouldWait = true; - - setTimeout(() => { - shouldWait = false; - }, duration); - } - }; - - return throttled; + return CoreUtilsSingleton.throttle(fn, duration); } /** @@ -1685,18 +882,16 @@ export class CoreUtilsProvider { * @param promise Promise to ignore errors. * @param fallback Value to return if the promise is rejected. * @returns Promise with ignored errors, resolving to the fallback result if provided. + * @deprecated since 5.0. Use CorePromiseUtils.ignoreErrors instead. */ async ignoreErrors(promise?: Promise): Promise; async ignoreErrors(promise: Promise, fallback: Fallback): Promise; async ignoreErrors(promise?: Promise, fallback?: Fallback): Promise { - try { - const result = await promise; - - return result; - } catch { - // Ignore errors. - return fallback; + if(promise) { + return CorePromiseUtils.ignoreErrors(promise, fallback); } + + return CorePromiseUtils.ignoreErrors(promise); } /** @@ -1749,85 +944,19 @@ export class CoreUtilsProvider { * * @param options Options. * @returns Boolean. + * @deprecated since 5.0. Use CoreOpener.shouldOpenWithDialog instead. */ - shouldOpenWithDialog(options: CoreUtilsOpenFileOptions = {}): boolean { - const openFileAction = options.iOSOpenFileAction ?? CoreConstants.CONFIG.iOSDefaultOpenFileAction; - - return CorePlatform.isIOS() && openFileAction == OpenFileAction.OPEN_WITH; + shouldOpenWithDialog(options: CoreOpenerOpenFileOptions = {}): boolean { + return CoreOpener.shouldOpenWithDialog(options); } } export const CoreUtils = makeSingleton(CoreUtilsProvider); -/** - * Data for each entry of executeOrderedPromises. - */ -export type OrderedPromiseData = { - /** - * Function to execute. - */ - function: () => Promise; - - /** - * Whether the promise should block the following one. - */ - blocking?: boolean; -}; - -/** - * Data about a country. - */ -export type CoreCountry = { - code: string; - name: string; -}; - -/** - * Menu item. - */ -export type CoreMenuItem = { - label: string; - value: T | number; -}; - -/** - * Options for opening a file. - */ -export type CoreUtilsOpenFileOptions = { - iOSOpenFileAction?: OpenFileAction; // Action to do when opening a file. -}; - -/** - * Options for opening in browser. - */ -export type CoreUtilsOpenInBrowserOptions = { - showBrowserWarning?: boolean; // Whether to display a warning before opening in browser. Defaults to true. - originalUrl?: string; // Original URL to open (in case the URL was treated, e.g. to add a token or an auto-login). - /** - * @deprecated since 4.3. Use originalUrl instead. - */ - browserWarningUrl?: string; -}; - -/** - * Options for opening in InAppBrowser. - */ -export type CoreUtilsOpenInAppOptions = InAppBrowserOptions & { - originalUrl?: string; // Original URL to open (in case the URL was treated, e.g. to add a token or an auto-login). -}; - /** * Options for waiting. * * @deprecated since 4.5. Use CoreWaitOptions instead. */ export type CoreUtilsWaitOptions = CoreWaitOptions; - -/** - * Possible default picker actions. - */ -export enum OpenFileAction { - OPEN = 'open', - OPEN_WITH = 'open-with', -} diff --git a/src/core/services/ws.ts b/src/core/services/ws.ts index 73ecddc4e11..119f4dd9dca 100644 --- a/src/core/services/ws.ts +++ b/src/core/services/ws.ts @@ -26,7 +26,7 @@ import { CoreNetwork } from '@services/network'; import { CoreFile, CoreFileFormat } from '@services/file'; import { CoreMimetypeUtils } from '@services/utils/mimetype'; import { CoreText } from '@singletons/text'; -import { CoreConstants } from '@/core/constants'; +import { CoreConstants, MINIMUM_MOODLE_VERSION } from '@/core/constants'; import { CoreError } from '@classes/errors/error'; import { CoreInterceptor } from '@classes/interceptor'; import { makeSingleton, Translate, Http, NativeHttp } from '@singletons'; @@ -35,7 +35,6 @@ import { CoreWSError } from '@classes/errors/wserror'; import { CoreAjaxError } from '@classes/errors/ajaxerror'; import { CoreAjaxWSError } from '@classes/errors/ajaxwserror'; import { CoreNetworkError } from '@classes/errors/network-error'; -import { CoreSite } from '@classes/sites/site'; import { CoreHttpError } from '@classes/errors/httperror'; import { CorePromisedValue } from '@classes/promised-value'; import { CorePlatform } from '@services/platform'; @@ -171,7 +170,7 @@ export class CoreWSProvider { if (value == null) { // Skip null or undefined value. continue; - } else if (typeof value == 'object') { + } else if (typeof value === 'object') { // Object or array. value = this.convertValuesToString(value, stripUnicode); if (value == null) { @@ -499,7 +498,7 @@ export class CoreWSProvider { } // Check if error. Ajax layer should always return an object (if error) or an array (if success). - if (!data || typeof data != 'object') { + if (!data || typeof data !== 'object') { const message = CoreSites.isLoggedIn() ? Translate.instant('core.siteunavailablehelp', { site: CoreSites.getCurrentSite()?.siteUrl }) : Translate.instant('core.sitenotfoundhelp'); @@ -592,7 +591,7 @@ export class CoreWSProvider { options.debug = { code: 'endpointnotfound', details: Translate.instant('core.ajaxendpointnotfound', { - $a: CoreSite.MINIMUM_MOODLE_VERSION, + $a: MINIMUM_MOODLE_VERSION, }), }; break; @@ -1091,7 +1090,7 @@ export class CoreWSProvider { }), }, }); - } else if (typeof data != 'object') { + } else if (typeof data !== 'object') { this.logger.warn('Upload file: Response of type "' + typeof data + '" received, expecting "object"'); throw await this.createCannotConnectSiteError(preSets.siteUrl, { diff --git a/src/core/singletons/array.ts b/src/core/singletons/array.ts index 71e885cb7ac..436d41b325a 100644 --- a/src/core/singletons/array.ts +++ b/src/core/singletons/array.ts @@ -17,6 +17,62 @@ */ export class CoreArray { + // Avoid creating singleton instances. + private constructor() { + // Nothing to do. + } + + /** + * Converts an array of objects to an object, using a property of each entry as the key. + * It can also be used to convert an array of strings to an object where the keys are the elements of the array. + * E.g. [{id: 10, name: 'A'}, {id: 11, name: 'B'}] => {10: {id: 10, name: 'A'}, 11: {id: 11, name: 'B'}} + * + * @param array The array to convert. + * @param propertyName The name of the property to use as the key. If not provided, the whole item will be used. + * @param result Object where to put the properties. If not defined, a new object will be created. + * @returns The object. + */ + static toObject( + array: T[] = [], + propertyName?: string, + result: Record = {}, + ): Record { + for (const entry of array) { + const key = propertyName ? entry[propertyName] : entry; + + result[key] = entry; + } + + return result; + } + + /** + * Converts an array of objects to an indexed array, using a property of each entry as the key. + * Every entry will contain an array of the found objects of the property identifier. + * E.g. [{id: 10, name: 'A'}, {id: 10, name: 'B'}] => {10: [ {id: 10, name: 'A'}, {id: 10, name: 'B'} ] } + * + * @param array The array to convert. + * @param propertyName The name of the property to use as the key. If not provided, the whole item will be used. + * @param result Object where to put the properties. If not defined, a new object will be created. + * @returns The object. + */ + static toObjectMultiple( + array: T[] = [], + propertyName?: string, + result: Record = {}, + ): Record { + for (const entry of array) { + const key = propertyName ? entry[propertyName] : entry; + if (result[key] === undefined) { + result[key] = []; + } + + result[key].push(entry); + } + + return result; + } + /** * Flatten the first dimension of a multi-dimensional array. * @@ -93,4 +149,40 @@ export class CoreArray { }); } + /** + * Merge two arrays, removing duplicate values. + * + * @param array1 The first array. + * @param array2 The second array. + * @param [key] Key of the property that must be unique. If not specified, the whole entry. + * @returns Merged array. + */ + static mergeWithoutDuplicates(array1: T[], array2: T[], key?: string): T[] { + return CoreArray.unique(array1.concat(array2), key) as T[]; + } + + /** + * Gets the index of the first string that matches a regular expression. + * + * @param array Array to search. + * @param regex RegExp to apply to each string. + * @returns Index of the first string that matches the RegExp. -1 if not found. + */ + static indexOfRegexp(array: string[], regex: RegExp): number { + if (!array || !array.length) { + return -1; + } + + for (let i = 0; i < array.length; i++) { + const entry = array[i]; + const matches = entry.match(regex); + + if (matches && matches.length) { + return i; + } + } + + return -1; + } + } diff --git a/src/core/singletons/browser.ts b/src/core/singletons/browser.ts index 5ff1d3ddefa..30628431b26 100644 --- a/src/core/singletons/browser.ts +++ b/src/core/singletons/browser.ts @@ -14,9 +14,16 @@ /** * Helpers to interact with Browser APIs. + * + * This singleton is not necessary to be exported for site plugins. */ export class CoreBrowser { + // Avoid creating singleton instances. + private constructor() { + // Nothing to do. + } + /** * Check whether the given cookie is set. * @@ -34,9 +41,9 @@ export class CoreBrowser { * @returns Whether the development setting is set. */ static hasDevelopmentSetting(name: string): boolean { - const setting = this.getDevelopmentSettingKey(name); + const setting = CoreBrowser.getDevelopmentSettingKey(name); - return this.hasCookie(setting) || this.hasLocalStorage(setting); + return CoreBrowser.hasCookie(setting) || CoreBrowser.hasLocalStorage(setting); } /** @@ -84,9 +91,9 @@ export class CoreBrowser { * @returns Development setting value. */ static getDevelopmentSetting(name: string): string | null { - const setting = this.getDevelopmentSettingKey(name); + const setting = CoreBrowser.getDevelopmentSettingKey(name); - return this.getCookie(setting) ?? this.getLocalStorage(setting); + return CoreBrowser.getCookie(setting) ?? CoreBrowser.getLocalStorage(setting); } /** @@ -96,7 +103,7 @@ export class CoreBrowser { * @param value Setting value. */ static setDevelopmentSetting(name: string, value: string): void { - const setting = this.getDevelopmentSettingKey(name); + const setting = CoreBrowser.getDevelopmentSettingKey(name); document.cookie = `${setting}=${value};path=/`; localStorage.setItem(setting, value); @@ -108,7 +115,7 @@ export class CoreBrowser { * @param name Setting name. */ static clearDevelopmentSetting(name: string): void { - const setting = this.getDevelopmentSettingKey(name); + const setting = CoreBrowser.getDevelopmentSettingKey(name); document.cookie = `${setting}=;path=/;expires=Thu, 01 Jan 1970 00:00:01 GMT`; localStorage.removeItem(setting); diff --git a/src/core/singletons/colors.ts b/src/core/singletons/colors.ts index 806fffe7412..8fdd18acc67 100644 --- a/src/core/singletons/colors.ts +++ b/src/core/singletons/colors.ts @@ -42,6 +42,11 @@ export enum CoreIonicColorNames { */ export class CoreColors { + // Avoid creating singleton instances. + private constructor() { + // Nothing to do. + } + /** * Returns better contrast color. * @@ -99,7 +104,7 @@ export class CoreColors { } const hex = [0,1,2].map( - (idx) => this.componentToHex(rgba[idx]), + (idx) => CoreColors.componentToHex(rgba[idx]), ).join(''); return '#' + hex; diff --git a/src/core/singletons/countries.ts b/src/core/singletons/countries.ts new file mode 100644 index 00000000000..9a0bdbb91ea --- /dev/null +++ b/src/core/singletons/countries.ts @@ -0,0 +1,137 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { CoreLang } from '@services/lang'; +import { Translate } from '@singletons'; + +/** + * Singleton with helper functions for country lists. + */ +export class CoreCountries { + + // Avoid creating singleton instances. + private constructor() { + // Nothing to do. + } + + /** + * Get country name based on country code. + * + * @param code Country code (AF, ES, US, ...). + * @returns Country name. If the country is not found, return the country code. + */ + static getCountryName(code: string): string { + const countryKey = 'assets.countries.' + code; + const countryName = Translate.instant(countryKey); + + return countryName !== countryKey ? countryName : code; + } + + /** + * Get list of countries with their code and translated name. + * + * @returns Promise resolved with the list of countries. + */ + static async getCountryList(): Promise> { + // Get the keys of the countries. + const keys = await CoreCountries.getCountryKeysList(); + + // Now get the code and the translated name. + const countries: Record = {}; + + keys.forEach((key) => { + if (key.indexOf('assets.countries.') === 0) { + const code = key.replace('assets.countries.', ''); + countries[code] = Translate.instant(key); + } + }); + + return countries; + } + + /** + * Get list of countries with their code and translated name. Sorted by the name of the country. + * + * @returns Promise resolved with the list of countries. + */ + static async getCountryListSorted(): Promise { + // Get the keys of the countries. + const countries = await CoreCountries.getCountryList(); + + // Sort translations. + return Object.keys(countries) + .sort((a, b) => countries[a].localeCompare(countries[b])) + .map((code) => ({ code, name: countries[code] })); + } + + /** + * Get the list of language keys of the countries. + * + * @returns Promise resolved with the countries list. Rejected if not translated. + */ + protected static async getCountryKeysList(): Promise { + // It's possible that the current language isn't translated, so try with default language first. + const defaultLang = CoreLang.getDefaultLanguage(); + + try { + return await CoreCountries.getCountryKeysListForLanguage(defaultLang); + } catch { + // Not translated, try to use the fallback language. + const fallbackLang = CoreLang.getFallbackLanguage(); + + if (fallbackLang === defaultLang) { + // Same language, just reject. + throw new Error('Countries not found.'); + } + + return CoreCountries.getCountryKeysListForLanguage(fallbackLang); + } + } + + /** + * Get the list of language keys of the countries, based on the translation table for a certain language. + * + * @param lang Language to check. + * @returns Promise resolved with the countries list. Rejected if not translated. + */ + protected static async getCountryKeysListForLanguage(lang: string): Promise { + // Get the translation table for the language. + const table = await CoreLang.getTranslationTable(lang); + + // Gather all the keys for countries, + const keys: string[] = []; + + for (const name in table) { + if (name.indexOf('assets.countries.') === 0) { + keys.push(name); + } + } + + if (keys.length === 0) { + // Not translated, reject. + throw new Error('Countries not found.'); + } + + return keys; + } + +} + +/** + * Data about a country. + */ +export type CoreCountry = { + code: string; + name: string; +}; diff --git a/src/core/singletons/directives-registry.ts b/src/core/singletons/directives-registry.ts index 50fc996b69e..47e91157157 100644 --- a/src/core/singletons/directives-registry.ts +++ b/src/core/singletons/directives-registry.ts @@ -32,9 +32,9 @@ export class CoreDirectivesRegistry { * @param instance Directive instance. */ static register(element: Element, instance: unknown): void { - const list = this.instances.get(element) ?? []; + const list = CoreDirectivesRegistry.instances.get(element) ?? []; list.push(instance); - this.instances.set(element, list); + CoreDirectivesRegistry.instances.set(element, list); } /** @@ -45,7 +45,7 @@ export class CoreDirectivesRegistry { * @returns Directive instance. */ static resolve(element?: Element | null, directiveClass?: DirectiveConstructor): T | null { - const list = (element && this.instances.get(element) as T[]) ?? []; + const list = (element && CoreDirectivesRegistry.instances.get(element) as T[]) ?? []; return list.find(instance => !directiveClass || instance instanceof directiveClass) ?? null; } @@ -58,7 +58,7 @@ export class CoreDirectivesRegistry { * @returns Directive instances. */ static resolveAll(element?: Element | null, directiveClass?: DirectiveConstructor): T[] { - const list = (element && this.instances.get(element) as T[]) ?? []; + const list = (element && CoreDirectivesRegistry.instances.get(element) as T[]) ?? []; return list.filter(instance => !directiveClass || instance instanceof directiveClass) ?? []; } @@ -71,7 +71,7 @@ export class CoreDirectivesRegistry { * @returns Directive instance. */ static require(element: Element, directiveClass?: DirectiveConstructor): T { - const instance = this.resolve(element, directiveClass); + const instance = CoreDirectivesRegistry.resolve(element, directiveClass); if (!instance) { throw new Error('Couldn\'t resolve directive instance'); @@ -91,9 +91,9 @@ export class CoreDirectivesRegistry { element: Element | null, directiveClass?: DirectiveConstructor, ): Promise { - const instance = this.resolve(element, directiveClass); + const instance = CoreDirectivesRegistry.resolve(element, directiveClass); if (!instance) { - this.logger.error('No instance registered for element ' + directiveClass, element); + CoreDirectivesRegistry.logger.error('No instance registered for element ' + directiveClass, element); return; } @@ -129,7 +129,7 @@ export class CoreDirectivesRegistry { } await Promise.all(elements.map(async element => { - const instances = this.resolveAll(element, directiveClass); + const instances = CoreDirectivesRegistry.resolveAll(element, directiveClass); await Promise.all(instances.map(instance => instance.ready())); })); @@ -139,7 +139,7 @@ export class CoreDirectivesRegistry { // Check if there are new elements now that the found elements are ready (there could be nested elements). if (elements.length !== findElements().length) { - await this.waitDirectivesReady(element, selector, directiveClass); + await CoreDirectivesRegistry.waitDirectivesReady(element, selector, directiveClass); } } @@ -174,7 +174,7 @@ export class CoreDirectivesRegistry { allElements = allElements.concat(elements); await Promise.all(elements.map(async element => { - const instances = this.resolveAll(element, directive.class); + const instances = CoreDirectivesRegistry.resolveAll(element, directive.class); await Promise.all(instances.map(instance => instance.ready())); })); @@ -191,7 +191,7 @@ export class CoreDirectivesRegistry { }, []); if (allElements.length !== elementsAfterReady.length) { - await this.waitMultipleDirectivesReady(element, directives); + await CoreDirectivesRegistry.waitMultipleDirectivesReady(element, directives); } } diff --git a/src/core/singletons/dom.ts b/src/core/singletons/dom.ts index c35b87ed686..2e91e4124fe 100644 --- a/src/core/singletons/dom.ts +++ b/src/core/singletons/dom.ts @@ -13,7 +13,7 @@ // limitations under the License. import { CoreCancellablePromise } from '@classes/cancellable-promise'; -import { CoreUtils } from '@services/utils/utils'; +import { CoreUtils } from '@singletons/utils'; import { CoreEventObserver } from '@singletons/events'; import { CorePlatform } from '@services/platform'; import { CoreWait } from './wait'; @@ -512,7 +512,7 @@ export class CoreDom { return resolve(); } - unsubscribe = this.watchElementInViewport(element, intersectionRatio, inViewport => { + unsubscribe = CoreDom.watchElementInViewport(element, intersectionRatio, inViewport => { if (!inViewport) { return; } @@ -632,7 +632,7 @@ export class CoreDom { const value = styles.getPropertyValue(property); if (property === 'font-size') { - if (this.fontSizeZoom === null) { + if (CoreDom.fontSizeZoom === null) { const baseFontSize = 20; const span = document.createElement('span'); span.style.opacity = '0'; @@ -640,13 +640,13 @@ export class CoreDom { document.body.append(span); - this.fontSizeZoom = baseFontSize / Number(getComputedStyle(span).fontSize.slice(0, -2)); + CoreDom.fontSizeZoom = baseFontSize / Number(getComputedStyle(span).fontSize.slice(0, -2)); span.remove(); } - if (this.fontSizeZoom !== 1) { - return `calc(${this.fontSizeZoom} * ${value})`; + if (CoreDom.fontSizeZoom !== 1) { + return `calc(${CoreDom.fontSizeZoom} * ${value})`; } } diff --git a/src/core/singletons/error-logs.ts b/src/core/singletons/error-logs.ts index 8f828034b37..f0b02c73ba2 100644 --- a/src/core/singletons/error-logs.ts +++ b/src/core/singletons/error-logs.ts @@ -11,24 +11,26 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -import { Injectable } from '@angular/core'; -import { makeSingleton } from '@singletons'; /** * Service that stores error logs in memory. */ -@Injectable({ providedIn: 'root' }) -export class CoreErrorLogsService { +export class CoreErrorLogs { - protected errorLogs: CoreSettingsErrorLog[] = []; + protected static errorLogs: CoreSettingsErrorLog[] = []; + + // Avoid creating singleton instances. + private constructor() { + // Nothing to do. + } /** * Retrieve error logs displayed in the DOM. * * @returns Error logs */ - getErrorLogs(): CoreSettingsErrorLog[] { - return this.errorLogs; + static getErrorLogs(): CoreSettingsErrorLog[] { + return CoreErrorLogs.errorLogs; } /** @@ -36,14 +38,12 @@ export class CoreErrorLogsService { * * @param error Error. */ - addErrorLog(error: CoreSettingsErrorLog): void { - this.errorLogs.push(error); + static addErrorLog(error: CoreSettingsErrorLog): void { + CoreErrorLogs.errorLogs.push(error); } } -export const CoreErrorLogs = makeSingleton(CoreErrorLogsService); - export type CoreSettingsErrorLog = { data?: unknown; message: string; diff --git a/src/core/singletons/events.ts b/src/core/singletons/events.ts index 53ec4d87152..df42a0a9681 100644 --- a/src/core/singletons/events.ts +++ b/src/core/singletons/events.ts @@ -122,6 +122,11 @@ export class CoreEvents { protected static observables: { [eventName: string]: Subject } = {}; protected static uniqueEvents: { [eventName: string]: {data: unknown} } = {}; + // Avoid creating singleton instances. + private constructor() { + // Nothing to do. + } + /** * Listen for a certain event. To stop listening to the event: * let observer = eventsProvider.on('something', myCallBack); @@ -140,8 +145,8 @@ export class CoreEvents { ): CoreEventObserver { // If it's a unique event and has been triggered already, call the callBack. // We don't need to create an observer because the event won't be triggered again. - if (this.uniqueEvents[eventName]) { - callBack(this.uniqueEvents[eventName].data as CoreEventData & CoreEventSiteData); + if (CoreEvents.uniqueEvents[eventName]) { + callBack(CoreEvents.uniqueEvents[eventName].data as CoreEventData & CoreEventSiteData); // Return a fake observer to prevent errors. return { @@ -151,14 +156,14 @@ export class CoreEvents { }; } - this.logger.debug(`New observer listening to event '${eventName}'`); + CoreEvents.logger.debug(`New observer listening to event '${eventName}'`); - if (this.observables[eventName] === undefined) { + if (CoreEvents.observables[eventName] === undefined) { // No observable for this event, create a new one. - this.observables[eventName] = new Subject(); + CoreEvents.observables[eventName] = new Subject(); } - const subscription = this.observables[eventName].subscribe( + const subscription = CoreEvents.observables[eventName].subscribe( (value: CoreEventData & CoreEventSiteData) => { if (!siteId || value.siteId == siteId) { callBack(value); @@ -169,7 +174,7 @@ export class CoreEvents { // Create and return a CoreEventObserver. return { off: (): void => { - this.logger.debug(`Stop listening to event '${eventName}'`); + CoreEvents.logger.debug(`Stop listening to event '${eventName}'`); subscription.unsubscribe(); }, }; @@ -211,7 +216,7 @@ export class CoreEvents { * @returns Observer to stop listening. */ static onMultiple(eventNames: string[], callBack: (value: T) => void, siteId?: string): CoreEventObserver { - const observers = eventNames.map((name) => this.on(name, callBack, siteId)); + const observers = eventNames.map((name) => CoreEvents.on(name, callBack, siteId)); // Create and return a CoreEventObserver. return { @@ -235,12 +240,12 @@ export class CoreEvents { data?: CoreEventData, siteId?: string, ): void { - this.logger.debug(`Event '${eventName}' triggered.`); - if (this.observables[eventName]) { + CoreEvents.logger.debug(`Event '${eventName}' triggered.`); + if (CoreEvents.observables[eventName]) { if (siteId) { Object.assign(data || {}, { siteId }); } - this.observables[eventName].next(data || {}); + CoreEvents.observables[eventName].next(data || {}); } } @@ -256,23 +261,23 @@ export class CoreEvents { data: CoreEventData, siteId?: string, ): void { - if (this.uniqueEvents[eventName]) { - this.logger.debug(`Unique event '${eventName}' ignored because it was already triggered.`); + if (CoreEvents.uniqueEvents[eventName]) { + CoreEvents.logger.debug(`Unique event '${eventName}' ignored because it was already triggered.`); } else { - this.logger.debug(`Unique event '${eventName}' triggered.`); + CoreEvents.logger.debug(`Unique event '${eventName}' triggered.`); if (siteId) { Object.assign(data || {}, { siteId }); } // Store the data so it can be passed to observers that register from now on. - this.uniqueEvents[eventName] = { + CoreEvents.uniqueEvents[eventName] = { data, }; // Now pass the data to observers. - if (this.observables[eventName]) { - this.observables[eventName].next(data); + if (CoreEvents.observables[eventName]) { + CoreEvents.observables[eventName].next(data); } } } @@ -283,7 +288,7 @@ export class CoreEvents { * @param eventName Event name. */ static waitUntil(eventName: string): Promise { - return new Promise(resolve => this.once(eventName, () => resolve())); + return new Promise(resolve => CoreEvents.once(eventName, () => resolve())); } } diff --git a/src/core/singletons/file-utils.ts b/src/core/singletons/file-utils.ts new file mode 100644 index 00000000000..a7861eba083 --- /dev/null +++ b/src/core/singletons/file-utils.ts @@ -0,0 +1,103 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { CoreFileEntry } from '@services/file-helper'; +import { FileEntry } from '@awesome-cordova-plugins/file/ngx'; +import { Translate } from '@singletons'; + +/** + * Helpers to interact with the file system. + */ +export class CoreFileUtils { + + // Avoid creating singleton instances. + private constructor() { + // Nothing to do. + } + + /** + * Check if a file is a FileEntry + * + * @param file File. + * @returns Type guard indicating if the file is a FileEntry. + */ + static isFileEntry(file: CoreFileEntry): file is FileEntry { + return 'isFile' in file; + } + + /** + * Check if an unknown value is a FileEntry. + * + * @param file Object to check. + * @returns Type guard indicating if the file is a FileEntry. + */ + static valueIsFileEntry(file: unknown): file is FileEntry { + // We cannot use instanceof because FileEntry is a type. Check some of the properties. + return !!(file && typeof file === 'object' && 'isFile' in file && 'filesystem' in file && + 'toInternalURL' in file && 'copyTo' in file); + } + + /** + * Given a list of files, check if there are repeated names. + * + * @param files List of files. + * @returns String with error message if repeated, false if no repeated. + */ + static hasRepeatedFilenames(files: CoreFileEntry[]): string | false { + if (!files || !files.length) { + return false; + } + + const names: string[] = []; + + // Check if there are 2 files with the same name. + for (let i = 0; i < files.length; i++) { + const file = files[i]; + const name = (CoreFileUtils.isFileEntry(file) ? file.name : file.filename) || ''; + + if (names.indexOf(name) > -1) { + return Translate.instant('core.filenameexist', { $a: name }); + } + + names.push(name); + } + + return false; + } + + /** + * Extract the file name and directory from a given path. + * + * @param path Path to be extracted. + * @returns Plain object containing the file name and directory. + * @description + * file.pdf -> directory: '', name: 'file.pdf' + * /file.pdf -> directory: '', name: 'file.pdf' + * path/file.pdf -> directory: 'path', name: 'file.pdf' + * path/ -> directory: 'path', name: '' + * path -> directory: '', name: 'path' + */ + static getFileAndDirectoryFromPath(path: string): {directory: string; name: string} { + const file = { + directory: '', + name: '', + }; + + file.directory = path.substring(0, path.lastIndexOf('/')); + file.name = path.substring(path.lastIndexOf('/') + 1); + + return file; + } + +} diff --git a/src/core/singletons/form.ts b/src/core/singletons/form.ts index 5cdc0d77c3c..80ccda79fb3 100644 --- a/src/core/singletons/form.ts +++ b/src/core/singletons/form.ts @@ -22,6 +22,11 @@ export class CoreForms { private static formIds: Record = {}; + // Avoid creating singleton instances. + private constructor() { + // Nothing to do. + } + /** * Get the data from a form. It will only collect elements that have a name. * @@ -102,9 +107,9 @@ export class CoreForms { * @returns Unique id. */ static uniqueId(name: string): string { - const count = this.formIds[name] ?? 0; + const count = CoreForms.formIds[name] ?? 0; - return `${name}-${this.formIds[name] = count + 1}`; + return `${name}-${CoreForms.formIds[name] = count + 1}`; } } diff --git a/src/core/singletons/html-classes.ts b/src/core/singletons/html-classes.ts index 9803f1e1954..93ac3ef88cc 100644 --- a/src/core/singletons/html-classes.ts +++ b/src/core/singletons/html-classes.ts @@ -29,6 +29,11 @@ export class CoreHTMLClasses { protected static readonly MOODLEAPP_VERSION_PREFIX = 'moodleapp-'; protected static readonly MOODLE_SITE_THEME_PREFIX = 'theme-site-'; + // Avoid creating singleton instances. + private constructor() { + // Nothing to do. + } + /** * Initialize HTML classes. */ @@ -84,7 +89,7 @@ export class CoreHTMLClasses { * * @param prefixes Prefixes of the class mode to be removed. */ - protected static removeModeClasses(prefixes: string[]): void { + static removeModeClasses(prefixes: string[]): void { for (const modeClass of CoreHTMLClasses.getModeClasses()) { if (!prefixes.some((prefix) => modeClass.startsWith(prefix))) { continue; @@ -101,10 +106,10 @@ export class CoreHTMLClasses { */ static addSiteClasses(siteInfo: CoreSiteInfo | CoreSiteInfoResponse): void { // Add version classes to html tag. - this.removeSiteClasses(); + CoreHTMLClasses.removeSiteClasses(); - this.addVersionClass(CoreHTMLClasses.MOODLE_VERSION_PREFIX, CoreSites.getReleaseNumber(siteInfo.release || '')); - this.addSiteUrlClass(siteInfo.siteurl); + CoreHTMLClasses.addVersionClass(CoreHTMLClasses.MOODLE_VERSION_PREFIX, CoreSites.getReleaseNumber(siteInfo.release || '')); + CoreHTMLClasses.addSiteUrlClass(siteInfo.siteurl); if (siteInfo.theme) { CoreHTMLClasses.toggleModeClass(CoreHTMLClasses.MOODLE_SITE_THEME_PREFIX + siteInfo.theme, true); @@ -116,7 +121,7 @@ export class CoreHTMLClasses { */ static removeSiteClasses(): void { // Remove version classes from html tag. - this.removeModeClasses( + CoreHTMLClasses.removeModeClasses( [ CoreHTMLClasses.MOODLE_VERSION_PREFIX, CoreHTMLClasses.MOODLE_SITE_URL_PREFIX, @@ -161,7 +166,7 @@ export class CoreHTMLClasses { * Convenience function to add site url to html classes. */ static addSiteUrlClass(siteUrl: string): void { - const className = this.urlToClassName(siteUrl); + const className = CoreHTMLClasses.urlToClassName(siteUrl); CoreHTMLClasses.toggleModeClass(CoreHTMLClasses.MOODLE_SITE_URL_PREFIX + className, true); } diff --git a/src/core/singletons/icons.ts b/src/core/singletons/icons.ts index 2a49c7c2811..de179372fec 100644 --- a/src/core/singletons/icons.ts +++ b/src/core/singletons/icons.ts @@ -28,6 +28,11 @@ export class CoreIcons { protected static logger = CoreLogger.getInstance('CoreIcons'); + // Avoid creating singleton instances. + private constructor() { + // Nothing to do. + } + /** * Add custom icons to Ionicons. */ @@ -47,7 +52,7 @@ export class CoreIcons { if (CoreIcons.ALIASES[icon]) { if (isAppIcon) { - this.logger.error(`Icon ${icon} is an alias of ${CoreIcons.ALIASES[icon]}, please use the new name.`); + CoreIcons.logger.error(`Icon ${icon} is an alias of ${CoreIcons.ALIASES[icon]}, please use the new name.`); } return { newLibrary, fileName: CoreIcons.ALIASES[icon] }; @@ -73,7 +78,7 @@ export class CoreIcons { CoreIcons.CUSTOM_ICONS[icon] === undefined && CoreIcons.CUSTOM_ICONS[CoreIcons.prefixIconName(font, library, icon)] === undefined ) { - this.logger.error(`Icon ${icon} not found`); + CoreIcons.logger.error(`Icon ${icon} not found`); } } diff --git a/src/core/singletons/keyboard.ts b/src/core/singletons/keyboard.ts index bb44c0d4327..2c3c0126943 100644 --- a/src/core/singletons/keyboard.ts +++ b/src/core/singletons/keyboard.ts @@ -25,6 +25,11 @@ export class CoreKeyboard { protected static keyboardOpening = false; protected static keyboardClosing = false; + // Avoid creating singleton instances. + private constructor() { + // Nothing to do. + } + /** * Closes the keyboard. */ @@ -51,7 +56,7 @@ export class CoreKeyboard { */ static onKeyboardShow(keyboardHeight: number): void { document.body.classList.add('keyboard-is-open'); - this.setKeyboardShown(true); + CoreKeyboard.setKeyboardShown(true); // Error on iOS calculating size. // More info: https://github.com/ionic-team/ionic-plugin-keyboard/issues/276 . CoreEvents.trigger(CoreEvents.KEYBOARD_CHANGE, keyboardHeight); @@ -62,7 +67,7 @@ export class CoreKeyboard { */ static onKeyboardHide(): void { document.body.classList.remove('keyboard-is-open'); - this.setKeyboardShown(false); + CoreKeyboard.setKeyboardShown(false); CoreEvents.trigger(CoreEvents.KEYBOARD_CHANGE, 0); } diff --git a/src/core/singletons/math.ts b/src/core/singletons/math.ts index af51906ae75..d1bb2a9d67b 100644 --- a/src/core/singletons/math.ts +++ b/src/core/singletons/math.ts @@ -17,6 +17,11 @@ */ export class CoreMath { + // Avoid creating singleton instances. + private constructor() { + // Nothing to do. + } + /** * Clamp a value between a minimum and a maximum. * diff --git a/src/core/singletons/media.ts b/src/core/singletons/media.ts index eebca879358..2525f5fb5e4 100644 --- a/src/core/singletons/media.ts +++ b/src/core/singletons/media.ts @@ -93,6 +93,24 @@ export class CoreMedia { return sources.some(source => CoreMedia.sourceUsesJavascriptPlayer(source)); } + /** + * Check if the browser supports mediaDevices.getUserMedia. + * + * @returns Whether the function is supported. + */ + static canGetUserMedia(): boolean { + return !!(navigator && navigator.mediaDevices && navigator.mediaDevices.getUserMedia); + } + + /** + * Check if the browser supports MediaRecorder. + * + * @returns Whether the function is supported. + */ + static canRecordMedia(): boolean { + return !!window.MediaRecorder; + } + } /** diff --git a/src/core/singletons/object.ts b/src/core/singletons/object.ts index 00e8dd65fd2..5ed742f1f7c 100644 --- a/src/core/singletons/object.ts +++ b/src/core/singletons/object.ts @@ -30,6 +30,11 @@ export type CoreObjectWithoutUndefined = Pretty<{ */ export class CoreObject { + // Avoid creating singleton instances. + private constructor() { + // Nothing to do. + } + /** * Returns a value of an object and deletes it from the object. * @@ -170,4 +175,335 @@ export class CoreObject { return cleanObj as CoreObjectWithoutUndefined; } + /** + * Tests to see whether two arrays or objects have the same value at a particular key. + * Missing values are replaced by '', and the values are compared with ===. + * Booleans and numbers are cast to string before comparing. + * + * @param obj1 The first object or array. + * @param obj2 The second object or array. + * @param key Key to check. + * @returns Whether the two objects/arrays have the same value (or lack of one) for a given key. + */ + static sameAtKeyMissingIsBlank( + obj1: Record | unknown[], + obj2: Record | unknown[], + key: string, + ): boolean { + let value1 = obj1[key] !== undefined ? obj1[key] : ''; + let value2 = obj2[key] !== undefined ? obj2[key] : ''; + + if (typeof value1 == 'number' || typeof value1 == 'boolean') { + value1 = '' + value1; + } + if (typeof value2 == 'number' || typeof value2 == 'boolean') { + value2 = '' + value2; + } + + return value1 === value2; + } + + /** + * Stringify an object, sorting the properties. It doesn't sort arrays, only object properties. E.g.: + * {b: 2, a: 1} -> '{"a":1,"b":2}' + * + * @param obj Object to stringify. + * @returns Stringified object. + */ + static sortAndStringify(obj: Record): string { + return JSON.stringify(CoreObject.sortProperties(obj)); + } + + /** + * Given an object, sort its properties and the properties of all the nested objects. + * + * @param obj The object to sort. If it isn't an object, the original value will be returned. + * @returns Sorted object. + */ + static sortProperties(obj: T): T { + if (obj != null && typeof obj === 'object' && !Array.isArray(obj)) { + // It's an object, sort it. + return Object.keys(obj).sort().reduce((accumulator, key) => { + // Always call sort with the value. If it isn't an object, the original value will be returned. + accumulator[key] = CoreObject.sortProperties(obj[key]); + + return accumulator; + }, {} as T); + } + + return obj; + } + + /** + * Given an object, sort its values. Values need to be primitive values, it cannot have subobjects. + * + * @param obj The object to sort. If it isn't an object, the original value will be returned. + * @returns Sorted object. + */ + static sortValues(obj: T): T { + if (typeof obj === 'object' && !Array.isArray(obj)) { + // It's an object, sort it. Convert it to an array to be able to sort it and then convert it back to object. + const array = CoreObject.toArrayOfObjects(obj as Record, 'name', 'value', false, true); + + return CoreObject.toKeyValueMap(array, 'name', 'value') as unknown as T; + } + + return obj; + } + + /** + * Converts an object into an array, losing the keys. + * + * @param obj Object to convert. + * @returns Array with the values of the object but losing the keys. + */ + static toArray(obj: Record): T[] { + return Object.keys(obj).map((key) => obj[key]); + } + + /** + * Converts an object into an array of objects, where each entry is an object containing + * the key and value of the original object. + * For example, it can convert {size: 2} into [{name: 'size', value: 2}]. + * + * @param obj Object to convert. + * @param keyName Name of the properties where to store the keys. + * @param valueName Name of the properties where to store the values. + * @param sortByKey True to sort keys alphabetically, false otherwise. Has priority over sortByValue. + * @param sortByValue True to sort values alphabetically, false otherwise. + * @returns Array of objects with the name & value of each property. + */ + static toArrayOfObjects< + A extends Record = Record, + O extends Record = Record + >( + obj: O, + keyName: string, + valueName: string, + sortByKey?: boolean, + sortByValue?: boolean, + ): A[] { + // Get the entries from an object or primitive value. + const getEntries = (elKey: string, value: unknown): Record[] | unknown => { + if (value === undefined || value == null) { + // Filter undefined and null values. + return; + } else if (CoreObject.isObject(value)) { + // It's an object, return at least an entry for each property. + const keys = Object.keys(value); + let entries: unknown[] = []; + + keys.forEach((key) => { + const newElKey = elKey ? elKey + '[' + key + ']' : key; + const subEntries = getEntries(newElKey, value[key]); + + if (subEntries) { + entries = entries.concat(subEntries); + } + }); + + return entries; + } else { + // Not an object, return a single entry. + const entry = {}; + entry[keyName] = elKey; + entry[valueName] = value; + + return entry; + } + }; + + if (!obj) { + return []; + } + + // "obj" will always be an object, so "entries" will always be an array. + const entries = getEntries('', obj) as A[]; + if (sortByKey || sortByValue) { + return entries.sort((a, b) => { + if (sortByKey) { + return (a[keyName] as number) >= (b[keyName] as number) ? 1 : -1; + } else { + return (a[valueName] as number) >= (b[valueName] as number) ? 1 : -1; + } + }); + } + + return entries; + } + + /** + * Converts an array of objects into an object with key and value. The opposite of objectToArrayOfObjects. + * For example, it can convert [{name: 'size', value: 2}] into {size: 2}. + * + * @param objects List of objects to convert. + * @param keyName Name of the properties where the keys are stored. + * @param valueName Name of the properties where the values are stored. + * @param keyPrefix Key prefix if neededs to delete it. + * @returns Object. + */ + static toKeyValueMap( + objects: Record[], + keyName: string, + valueName: string, + keyPrefix?: string, + ): {[name: string]: T} { + const prefixSubstr = keyPrefix ? keyPrefix.length : 0; + const mapped = {}; + objects.forEach((item) => { + const keyValue = item[keyName] as string; + const key = prefixSubstr > 0 ? keyValue.substring(prefixSubstr) : keyValue; + mapped[key] = item[valueName]; + }); + + return mapped; + } + + /** + * Convert an object to a format of GET param. E.g.: {a: 1, b: 2} -> a=1&b=2 + * + * @param object Object to convert. + * @param removeEmpty Whether to remove params whose value is null/undefined. + * @returns GET params. + */ + static toGetParams(object: Record, removeEmpty: boolean = true): string { + // First of all, flatten the object so all properties are in the first level. + const flattened = CoreObject.flatten(object); + let result = ''; + let joinChar = ''; + + for (const name in flattened) { + let value = flattened[name]; + + if (removeEmpty && (value === null || value === undefined)) { + continue; + } + + if (typeof value === 'boolean') { + value = value ? 1 : 0; + } + + result += joinChar + name + '=' + value; + joinChar = '&'; + } + + return result; + } + + /** + * Function to enumerate enum keys. + * + * @param enumeration Enumeration object. + * @returns Keys of the enumeration. + */ + static enumKeys(enumeration: O): K[] { + return Object.keys(enumeration).filter(k => Number.isNaN(+k)) as K[]; + } + + /** + * Check if a value is an object. + * + * @param object Variable. + * @returns Type guard indicating if this is an object. + */ + static isObject(object: unknown): object is Record { + return typeof object === 'object' && object !== null; + } + + /** + * Flatten an object, moving subobjects' properties to the first level. + * It supports 2 notations: dot notation and square brackets. + * E.g.: {a: {b: 1, c: 2}, d: 3} -> {'a.b': 1, 'a.c': 2, d: 3} + * + * @param obj Object to flatten. + * @param useDotNotation Whether to use dot notation '.' or square brackets '['. + * @returns Flattened object. + */ + static flatten(obj: Record, useDotNotation?: boolean): Record { + const toReturn = {}; + + for (const name in obj) { + if (!Object.prototype.hasOwnProperty.call(obj, name)) { + continue; + } + + const value = obj[name]; + if (typeof value === 'object' && !Array.isArray(value)) { + const flatObject = CoreObject.flatten(value as Record); + for (const subName in flatObject) { + if (!Object.prototype.hasOwnProperty.call(flatObject, subName)) { + continue; + } + + const newName = useDotNotation ? name + '.' + subName : name + '[' + subName + ']'; + toReturn[newName] = flatObject[subName]; + } + } else { + toReturn[name] = value; + } + } + + return toReturn; + } + + /** + * Compare two objects. This function won't compare functions and proto properties, it's a basic compare. + * Also, this will only check if itemA's properties are in itemB with same value. This function will still + * return true if itemB has more properties than itemA. + * + * @param itemA First object. + * @param itemB Second object. + * @param maxLevels Number of levels to reach if 2 objects are compared. + * @param level Current deep level (when comparing objects). + * @param undefinedIsNull True if undefined is equal to null. Defaults to true. + * @returns Whether both items are equal. + */ + static basicLeftCompare( + itemA: any, // eslint-disable-line @typescript-eslint/no-explicit-any + itemB: any, // eslint-disable-line @typescript-eslint/no-explicit-any + maxLevels: number = 0, + level: number = 0, + undefinedIsNull: boolean = true, + ): boolean { + if (typeof itemA == 'function' || typeof itemB == 'function') { + return true; // Don't compare functions. + } + + if (typeof itemA === 'object' && typeof itemB === 'object') { + if (level >= maxLevels) { + return true; // Max deep reached. + } + + let equal = true; + for (const name in itemA) { + const value = itemA[name]; + if (name == '$$hashKey') { + // Ignore $$hashKey property since it's a "calculated" property. + continue; + } + + if (!CoreObject.basicLeftCompare(value, itemB[name], maxLevels, level + 1)) { + equal = false; + } + } + + return equal; + } + + if (undefinedIsNull && ( + (itemA === undefined && itemB === null) || (itemA === null && itemB === undefined))) { + return true; + } + + // We'll treat "2" and 2 as the same value. + const floatA = parseFloat(itemA); + const floatB = parseFloat(itemB); + + if (!isNaN(floatA) && !isNaN(floatB)) { + return floatA == floatB; + } + + return itemA === itemB; + } + } diff --git a/src/core/singletons/opener.ts b/src/core/singletons/opener.ts new file mode 100644 index 00000000000..da02331827e --- /dev/null +++ b/src/core/singletons/opener.ts @@ -0,0 +1,433 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { InAppBrowserObject, InAppBrowserOptions } from '@awesome-cordova-plugins/in-app-browser'; +import { CoreErrorWithOptions } from '@classes/errors/errorwithoptions'; +import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics'; +import { CoreFilepool } from '@services/filepool'; +import { CoreLang, CoreLangFormat } from '@services/lang'; +import { CorePlatform } from '@services/platform'; +import { CoreSites } from '@services/sites'; +import { CoreMimetypeUtils } from '@services/utils/mimetype'; +import { Translate, FileOpener, WebIntent, InAppBrowser, NgZone } from '@singletons'; +import { CoreConstants } from '../constants'; +import { CoreFile } from '@services/file'; +import { CorePromiseUtils } from './promise-utils'; +import { CoreUrl } from './url'; +import { CoreLogger } from './logger'; +import { CoreConfig } from '@services/config'; +import { CoreDomUtils } from '@services/utils/dom'; +import { CoreEvents } from '@singletons/events'; +import { CoreColors } from './colors'; + +/** + * Singleton with helper functions to handler open files and urls. + */ +export class CoreOpener { + + protected static logger = CoreLogger.getInstance('CoreOpener'); + + // Avoid creating singleton instances. + private constructor() { + // Nothing to do. + } + + /** + * Show a confirm before opening a link in browser, unless the user previously marked to not show again. + * + * @param url URL to open. + */ + protected static async confirmOpenBrowserIfNeeded(url: string): Promise { + if (!CoreUrl.isHttpURL(url)) { + // Only ask confirm for http(s), other cases usually launch external apps. + return; + } + + // Check if the user decided not to see the warning. + const dontShowWarning = await CoreConfig.get(CoreConstants.SETTINGS_DONT_SHOW_EXTERNAL_LINK_WARN, 0); + if (dontShowWarning) { + return; + } + + // Remove common sensitive information from the URL. + url = url + .replace(/token=[^&#]+/gi, 'token=secret') + .replace(/tokenpluginfile\.php\/[^/]+/gi, 'tokenpluginfile.php/secret'); + + const dontShowAgain = await CoreDomUtils.showPrompt( + Translate.instant('core.warnopeninbrowser', { url }), + undefined, + Translate.instant('core.dontshowagain'), + 'checkbox', + ); + + if (dontShowAgain) { + CoreConfig.set(CoreConstants.SETTINGS_DONT_SHOW_EXTERNAL_LINK_WARN, 1); + } + } + + /** + * Open a file using platform specific method. + * + * @param path The local path of the file to be open. + * @param options Options. + * @returns Promise resolved when done. + */ + static async openFile(path: string, options: CoreOpenerOpenFileOptions = {}): Promise { + // Convert the path to a native path if needed. + path = CoreFile.unconvertFileSrc(path); + + const extension = CoreMimetypeUtils.getFileExtension(path); + const mimetype = extension && CoreMimetypeUtils.getMimeType(extension); + + if (mimetype == 'text/html' && CorePlatform.isAndroid()) { + // Open HTML local files in InAppBrowser, in system browser some embedded files aren't loaded. + CoreOpener.openInApp(path); + + return; + } else if (extension === 'apk' && CorePlatform.isAndroid()) { + const url = await CorePromiseUtils.ignoreErrors( + CoreFilepool.getFileUrlByPath(CoreSites.getCurrentSiteId(), CoreFile.removeBasePath(path)), + ); + + // @todo MOBILE-4167: Handle urls with expired tokens. + + throw new CoreErrorWithOptions( + Translate.instant('core.cannotinstallapkinfo'), + Translate.instant('core.cannotinstallapk'), + url + ? [ + { + text: Translate.instant('core.openinbrowser'), + handler: () => CoreOpener.openInBrowser(url), + }, + { + text: Translate.instant('core.cancel'), + role: 'cancel', + }, + ] + : undefined, + ); + } + + // Path needs to be decoded, the file won't be opened if the path has %20 instead of spaces and so. + try { + path = decodeURIComponent(path); + } catch { + // Error, use the original path. + } + + const openFile = async (mimetype?: string) => { + if (CoreOpener.shouldOpenWithDialog(options)) { + await FileOpener.showOpenWithDialog(path, mimetype || ''); + } else { + await FileOpener.open(path, mimetype || ''); + } + }; + + try { + try { + await openFile(mimetype); + } catch (error) { + if (!extension || !error || Number(error.status) !== 9) { + throw error; + } + + // Cannot open mimetype. Check if there is a deprecated mimetype for the extension. + const deprecatedMimetype = CoreMimetypeUtils.getDeprecatedMimeType(extension); + if (!deprecatedMimetype || deprecatedMimetype === mimetype) { + throw error; + } + + await openFile(deprecatedMimetype); + } + } catch (error) { + CoreOpener.logger.error('Error opening file ' + path + ' with mimetype ' + mimetype); + CoreOpener.logger.error('Error: ', JSON.stringify(error)); + + if (!extension || extension.indexOf('/') > -1 || extension.indexOf('\\') > -1) { + // Extension not found. + throw new Error(Translate.instant('core.erroropenfilenoextension')); + } + + throw new Error(Translate.instant('core.erroropenfilenoapp')); + } + } + + /** + * Open a URL using a browser. + * Do not use for files, refer to {@link CoreOpener.openFile}. + * + * @param url The URL to open. + * @param options Options. + */ + static async openInBrowser(url: string, options: CoreOpenerOpenInBrowserOptions = {}): Promise { + // eslint-disable-next-line deprecation/deprecation + const originaUrl = CoreUrl.unfixPluginfileURL(options.originalUrl ?? options.browserWarningUrl ?? url); + if (options.showBrowserWarning || options.showBrowserWarning === undefined) { + try { + await CoreOpener.confirmOpenBrowserIfNeeded(originaUrl); + } catch { + // Cancelled, stop. + return; + } + } + + const site = CoreSites.getCurrentSite(); + CoreAnalytics.logEvent({ type: CoreAnalyticsEventType.OPEN_LINK, link: originaUrl }); + window.open( + site?.containsUrl(url) + ? CoreUrl.addParamsToUrl(url, { lang: await CoreLang.getCurrentLanguage(CoreLangFormat.LMS) }) + : url, + '_system', + ); + } + + /** + * Open an online file using platform specific method. + * Specially useful for audio and video since they can be streamed. + * + * @param url The URL of the file. + * @returns Promise resolved when opened. + */ + static async openOnlineFile(url: string): Promise { + if (CorePlatform.isAndroid()) { + // In Android we need the mimetype to open it. + const mimetype = await CorePromiseUtils.ignoreErrors(CoreMimetypeUtils.getMimeTypeFromUrl(url)); + + if (!mimetype) { + // Couldn't retrieve mimetype. Return error. + throw new Error(Translate.instant('core.erroropenfilenoextension')); + } + + const options = { + action: WebIntent.ACTION_VIEW, + url, + type: mimetype, + }; + + try { + await WebIntent.startActivity(options); + + CoreAnalytics.logEvent({ + type: CoreAnalyticsEventType.OPEN_LINK, + link: CoreUrl.unfixPluginfileURL(url), + }); + + return; + } catch (error) { + CoreOpener.logger.error('Error opening online file ' + url + ' with mimetype ' + mimetype); + CoreOpener.logger.error('Error: ', JSON.stringify(error)); + + throw new Error(Translate.instant('core.erroropenfilenoapp')); + } + } + + // In the rest of platforms we need to open them in InAppBrowser. + CoreOpener.openInApp(url); + } + + /** + * Given some options, check if a file should be opened with showOpenWithDialog. + * + * @param options Options. + * @returns Boolean. + */ + static shouldOpenWithDialog(options: CoreOpenerOpenFileOptions = {}): boolean { + const openFileAction = options.iOSOpenFileAction ?? CoreConstants.CONFIG.iOSDefaultOpenFileAction; + + return CorePlatform.isIOS() && openFileAction == OpenFileAction.OPEN_WITH; + } + + private static iabInstance?: InAppBrowserObject; + + /** + * Close the InAppBrowser window. + */ + static closeInAppBrowser(): void { + if (!CoreOpener.iabInstance) { + return; + } + + CoreOpener.iabInstance.close(); + } + + /** + * Get inapp browser instance (if any). + * + * @returns IAB instance, undefined if not open. + */ + static getInAppBrowserInstance(): InAppBrowserObject | undefined { + return CoreOpener.iabInstance; + } + + /** + * Check if inapp browser is open. + * + * @returns Whether it's open. + */ + static isInAppBrowserOpen(): boolean { + return !!CoreOpener.iabInstance; + } + + /** + * Open a URL using InAppBrowser. + * Do not use for files, refer to CoreOpener.openFile. + * + * @param url The URL to open. + * @param options Override default options passed to InAppBrowser. + * @returns The opened window. + */ + static openInApp(url: string, options?: CoreOpenerOpenInAppBrowserOptions): InAppBrowserObject { + options = options || {}; + options.usewkwebview = 'yes'; // Force WKWebView in iOS. + options.enableViewPortScale = options.enableViewPortScale ?? 'yes'; // Enable zoom on iOS by default. + options.allowInlineMediaPlayback = options.allowInlineMediaPlayback ?? 'yes'; // Allow playing inline videos in iOS. + + if (!options.location && CorePlatform.isIOS() && url.indexOf('file://') === 0) { + // The URL uses file protocol, don't show it on iOS. + // In Android we keep it because otherwise we lose the whole toolbar. + options.location = 'no'; + } + + CoreOpener.setInAppBrowserToolbarColors(options); + + CoreOpener.iabInstance = InAppBrowser.create(url, '_blank', options); + + if (CorePlatform.isMobile()) { + const loadStartUrls: string[] = []; + + const loadStartSubscription = CoreOpener.iabInstance.on('loadstart').subscribe((event) => { + NgZone.run(() => { + // Store the last loaded URLs (max 10). + loadStartUrls.push(event.url); + if (loadStartUrls.length > 10) { + loadStartUrls.shift(); + } + + CoreEvents.trigger(CoreEvents.IAB_LOAD_START, event); + }); + }); + + const loadStopSubscription = CoreOpener.iabInstance.on('loadstop').subscribe((event) => { + NgZone.run(() => { + CoreEvents.trigger(CoreEvents.IAB_LOAD_STOP, event); + }); + }); + + const messageSubscription = CoreOpener.iabInstance.on('message').subscribe((event) => { + NgZone.run(() => { + CoreEvents.trigger(CoreEvents.IAB_MESSAGE, event.data); + }); + }); + + const exitSubscription = CoreOpener.iabInstance.on('exit').subscribe((event) => { + NgZone.run(() => { + loadStartSubscription.unsubscribe(); + loadStopSubscription.unsubscribe(); + messageSubscription.unsubscribe(); + exitSubscription.unsubscribe(); + + CoreOpener.iabInstance = undefined; + CoreEvents.trigger(CoreEvents.IAB_EXIT, event); + }); + }); + } + + CoreAnalytics.logEvent({ + type: CoreAnalyticsEventType.OPEN_LINK, + link: CoreUrl.unfixPluginfileURL(options.originalUrl ?? url), + }); + + return CoreOpener.iabInstance; + } + + /** + * Given some IAB options, set the toolbar colors properties to the right values. + * + * @param options Options to change. + * @returns Changed options. + */ + protected static setInAppBrowserToolbarColors(options: InAppBrowserOptions): InAppBrowserOptions { + if (options.toolbarcolor) { + // Color already set. + return options; + } + + // Color not set. Check if it needs to be changed automatically. + let bgColor: string | undefined; + let textColor: string | undefined; + + if (CoreConstants.CONFIG.iabToolbarColors === 'auto') { + bgColor = CoreColors.getToolbarBackgroundColor(); + } else if (CoreConstants.CONFIG.iabToolbarColors && typeof CoreConstants.CONFIG.iabToolbarColors === 'object') { + bgColor = CoreConstants.CONFIG.iabToolbarColors.background; + textColor = CoreConstants.CONFIG.iabToolbarColors.text; + } + + if (!bgColor) { + // Use default color. In iOS, use black background color since the default is transparent and doesn't look good. + options.locationcolor = '#000000'; + + return options; + } + + if (!textColor) { + textColor = CoreColors.isWhiteContrastingBetter(bgColor) ? '#ffffff' : '#000000'; + } + + options.toolbarcolor = bgColor; + options.closebuttoncolor = textColor; + options.navigationbuttoncolor = textColor; + options.locationcolor = bgColor; + options.locationtextcolor = textColor; + + return options; + } + +} + +/** + * Options for opening in InAppBrowser. + */ +export type CoreOpenerOpenInAppBrowserOptions = InAppBrowserOptions & { + originalUrl?: string; // Original URL to open (in case the URL was treated, e.g. to add a token or an auto-login). +}; + +/** + * Options for opening a file. + */ +export type CoreOpenerOpenFileOptions = { + iOSOpenFileAction?: OpenFileAction; // Action to do when opening a file. +}; + +/** + * Options for opening in browser. + */ +export type CoreOpenerOpenInBrowserOptions = { + showBrowserWarning?: boolean; // Whether to display a warning before opening in browser. Defaults to true. + originalUrl?: string; // Original URL to open (in case the URL was treated, e.g. to add a token or an auto-login). + /** + * @deprecated since 4.3. Use originalUrl instead. + */ + browserWarningUrl?: string; +}; + +/** + * Possible default picker actions. + */ +export enum OpenFileAction { + OPEN = 'open', + OPEN_WITH = 'open-with', +} diff --git a/src/core/singletons/promise-utils.ts b/src/core/singletons/promise-utils.ts new file mode 100644 index 00000000000..244da8bc82b --- /dev/null +++ b/src/core/singletons/promise-utils.ts @@ -0,0 +1,199 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { CoreLogger } from './logger'; + +/** + * Singleton with helper functions for promises. + */ +export class CorePromiseUtils { + + protected static logger = CoreLogger.getInstance('CorePromiseUtils'); + + // Avoid creating singleton instances. + private constructor() { + // Nothing to do. + } + + /** + * Similar to Promise.all, but if a promise fails this function's promise won't be rejected until ALL promises have finished. + * + * @param promises Promises. + */ + static async allPromises(promises: Promise[]): Promise { + if (!promises || !promises.length) { + return; + } + + const getPromiseError = async (promise: unknown): Promise => { + try { + await promise; + } catch (error) { + return error; + } + }; + + const errors = await Promise.all(promises.map(getPromiseError)); + const error = errors.find(error => !!error); + + if (error) { + throw error; + } + } + + /** + * Combination of allPromises and ignoreErrors functions. + * + * @param promises Promises. + */ + static async allPromisesIgnoringErrors(promises: Promise[]): Promise { + await CorePromiseUtils.ignoreErrors(CorePromiseUtils.allPromises(promises)); + } + + /** + * Execute promises one depending on the previous. + * + * @param orderedPromisesData Data to be executed. + * @returns Promise resolved when all promises are resolved. + */ + static async executeOrderedPromises(orderedPromisesData: OrderedPromiseData[]): Promise { + const promises: Promise[] = []; + let dependency = Promise.resolve(); + + // Execute all the processes in order. + for (const i in orderedPromisesData) { + const data = orderedPromisesData[i]; + // Add the process to the dependency stack. + const promise = dependency.finally(() => { + try { + return data.function(); + } catch (e) { + CorePromiseUtils.logger.error(e.message); + + return; + } + }); + promises.push(promise); + + // If the new process is blocking, we set it as the dependency. + if (data.blocking) { + dependency = promise; + } + } + + // Return when all promises are done. + await CorePromiseUtils.allPromises(promises); + } + + /** + * Ignore errors from a promise. + * + * @param promise Promise to ignore errors. + * @param fallback Value to return if the promise is rejected. + * @returns Promise with ignored errors, resolving to the fallback result if provided. + */ + static async ignoreErrors(promise?: Promise): Promise; + static async ignoreErrors(promise: Promise, fallback: Fallback): Promise; + static async ignoreErrors(promise?: Promise, fallback?: Fallback): + Promise { + try { + const result = await promise; + + return result; + } catch { + // Ignore errors. + return fallback; + } + } + + /** + * Given a promise, returns true if it's rejected or false if it's resolved. + * + * @param promise Promise to check + * @returns Promise resolved with boolean: true if the promise is rejected or false if it's resolved. + */ + static async promiseFails(promise: Promise): Promise { + try { + await promise; + + return false; + } catch { + return true; + } + } + + /** + * Given a promise, returns true if it's resolved or false if it's rejected. + * + * @param promise Promise to check + * @returns Promise resolved with boolean: true if the promise it's resolved or false if it's rejected. + */ + static async promiseWorks(promise: Promise): Promise { + try { + await promise; + + return true; + } catch { + return false; + } + } + + /** + * Set a timeout to a Promise. If the time passes before the Promise is resolved or rejected, it will be automatically + * rejected. + * + * @param promise The promise to timeout. + * @param time Number of milliseconds of the timeout. + * @returns Promise with the timeout. + */ + static timeoutPromise(promise: Promise, time: number): Promise { + return new Promise((resolve, reject): void => { + let timedOut = false; + const resolveBeforeTimeout = (value: T) => { + if (timedOut) { + return; + } + resolve(value); + }; + const timeout = setTimeout( + () => { + reject({ timeout: true }); + timedOut = true; + }, + time, + ); + + promise + .then(resolveBeforeTimeout) + .catch(reject) + .finally(() => clearTimeout(timeout)); + }); + } + +} + +/** + * Data for each entry of executeOrderedPromises. + */ +export type OrderedPromiseData = { + /** + * Function to execute. + */ + function: () => Promise; + + /** + * Whether the promise should block the following one. + */ + blocking?: boolean; +}; diff --git a/src/core/singletons/redirects.ts b/src/core/singletons/redirects.ts new file mode 100644 index 00000000000..befe03ac2eb --- /dev/null +++ b/src/core/singletons/redirects.ts @@ -0,0 +1,168 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { CoreRedirectPayload } from '@services/navigator'; +import { CoreLogger } from './logger'; +import { CoreObject } from './object'; +import { CoreWS } from '@services/ws'; +import { CorePromiseUtils } from './promise-utils'; + +/** + * Singleton with helper functions to manage redirects. + * + * This singleton is not necessary to be exported for site plugins. + */ +export class CoreRedirects { + + private static redirect?: CoreRedirectData; + protected static logger = CoreLogger.getInstance('CoreRedirects'); + + // Avoid creating singleton instances. + private constructor() { + // Nothing to do. + } + + /** + * Read redirect data from local storage and clear it if it existed. + */ + static consumeStorageRedirect(): void { + if (!localStorage?.getItem) { + return; + } + + try { + // Read data from storage. + const jsonData = localStorage.getItem('CoreRedirect'); + + if (!jsonData) { + return; + } + + // Clear storage. + localStorage.removeItem('CoreRedirect'); + + // Remember redirect data. + const data: CoreRedirectData = JSON.parse(jsonData); + + if (!CoreObject.isEmpty(data)) { + CoreRedirects.redirect = data; + } + } catch (error) { + CoreRedirects.logger.error('Error loading redirect data:', error); + } + } + + /** + * Retrieve and forget redirect data. + * + * @returns Redirect data if any. + */ + static consumeMemoryRedirect(): CoreRedirectData | null { + const redirect = CoreRedirects.getRedirect(); + + CoreRedirects.forgetRedirect(); + + if (redirect && (!redirect.timemodified || Date.now() - redirect.timemodified > 300000)) { + // Redirect data is only valid for 5 minutes, discard it. + return null; + } + + return redirect; + } + + /** + * Forget redirect data. + */ + static forgetRedirect(): void { + delete CoreRedirects.redirect; + } + + /** + * Retrieve redirect data. + * + * @returns Redirect data if any. + */ + static getRedirect(): CoreRedirectData | null { + return CoreRedirects.redirect || null; + } + + /** + * Store redirect params. + * + * @param siteId Site ID. + * @param redirectData Redirect data. + */ + static storeRedirect(siteId: string, redirectData: CoreRedirectPayload = {}): void { + if (!redirectData.redirectPath && !redirectData.urlToOpen) { + return; + } + + try { + const redirect: CoreRedirectData = { + siteId, + timemodified: Date.now(), + ...redirectData, + }; + + localStorage.setItem('CoreRedirect', JSON.stringify(redirect)); + } catch { + // Ignore errors. + } + } + + /** + * Check if a URL has a redirect. + * + * @param url The URL to check. + * @returns Promise resolved with boolean_ whether there is a redirect. + */ + static async checkRedirect(url: string): Promise { + if (!window.fetch) { + // Cannot check if there is a redirect, assume it's false. + return false; + } + + const initOptions: RequestInit = { redirect: 'follow' }; + + // Some browsers implement fetch but no AbortController. + const controller = AbortController ? new AbortController() : false; + + if (controller) { + initOptions.signal = controller.signal; + } + + try { + const response = await CorePromiseUtils.timeoutPromise(window.fetch(url, initOptions), CoreWS.getRequestTimeout()); + + return response.redirected; + } catch (error) { + if (error.timeout && controller) { + // Timeout, abort the request. + controller.abort(); + } + + // There was a timeout, cannot determine if there's a redirect. Assume it's false. + return false; + } + } + +} + +/** + * Data stored for a redirect to another page/site. + */ +export type CoreRedirectData = CoreRedirectPayload & { + siteId?: string; // ID of the site to load. + timemodified?: number; // Timestamp when this redirect was last modified. +}; diff --git a/src/core/singletons/sso.ts b/src/core/singletons/sso.ts new file mode 100644 index 00000000000..ae660f36083 --- /dev/null +++ b/src/core/singletons/sso.ts @@ -0,0 +1,75 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { CorePromisedValue } from '@classes/promised-value'; + +/** + * Singleton with helper functions for Single Sign On. + */ +export class CoreSSO { + + private static ssoAuthenticationDeferred?: CorePromisedValue; + + // Avoid creating singleton instances. + private constructor() { + // Nothing to do. + } + + /** + * Start an SSO authentication process. + * Please notice that this function should be called when the app receives the new token from the browser, + * NOT when the browser is opened. + */ + static startSSOAuthentication(): void { + CoreSSO.ssoAuthenticationDeferred = new CorePromisedValue(); + + // Resolve it automatically after 10 seconds (it should never take that long). + const cancelTimeout = setTimeout(() => CoreSSO.finishSSOAuthentication(), 10000); + + // If the promise is resolved because finishSSOAuthentication is called, stop the cancel promise. + // eslint-disable-next-line promise/catch-or-return + CoreSSO.ssoAuthenticationDeferred.then(() => clearTimeout(cancelTimeout)); + } + + /** + * Finish an SSO authentication process. + */ + static finishSSOAuthentication(): void { + if (!CoreSSO.ssoAuthenticationDeferred) { + return; + } + + CoreSSO.ssoAuthenticationDeferred.resolve(); + CoreSSO.ssoAuthenticationDeferred = undefined; + } + + /** + * Check if there's an ongoing SSO authentication process. + * + * @returns Whether there's a SSO authentication ongoing. + */ + static isSSOAuthenticationOngoing(): boolean { + return !!CoreSSO.ssoAuthenticationDeferred; + } + + /** + * Returns a promise that will be resolved once SSO authentication finishes. + * + * @returns Promise resolved once SSO authentication finishes. + */ + static async waitForSSOAuthentication(): Promise { + await CoreSSO.ssoAuthenticationDeferred; + } + +} diff --git a/src/core/singletons/subscriptions.ts b/src/core/singletons/subscriptions.ts index b0cea166d4f..cb7acb87312 100644 --- a/src/core/singletons/subscriptions.ts +++ b/src/core/singletons/subscriptions.ts @@ -26,6 +26,11 @@ type Subscribable = EventEmitter | Observable; */ export class CoreSubscriptions { + // Avoid creating singleton instances. + private constructor() { + // Nothing to do. + } + /** * Listen once to a subscribable object. * diff --git a/src/core/singletons/swiper.ts b/src/core/singletons/swiper.ts index 46cb7bb3de6..9dbf5ab7d1f 100644 --- a/src/core/singletons/swiper.ts +++ b/src/core/singletons/swiper.ts @@ -23,6 +23,11 @@ import { SwiperOptions } from 'swiper/types'; */ export class CoreSwiper { + // Avoid creating singleton instances. + private constructor() { + // Nothing to do. + } + /** * Initialize a Swiper instance. * It will return swiper instance if current is not set or destroyed and new is set and not destroyed. diff --git a/src/core/singletons/time.ts b/src/core/singletons/time.ts index 69548a6945c..f647b02e4c1 100644 --- a/src/core/singletons/time.ts +++ b/src/core/singletons/time.ts @@ -14,12 +14,75 @@ import { Translate } from '@singletons'; import { CoreConstants } from '../constants'; +import { CorePlatform } from '@services/platform'; /** * Singleton with helper functions for time operations. */ export class CoreTime { + protected static readonly LEGACY_TIMEZONES = { + '-13.0': 'Australia/Perth', + '-12.5': 'Etc/GMT+12', + '-12.0': 'Etc/GMT+12', + '-11.5': 'Etc/GMT+11', + '-11.0': 'Etc/GMT+11', + '-10.5': 'Etc/GMT+10', + '-10.0': 'Etc/GMT+10', + '-9.5': 'Etc/GMT+9', + '-9.0': 'Etc/GMT+9', + '-8.5': 'Etc/GMT+8', + '-8.0': 'Etc/GMT+8', + '-7.5': 'Etc/GMT+7', + '-7.0': 'Etc/GMT+7', + '-6.5': 'Etc/GMT+6', + '-6.0': 'Etc/GMT+6', + '-5.5': 'Etc/GMT+5', + '-5.0': 'Etc/GMT+5', + '-4.5': 'Etc/GMT+4', + '-4.0': 'Etc/GMT+4', + '-3.5': 'Etc/GMT+3', + '-3.0': 'Etc/GMT+3', + '-2.5': 'Etc/GMT+2', + '-2.0': 'Etc/GMT+2', + '-1.5': 'Etc/GMT+1', + '-1.0': 'Etc/GMT+1', + '-0.5': 'Etc/GMT', + '0': 'Etc/GMT', + '0.0': 'Etc/GMT', + '0.5': 'Etc/GMT', + '1.0': 'Etc/GMT-1', + '1.5': 'Etc/GMT-1', + '2.0': 'Etc/GMT-2', + '2.5': 'Etc/GMT-2', + '3.0': 'Etc/GMT-3', + '3.5': 'Etc/GMT-3', + '4.0': 'Etc/GMT-4', + '4.5': 'Asia/Kabul', + '5.0': 'Etc/GMT-5', + '5.5': 'Asia/Kolkata', + '6.0': 'Etc/GMT-6', + '6.5': 'Asia/Rangoon', + '7.0': 'Etc/GMT-7', + '7.5': 'Etc/GMT-7', + '8.0': 'Etc/GMT-8', + '8.5': 'Etc/GMT-8', + '9.0': 'Etc/GMT-9', + '9.5': 'Australia/Darwin', + '10.0': 'Etc/GMT-10', + '10.5': 'Etc/GMT-10', + '11.0': 'Etc/GMT-11', + '11.5': 'Etc/GMT-11', + '12.0': 'Etc/GMT-12', + '12.5': 'Etc/GMT-12', + '13.0': 'Etc/GMT-13', + }; + + // Avoid creating singleton instances. + private constructor() { + // Nothing to do. + } + /** * Returns years, months, days, hours, minutes and seconds in a human readable format. * @@ -119,4 +182,24 @@ export class CoreTime { }; } + /** + * Returns the forced timezone to use. Timezone is forced for automated tests. + * + * @returns Timezone. Undefined to use the user's timezone. + */ + static getForcedTimezone(): string | undefined { + // Use the same timezone forced for LMS in tests. + return CorePlatform.isAutomated() ? 'Australia/Perth' : undefined; + } + + /** + * Translates legacy timezone names. + * + * @param tz Timezone name. + * @returns Readable timezone name. + */ + static translateLegacyTimezone(tz: string): string { + return CoreTime.LEGACY_TIMEZONES[tz] ?? tz; + } + } diff --git a/src/core/singletons/utils.ts b/src/core/singletons/utils.ts new file mode 100644 index 00000000000..6b1a1711179 --- /dev/null +++ b/src/core/singletons/utils.ts @@ -0,0 +1,348 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Translate } from '@singletons'; +import { CoreLogger } from './logger'; +import { CoreObject } from './object'; +import { CoreFileUtils } from './file-utils'; + +/** + * Singleton with utils helper functions. + */ +export class CoreUtils { + + // Avoid creating singleton instances. + private constructor() { + // Nothing to do. + } + + protected static readonly DONT_CLONE = ['[object FileEntry]', '[object DirectoryEntry]', '[object DOMFileSystem]']; + + protected static logger = CoreLogger.getInstance('CoreUtils'); + protected static uniqueIds: {[name: string]: number} = {}; + + /** + * Clone a variable. It should be an object, array or primitive type. + * + * @param source The variable to clone. + * @param level Depth we are right now inside a cloned object. It's used to prevent reaching max call stack size. + * @returns Cloned variable. + */ + static clone(source: T, level: number = 0): T { + if (level >= 20) { + // Max 20 levels. + CoreUtils.logger.error('Max depth reached when cloning object.', source); + + return source; + } + + if (CoreFileUtils.valueIsFileEntry(source)) { + // Don't clone FileEntry. It has a lot of depth and they shouldn't be modified. + return source; + } else if (Array.isArray(source)) { + // Clone the array and all the entries. + const newArray = [] as unknown as T; + for (let i = 0; i < source.length; i++) { + newArray[i] = CoreUtils.clone(source[i], level + 1); + } + + return newArray; + } else if (CoreObject.isObject(source)) { + // Check if the object shouldn't be copied. + if (source.toString && CoreUtils.DONT_CLONE.indexOf(source.toString()) != -1) { + // Object shouldn't be copied, return it as it is. + return source; + } + + // Clone the object and all the subproperties. + const newObject = {} as T; + for (const name in source) { + newObject[name] = CoreUtils.clone(source[name], level + 1); + } + + return newObject; + } else { + // Primitive type or unknown, return it as it is. + return source; + } + } + + /** + * Given a float, prints it nicely. Localized floats must not be used in calculations! + * Based on Moodle's format_float. + * + * @param float The float to print. + * @returns Locale float. + */ + static formatFloat(float: unknown): string { + if (float === undefined || float === null || typeof float == 'boolean') { + return ''; + } + + const localeSeparator = Translate.instant('core.decsep'); + + const floatString = String(float); + + return floatString.replace('.', localeSeparator); + } + + /** + * Returns a tree formatted from a plain list. + * List has to be sorted by depth to allow this function to work correctly. Errors can be thrown if a child node is + * processed before a parent node. + * + * @param list List to format. + * @param parentFieldName Name of the parent field to match with children. + * @param idFieldName Name of the children field to match with parent. + * @param rootParentId The id of the root. + * @param maxDepth Max Depth to convert to tree. Children found will be in the last level of depth. + * @returns Array with the formatted tree, children will be on each node under children field. + */ + static formatTree( + list: T[], + parentFieldName: string = 'parent', + idFieldName: string = 'id', + rootParentId: number = 0, + maxDepth: number = 5, + ): TreeNode[] { + const map = {}; + const mapDepth = {}; + const tree: TreeNode[] = []; + + // Create a map first to avoid problems with not sorted. + list.forEach((node: TreeNode, index): void => { + const id = node[idFieldName]; + + if (id === undefined) { + CoreUtils.logger.error(`Node with incorrect ${idFieldName}:${id} found on formatTree`); + } + + if (node.children === undefined) { + node.children = []; + } + map[id] = index; + }); + + list.forEach((node: TreeNode): void => { + const id = node[idFieldName]; + const parent = node[parentFieldName]; + + if (id === undefined || parent === undefined) { + CoreUtils.logger.error(`Node with incorrect ${idFieldName}:${id} + or ${parentFieldName}:${parent} found on formatTree`); + } + + // Use map to look-up the parents. + if (parent !== rootParentId) { + const parentNode = list[map[parent]] as TreeNode; + if (parentNode) { + if (mapDepth[parent] == maxDepth) { + // Reached max level of depth. Proceed with flat order. Find parent object of the current node. + const parentOfParent = parentNode[parentFieldName]; + if (parentOfParent) { + // This element will be the child of the node that is two levels up the hierarchy + // (i.e. the child of node.parent.parent). + (list[map[parentOfParent]] as TreeNode).children.push(node); + // Assign depth level to the same depth as the parent (i.e. max depth level). + mapDepth[id] = mapDepth[parent]; + // Change the parent to be the one that is two levels up the hierarchy. + node[parentFieldName] = parentOfParent; + } else { + CoreUtils.logger.error(`Node parent of parent:${parentOfParent} not found on formatTree`); + } + } else { + parentNode.children.push(node); + // Increase the depth level. + mapDepth[id] = mapDepth[parent] + 1; + } + } else { + CoreUtils.logger.error(`Node parent:${parent} not found on formatTree`); + } + } else { + tree.push(node); + + // Root elements are the first elements in the tree structure, therefore have the depth level 1. + mapDepth[id] = 1; + } + }); + + return tree; + } + + /** + * Get a unique ID for a certain name. + * + * @param name The name to get the ID for. + * @returns Unique ID. + */ + static getUniqueId(name: string): number { + if (!CoreUtils.uniqueIds[name]) { + CoreUtils.uniqueIds[name] = 0; + } + + return ++CoreUtils.uniqueIds[name]; + } + + /** + * Return true if the param is false (bool), 0 (number) or "0" (string). + * + * @param value Value to check. + * @returns Whether the value is false, 0 or "0". + */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + static isFalseOrZero(value: any): boolean { + return value !== undefined && (value === false || value === 'false' || parseInt(value, 10) === 0); + } + + /** + * Return true if the param is true (bool), 1 (number) or "1" (string). + * + * @param value Value to check. + * @returns Whether the value is true, 1 or "1". + */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + static isTrueOrOne(value: any): boolean { + return value !== undefined && (value === true || value === 'true' || parseInt(value, 10) === 1); + } + + /** + * Given a list (e.g. a,b,c,d,e) this function returns an array of 1->a, 2->b, 3->c etc. + * Taken from make_menu_from_list on moodlelib.php (not the same but similar). + * + * @param list The string to explode into array bits + * @param defaultLabel Element that will become default option, if not defined, it won't be added. + * @param separator The separator used within the list string. Default ','. + * @param defaultValue Element that will become default option value. Default 0. + * @returns The now assembled array + */ + static makeMenuFromList( + list: string, + defaultLabel?: string, + separator: string = ',', + defaultValue?: T, + ): CoreMenuItem[] { + // Split and format the list. + const split = list.split(separator).map((label, index) => ({ + label: label.trim(), + value: index + 1, + })) as { label: string; value: T | number }[]; + + if (defaultLabel) { + split.unshift({ + label: defaultLabel, + value: defaultValue || 0, + }); + } + + return split; + } + + /** + * Converts locale specific floating point/comma number back to a standard float number. + * Do NOT try to do any math operations before this conversion on any user submitted floats! + * Based on Moodle's unformat_float function. + * + * @param localeFloat Locale aware float representation. + * @param strict If true, then check the input and return false if it is not a valid number. + * @returns False if bad format, empty string if empty value or the parsed float if not. + */ + static unformatFloat(localeFloat: string | number | null | undefined, strict?: boolean): false | '' | number { + // Bad format on input type number. + if (localeFloat === undefined) { + return false; + } + + // Empty (but not zero). + if (localeFloat == null) { + return ''; + } + + // Convert float to string. + localeFloat = String(localeFloat); + localeFloat = localeFloat.trim(); + + if (localeFloat == '') { + return ''; + } + + localeFloat = localeFloat.replace(' ', ''); // No spaces - those might be used as thousand separators. + localeFloat = localeFloat.replace(Translate.instant('core.decsep'), '.'); + + // Use Number instead of parseFloat because the latter truncates the number when it finds ",", while Number returns NaN. + // If the number still has "," then it means it's not a valid separator. + const parsedFloat = Number(localeFloat); + + // Bad format. + if (strict && (!isFinite(parsedFloat) || isNaN(parsedFloat))) { + return false; + } + + return parsedFloat; + } + + /** + * Debounce a function so consecutive calls are ignored until a certain time has passed since the last call. + * + * @param fn Function to debounce. + * @param delay Time that must pass until the function is called. + * @returns Debounced function. + */ + static debounce(fn: (...args: T) => unknown, delay: number): (...args: T) => void { + let timeoutID: number; + + const debounced = (...args: T): void => { + clearTimeout(timeoutID); + + timeoutID = window.setTimeout(() => fn.apply(null, args), delay); + }; + + return debounced; + } + + /** + * Throttle a function so consecutive calls are ignored until a certain time has passed since the last executed call. + * + * @param fn Function to throttle. + * @param duration Time that must pass until the function is called. + * @returns Throttled function. + */ + static throttle(fn: (...args: T) => unknown, duration: number): (...args: T) => void { + let shouldWait = false; + + const throttled = (...args: T): void => { + if (!shouldWait) { + fn.apply(null, args); + + shouldWait = true; + + setTimeout(() => { + shouldWait = false; + }, duration); + } + }; + + return throttled; + } + + } + +export type TreeNode = T & { children: TreeNode[] }; + +/** + * Menu item. + */ +export type CoreMenuItem = { + label: string; + value: T | number; +}; diff --git a/src/core/singletons/wait.ts b/src/core/singletons/wait.ts index 72e6c6ea8fe..b4d41bebea7 100644 --- a/src/core/singletons/wait.ts +++ b/src/core/singletons/wait.ts @@ -20,6 +20,11 @@ import { CorePlatform } from '@services/platform'; */ export class CoreWait { + // Avoid creating singleton instances. + private constructor() { + // Nothing to do. + } + /** * Wait until the next tick. * diff --git a/src/core/singletons/window.ts b/src/core/singletons/window.ts index c14b8de9386..03972fe6087 100644 --- a/src/core/singletons/window.ts +++ b/src/core/singletons/window.ts @@ -13,15 +13,11 @@ // limitations under the License. import { CoreContentLinksHelper } from '@features/contentlinks/services/contentlinks-helper'; -import { CoreConfig } from '@services/config'; import { CoreFileHelper } from '@services/file-helper'; import { CoreSites } from '@services/sites'; -import { CoreDomUtils } from '@services/utils/dom'; import { CoreUrl } from '@singletons/url'; -import { CoreUtils } from '@services/utils/utils'; -import { Translate } from '@singletons'; -import { CoreConstants } from '../constants'; +import { CoreOpener } from './opener'; /** * Singleton with helper functions for windows. @@ -36,36 +32,16 @@ export class CoreWindow { /** * Show a confirm before opening a link in browser, unless the user previously marked to not show again. * - * @param url URL to open. - * @returns Promise resolved if confirmed, rejected if rejected. + * @returns Only shows a deprecation warning. + * @deprecated since 5.0. Not used anymore. Use CoreOpener.openInBrowser and it will confirm if needed. */ - static async confirmOpenBrowserIfNeeded(url: string): Promise { - if (!CoreUrl.isHttpURL(url)) { - // Only ask confirm for http(s), other cases usually launch external apps. - return; - } - - // Check if the user decided not to see the warning. - const dontShowWarning = await CoreConfig.get(CoreConstants.SETTINGS_DONT_SHOW_EXTERNAL_LINK_WARN, 0); - if (dontShowWarning) { - return; - } - - // Remove common sensitive information from the URL. - url = url - .replace(/token=[^&#]+/gi, 'token=secret') - .replace(/tokenpluginfile\.php\/[^/]+/gi, 'tokenpluginfile.php/secret'); + static async confirmOpenBrowserIfNeeded(): Promise { + const { CoreLogger } = await import('@singletons/logger'); - const dontShowAgain = await CoreDomUtils.showPrompt( - Translate.instant('core.warnopeninbrowser', { url }), - undefined, - Translate.instant('core.dontshowagain'), - 'checkbox', - ); + CoreLogger.getInstance('CoreWindow') + .warn('confirmOpenBrowserIfNeeded has been deprecated since 5.0. Not used anymore.\ + Use CoreOpener.openInBrowser and it will confirm if needed.'); - if (dontShowAgain) { - CoreConfig.set(CoreConstants.SETTINGS_DONT_SHOW_EXTERNAL_LINK_WARN, 1); - } } /** @@ -83,11 +59,12 @@ export class CoreWindow { try { await CoreFileHelper.showConfirmOpenUnsupportedFile(false, { filename }); } catch { - return; // Cancelled, stop. + // Cancelled, stop. + return; } } - await CoreUtils.openFile(url); + await CoreOpener.openFile(url); } else { let treated = false; @@ -100,7 +77,7 @@ export class CoreWindow { // Not opened in the app, open with browser. Check if we need to auto-login. if (!CoreSites.isLoggedIn()) { // Not logged in, cannot auto-login. - CoreUtils.openInBrowser(url); + CoreOpener.openInBrowser(url); } else { await CoreSites.getRequiredCurrentSite().openInBrowserWithAutoLogin(url); } diff --git a/src/testing/services/behat-runtime.ts b/src/testing/services/behat-runtime.ts index 65371cf4526..f6c7d7ed036 100644 --- a/src/testing/services/behat-runtime.ts +++ b/src/testing/services/behat-runtime.ts @@ -33,7 +33,7 @@ import { Swiper } from 'swiper'; import { LocalNotificationsMock } from '@features/emulator/services/local-notifications'; import { GetClosureArgs } from '@/core/utils/types'; import { CoreIframeComponent } from '@components/iframe/iframe'; -import { CoreUtils } from '@services/utils/utils'; +import { CorePromiseUtils } from '@singletons/promise-utils'; /** * Behat runtime servive with public API. @@ -888,7 +888,7 @@ export class TestingBehatRuntimeService { * @returns Promise resolved when toast has been dismissed. */ async waitToastDismiss(): Promise { - await CoreUtils.ignoreErrors(ToastController.dismiss()); + await CorePromiseUtils.ignoreErrors(ToastController.dismiss()); } } diff --git a/src/testing/testing.module.ts b/src/testing/testing.module.ts index 9c3d29688d5..d78d2ee7aec 100644 --- a/src/testing/testing.module.ts +++ b/src/testing/testing.module.ts @@ -13,7 +13,7 @@ // limitations under the License. import { APP_INITIALIZER, NgModule } from '@angular/core'; -import { CoreAppProvider } from '@services/app'; +import { CoreTime } from '@singletons/time'; import moment from 'moment-timezone'; import { TestingBehatRuntime, TestingBehatRuntimeService } from './services/behat-runtime'; import { CorePlatform } from '@services/platform'; @@ -35,7 +35,7 @@ function initializeAutomatedTests(window: AutomatedTestsWindow) { window.behat = TestingBehatRuntime.instance; // Force timezone for automated tests. - moment.tz.setDefault(CoreAppProvider.getForcedTimezone()); + moment.tz.setDefault(CoreTime.getForcedTimezone()); } @NgModule({ diff --git a/src/types/config.d.ts b/src/types/config.d.ts index e3de695994a..569bfaa405f 100644 --- a/src/types/config.d.ts +++ b/src/types/config.d.ts @@ -15,7 +15,7 @@ import { CoreColorScheme, CoreZoomLevel } from '@features/settings/services/settings-helper'; import { CoreMainMenuLocalizedCustomItem } from '@features/mainmenu/services/mainmenu'; import { CoreLoginSiteInfo, CoreSitesDemoSiteData } from '@services/sites'; -import { OpenFileAction } from '@services/utils/utils'; +import { OpenFileAction } from '@singletons/opener'; import { CoreLoginSiteFinderSettings, CoreLoginSiteSelectorListMethod } from '@features/login/services/login-helper'; import { CoreDatabaseConfiguration } from '@classes/database/database-table'; import { ToastDuration } from '@services/toasts';