diff --git a/src/app/components/components.module.ts b/src/app/components/components.module.ts index 55bee79bb..67c7a4a6c 100644 --- a/src/app/components/components.module.ts +++ b/src/app/components/components.module.ts @@ -22,6 +22,6 @@ import { CoreIconComponent } from './icon/icon'; imports: [], exports: [ CoreIconComponent, - ] + ], }) export class CoreComponentsModule {} diff --git a/src/app/components/icon/icon.ts b/src/app/components/icon/icon.ts index 7ae4328cf..eea96b488 100644 --- a/src/app/components/icon/icon.ts +++ b/src/app/components/icon/icon.ts @@ -28,7 +28,7 @@ import { Component, Input, OnChanges, OnDestroy, ElementRef, SimpleChange } from export class CoreIconComponent implements OnChanges, OnDestroy { // Common params. - @Input() name: string; + @Input() name = ''; @Input() color?: string; @Input() slash?: boolean; // Display a red slash over the icon. @@ -48,6 +48,7 @@ export class CoreIconComponent implements OnChanges, OnDestroy { constructor(el: ElementRef) { this.element = el.nativeElement; + this.newElement = this.element } /** @@ -58,7 +59,7 @@ export class CoreIconComponent implements OnChanges, OnDestroy { return; } - const oldElement = this.newElement ? this.newElement : this.element; + const oldElement = this.newElement; // Use a new created element to avoid ion-icon working. // This is necessary to make the FontAwesome stuff work. @@ -102,7 +103,7 @@ export class CoreIconComponent implements OnChanges, OnDestroy { this.newElement.classList.add('core-icon-dir-flip'); } - oldElement.parentElement.replaceChild(this.newElement, oldElement); + oldElement.parentElement?.replaceChild(this.newElement, oldElement); } /** diff --git a/src/app/directives/long-press.directive.ts b/src/app/directives/long-press.directive.ts index ca4cda73e..14a892eda 100644 --- a/src/app/directives/long-press.directive.ts +++ b/src/app/directives/long-press.directive.ts @@ -26,7 +26,7 @@ import { GestureController } from '@singletons/core.singletons'; export class CoreLongPressDirective implements OnInit, OnDestroy { element: HTMLElement; - pressGesture: Gesture; + pressGesture?: Gesture; @Output() longPress = new EventEmitter(); @@ -53,7 +53,7 @@ export class CoreLongPressDirective implements OnInit, OnDestroy { * Destroy gesture listening. */ ngOnDestroy(): void { - this.pressGesture.destroy(); + this.pressGesture?.destroy(); } } diff --git a/src/app/services/geolocation.ts b/src/app/services/geolocation.ts index 317e053f2..110b368e8 100644 --- a/src/app/services/geolocation.ts +++ b/src/app/services/geolocation.ts @@ -116,7 +116,7 @@ export class CoreGeolocationProvider { * * @param error Error. */ - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any + // eslint-disable-next-line @typescript-eslint/no-explicit-any protected isCordovaPermissionDeniedError(error?: any): boolean { return error && 'code' in error && 'PERMISSION_DENIED' in error && error.code === error.PERMISSION_DENIED; } diff --git a/src/app/services/groups.ts b/src/app/services/groups.ts index ed829076a..3eb041db4 100644 --- a/src/app/services/groups.ts +++ b/src/app/services/groups.ts @@ -60,8 +60,12 @@ export class CoreGroupsProvider { * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). * @return Promise resolved when the groups are retrieved. */ - async getActivityAllowedGroups(cmId: number, userId?: number, siteId?: string, ignoreCache?: boolean): - Promise { + async getActivityAllowedGroups( + cmId: number, + userId?: number, + siteId?: string, + ignoreCache?: boolean, + ): Promise { const site = await CoreSites.instance.getSite(siteId); userId = userId || site.getUserId(); @@ -111,7 +115,7 @@ export class CoreGroupsProvider { * @return Promise resolved when the groups are retrieved. If not allowed, empty array will be returned. */ async getActivityAllowedGroupsIfEnabled(cmId: number, userId?: number, siteId?: string, ignoreCache?: boolean): - Promise { + Promise { siteId = siteId || CoreSites.instance.getCurrentSiteId(); // Get real groupmode, in case it's forced by the course. @@ -136,10 +140,16 @@ export class CoreGroupsProvider { * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). * @return Promise resolved with the group info. */ - async getActivityGroupInfo(cmId: number, addAllParts?: boolean, userId?: number, siteId?: string, ignoreCache?: boolean): - Promise { + async getActivityGroupInfo( + cmId: number, + addAllParts?: boolean, + userId?: number, + siteId?: string, + ignoreCache?: boolean, + ): Promise { const groupInfo: CoreGroupInfo = { groups: [], + defaultGroupId: 0, }; const groupMode = await this.getActivityGroupMode(cmId, siteId, ignoreCache); @@ -163,13 +173,13 @@ export class CoreGroupsProvider { } else { // The "canaccessallgroups" field was added in 3.4. Add all participants for visible groups in previous versions. if (result.canaccessallgroups || (typeof result.canaccessallgroups == 'undefined' && groupInfo.visibleGroups)) { - groupInfo.groups.push({ id: 0, name: Translate.instance.instant('core.allparticipants') }); + groupInfo.groups!.push({ id: 0, name: Translate.instance.instant('core.allparticipants') }); groupInfo.defaultGroupId = 0; } else { groupInfo.defaultGroupId = result.groups[0].id; } - groupInfo.groups = groupInfo.groups.concat(result.groups); + groupInfo.groups = groupInfo.groups!.concat(result.groups); } return groupInfo; @@ -233,6 +243,7 @@ export class CoreGroupsProvider { } // @todo Get courses. + return []; } /** @@ -249,7 +260,7 @@ export class CoreGroupsProvider { const courseGroups = await Promise.all(promises); - return [].concat(...courseGroups); + return ([]).concat(...courseGroups); } /** @@ -339,7 +350,7 @@ export class CoreGroupsProvider { * @return Promise resolved when the data is invalidated. */ async invalidateActivityGroupInfo(cmId: number, userId?: number, siteId?: string): Promise { - const promises = []; + const promises = []>[]; promises.push(this.invalidateActivityAllowedGroups(cmId, userId, siteId)); promises.push(this.invalidateActivityGroupMode(cmId, siteId)); @@ -457,7 +468,7 @@ export type CoreGroupInfo = { /** * The group ID to use by default. If all participants is visible, 0 will be used. First group ID otherwise. */ - defaultGroupId?: number; + defaultGroupId: number; }; /** diff --git a/src/app/services/local-notifications.ts b/src/app/services/local-notifications.ts index f3638d86e..385837a89 100644 --- a/src/app/services/local-notifications.ts +++ b/src/app/services/local-notifications.ts @@ -100,13 +100,13 @@ export class CoreLocalNotificationsProvider { // eslint-disable-next-line @typescript-eslint/no-explicit-any protected observables: {[eventName: string]: {[component: string]: Subject}} = {}; - protected triggerSubscription: Subscription; - protected clickSubscription: Subscription; - protected clearSubscription: Subscription; - protected cancelSubscription: Subscription; - protected addSubscription: Subscription; - protected updateSubscription: Subscription; - protected queueRunner: CoreQueueRunner; // Queue to decrease the number of concurrent calls to the plugin (see MOBILE-3477). + protected triggerSubscription?: Subscription; + protected clickSubscription?: Subscription; + protected clearSubscription?: Subscription; + protected cancelSubscription?: Subscription; + protected addSubscription?: Subscription; + protected updateSubscription?: Subscription; + protected queueRunner?: CoreQueueRunner; // Queue to decrease the number of concurrent calls to the plugin (see MOBILE-3477). constructor() { this.logger = CoreLogger.getInstance('CoreLocalNotificationsProvider'); @@ -216,8 +216,8 @@ export class CoreLocalNotificationsProvider { */ canDisableSound(): boolean { // Only allow disabling sound in Android 7 or lower. In iOS and Android 8+ it can easily be done with system settings. - return this.isAvailable() && !CoreApp.instance.isDesktop() && CoreApp.instance.isAndroid() && - Device.instance.version && Number(Device.instance.version.split('.')[0]) < 8; + return this.isAvailable() &&!CoreApp.instance.isDesktop() && CoreApp.instance.isAndroid() && + Number(Device.instance.version?.split('.')[0]) < 8; } /** @@ -323,15 +323,16 @@ export class CoreLocalNotificationsProvider { * @param siteId Site ID. * @return Promise resolved when the notification ID is generated. */ - protected getUniqueNotificationId(notificationId: number, component: string, siteId: string): Promise { + protected async getUniqueNotificationId(notificationId: number, component: string, siteId: string): Promise { if (!siteId || !component) { return Promise.reject(new CoreError('Site ID or component not supplied.')); } - return this.getSiteCode(siteId).then((siteCode) => this.getComponentCode(component).then((componentCode) => - // We use the % operation to keep the number under Android's limit. - (siteCode * 100000000 + componentCode * 10000000 + notificationId) % 2147483647, - )); + const siteCode = await this.getSiteCode(siteId); + const componentCode = await this.getComponentCode(component); + + // We use the % operation to keep the number under Android's limit. + return (siteCode * 100000000 + componentCode * 10000000 + notificationId) % 2147483647; } /** @@ -371,8 +372,10 @@ export class CoreLocalNotificationsProvider { await this.dbReady; try { - const stored = await this.appDB.getRecord<{id: number; at: number}>(CoreLocalNotificationsProvider.TRIGGERED_TABLE, - { id: notification.id }); + const stored = await this.appDB.getRecord<{ id: number; at: number }>( + CoreLocalNotificationsProvider.TRIGGERED_TABLE, + { id: notification.id }, + ); let triggered = (notification.trigger && notification.trigger.at) || 0; @@ -399,7 +402,7 @@ export class CoreLocalNotificationsProvider { * * @param data Data received by the notification. */ - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any + // eslint-disable-next-line @typescript-eslint/no-explicit-any notifyClick(data: any): void { this.notifyEvent('click', data); } @@ -410,7 +413,7 @@ export class CoreLocalNotificationsProvider { * @param eventName Name of the event to notify. * @param data Data received by the notification. */ - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any + // eslint-disable-next-line @typescript-eslint/no-explicit-any notifyEvent(eventName: string, data: any): void { // Execute the code in the Angular zone, so change detection doesn't stop working. NgZone.instance.run(() => { @@ -429,7 +432,7 @@ export class CoreLocalNotificationsProvider { * @param data Notification data. * @return Parsed data. */ - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any + // eslint-disable-next-line @typescript-eslint/no-explicit-any protected parseNotificationData(data: any): any { if (!data) { return {}; @@ -538,8 +541,8 @@ export class CoreLocalNotificationsProvider { */ protected requestCode(table: string, id: string): Promise { const deferred = CoreUtils.instance.promiseDefer(); - const key = table + '#' + id; - const isQueueEmpty = Object.keys(this.codeRequestsQueue).length == 0; + const key = table + '#' + id; + const isQueueEmpty = Object.keys(this.codeRequestsQueue).length == 0; if (typeof this.codeRequestsQueue[key] != 'undefined') { // There's already a pending request for this store and ID, add the promise to it. diff --git a/src/app/services/plugin-file-delegate.ts b/src/app/services/plugin-file-delegate.ts index 273632bc4..8621d13b8 100644 --- a/src/app/services/plugin-file-delegate.ts +++ b/src/app/services/plugin-file-delegate.ts @@ -70,8 +70,11 @@ export class CorePluginFileDelegate extends CoreDelegate { * @param siteId Site ID. If not defined, current site. * @return Promise resolved with the file to use. Rejected if cannot download. */ - protected async getHandlerDownloadableFile(file: CoreWSExternalFile, handler: CorePluginFileHandler, siteId?: string): - Promise { + protected async getHandlerDownloadableFile( + file: CoreWSExternalFile, + handler?: CorePluginFileHandler, + siteId?: string, + ): Promise { const isDownloadable = await this.isFileDownloadable(file, siteId); if (!isDownloadable.downloadable) { @@ -93,7 +96,7 @@ export class CorePluginFileDelegate extends CoreDelegate { * @param args Arguments of the pluginfile URL defining component and filearea at least. * @return RegExp to match the revision or undefined if not found. */ - getComponentRevisionRegExp(args: string[]): RegExp { + getComponentRevisionRegExp(args: string[]): RegExp | void { // Get handler based on component (args[1]). const handler = this.getHandler(args[1], true); @@ -110,7 +113,7 @@ export class CorePluginFileDelegate extends CoreDelegate { * @return List of URLs. */ getDownloadableFilesFromHTML(container: HTMLElement): string[] { - let files = []; + let files = []; for (const component in this.enabledHandlers) { const handler = this.enabledHandlers[component]; @@ -130,8 +133,8 @@ export class CorePluginFileDelegate extends CoreDelegate { * @param siteId Site ID. If not defined, current site. * @return Promise resolved with file size and a boolean to indicate if it is the total size or only partial. */ - async getFilesDownloadSize(files: CoreWSExternalFile[], siteId?: string): Promise<{ size: number; total: boolean }> { - const filteredFiles = []; + async getFilesDownloadSize(files: CoreWSExternalFile[], siteId: string): Promise<{ size: number; total: boolean }> { + const filteredFiles = []; await Promise.all(files.map(async (file) => { const state = await CoreFilepool.instance.getFileStateByUrl(siteId, file.fileurl, file.timemodified); @@ -270,8 +273,12 @@ export class CorePluginFileDelegate extends CoreDelegate { * @param onProgress Function to call on progress. * @return Promise resolved when done. */ - async treatDownloadedFile(fileUrl: string, file: FileEntry, siteId?: string, onProgress?: CoreFilepoolOnProgressCallback): - Promise { + async treatDownloadedFile( + fileUrl: string, + file: FileEntry, + siteId?: string, + onProgress?: CoreFilepoolOnProgressCallback, + ): Promise { const handler = this.getHandlerForFile({ fileurl: fileUrl }); if (handler && handler.treatDownloadedFile) { @@ -373,8 +380,12 @@ export interface CorePluginFileHandler extends CoreDelegateHandler { * @param onProgress Function to call on progress. * @return Promise resolved when done. */ - treatDownloadedFile?(fileUrl: string, file: FileEntry, siteId?: string, onProgress?: CoreFilepoolOnProgressCallback): - Promise; + treatDownloadedFile?( + fileUrl: string, + file: FileEntry, + siteId?: string, + onProgress?: CoreFilepoolOnProgressCallback): + Promise; } /** diff --git a/src/app/services/utils/time.ts b/src/app/services/utils/time.ts index b3bba19be..d06b45b47 100644 --- a/src/app/services/utils/time.ts +++ b/src/app/services/utils/time.ts @@ -253,7 +253,7 @@ export class CoreTimeUtilsProvider { formatDurationShort(duration: number): string { const minutes = Math.floor(duration / 60); const seconds = duration - minutes * 60; - const durations = []; + const durations = []; if (minutes > 0) { durations.push(minutes + '\''); @@ -298,16 +298,16 @@ export class CoreTimeUtilsProvider { format = Translate.instance.instant(format ? format : 'core.strftimedaydatetime'); if (fixDay) { - format = format.replace(/%d/g, '%e'); + format = format!.replace(/%d/g, '%e'); } if (fixHour) { - format = format.replace('%I', '%l'); + format = format!.replace('%I', '%l'); } // Format could be in PHP format, convert it to moment. if (convert) { - format = this.convertPHPToMoment(format); + format = this.convertPHPToMoment(format!); } return moment(timestamp).format(format); diff --git a/src/app/services/utils/url.ts b/src/app/services/utils/url.ts index 4af96bb88..c5ceb35a3 100644 --- a/src/app/services/utils/url.ts +++ b/src/app/services/utils/url.ts @@ -102,9 +102,9 @@ export class CoreUrlUtilsProvider { canUseTokenPluginFile(url: string, siteUrl: string, accessKey?: string): boolean { // Do not use tokenpluginfile if site doesn't use slash params, the URL doesn't work. // Also, only use it for "core" pluginfile endpoints. Some plugins can implement their own endpoint (like customcert). - return accessKey && !url.match(/[&?]file=/) && ( - url.indexOf(CoreTextUtils.instance.concatenatePaths(siteUrl, 'pluginfile.php')) === 0 || - url.indexOf(CoreTextUtils.instance.concatenatePaths(siteUrl, 'webservice/pluginfile.php')) === 0); + return !!accessKey && !url.match(/[&?]file=/) && ( + url.indexOf(CoreTextUtils.instance.concatenatePaths(siteUrl, 'pluginfile.php')) === 0 || + url.indexOf(CoreTextUtils.instance.concatenatePaths(siteUrl, 'webservice/pluginfile.php')) === 0); } /** @@ -255,12 +255,12 @@ export class CoreUrlUtilsProvider { * @param url URL * @return Youtube Embed Video URL or null if not found. */ - getYoutubeEmbedUrl(url: string): string { + getYoutubeEmbedUrl(url: string): string | void { if (!url) { return; } - let videoId: string; + let videoId = ''; const params: CoreUrlParams = {}; url = CoreTextUtils.instance.decodeHTML(url); @@ -327,7 +327,7 @@ export class CoreUrlUtilsProvider { * @param url URL to treat. * @return Protocol, undefined if no protocol found. */ - getUrlProtocol(url: string): string { + getUrlProtocol(url: string): string | void { if (!url) { return; } @@ -345,7 +345,7 @@ export class CoreUrlUtilsProvider { * @param url URL to treat. * @return Scheme, undefined if no scheme found. */ - getUrlScheme(url: string): string { + getUrlScheme(url: string): string | void { if (!url) { return; } @@ -362,7 +362,7 @@ export class CoreUrlUtilsProvider { * @param url URL to treat. * @return Username. Undefined if no username found. */ - getUsernameFromUrl(url: string): string { + getUsernameFromUrl(url: string): string | void { if (url.indexOf('@') > -1) { // Get URL without protocol. const withoutProtocol = url.replace(/^[^?@/]*:\/\//, ''); @@ -402,7 +402,7 @@ export class CoreUrlUtilsProvider { * @return Whether the URL is a gravatar URL. */ isGravatarUrl(url: string): boolean { - return url && url.indexOf('gravatar.com/avatar') !== -1; + return url?.indexOf('gravatar.com/avatar') !== -1; } /** @@ -424,7 +424,7 @@ export class CoreUrlUtilsProvider { isLocalFileUrl(url: string): boolean { const urlParts = CoreUrl.parse(url); - return this.isLocalFileUrlScheme(urlParts.protocol); + return this.isLocalFileUrlScheme(urlParts?.protocol || ''); } /** @@ -434,9 +434,10 @@ export class CoreUrlUtilsProvider { * @return Whether the scheme belongs to a local file. */ isLocalFileUrlScheme(scheme: string): boolean { - if (scheme) { - scheme = scheme.toLowerCase(); + if (!scheme) { + return false; } + scheme = scheme.toLowerCase(); return scheme == 'cdvfile' || scheme == 'file' || @@ -451,7 +452,7 @@ export class CoreUrlUtilsProvider { * @return Whether the URL is a pluginfile URL. */ isPluginFileUrl(url: string): boolean { - return url && url.indexOf('/pluginfile.php') !== -1; + return url?.indexOf('/pluginfile.php') !== -1; } /** @@ -461,7 +462,7 @@ export class CoreUrlUtilsProvider { * @return Whether the URL is a theme image URL. */ isThemeImageUrl(url: string): boolean { - return url && url.indexOf('/theme/image.php') !== -1; + return url?.indexOf('/theme/image.php') !== -1; } /** @@ -488,7 +489,7 @@ export class CoreUrlUtilsProvider { removeUrlParams(url: string): string { const matches = url.match(/^[^?]+/); - return matches && matches[0]; + return matches ? matches[0] : ''; } /**