forked from EVOgeek/Vmeda.Online
		
	MOBILE-3947 app: Fix linting errors
This commit is contained in:
		
							parent
							
								
									dbb3db08ae
								
							
						
					
					
						commit
						93dad58ebe
					
				| @ -217,7 +217,7 @@ | ||||
|                         <p class="item-heading">{{ relatedBadge.name }}</p> | ||||
|                     </ion-label> | ||||
|                 </ion-item> | ||||
|                 <ion-item class="ion-text-wrap" *ngIf="badge.relatedbadges.length == 0"> | ||||
|                 <ion-item class="ion-text-wrap" *ngIf="badge.relatedbadges.length === 0"> | ||||
|                     <ion-label> | ||||
|                         <p class="item-heading">{{ 'addon.badges.norelated' | translate}}</p> | ||||
|                     </ion-label> | ||||
| @ -237,7 +237,7 @@ | ||||
|                         <p class="item-heading">{{ alignment.targetname }}</p> | ||||
|                     </ion-label> | ||||
|                 </ion-item> | ||||
|                 <ion-item class="ion-text-wrap" *ngIf="badge.alignment.length == 0"> | ||||
|                 <ion-item class="ion-text-wrap" *ngIf="badge.alignment.length === 0"> | ||||
|                     <ion-label> | ||||
|                         <p class="item-heading">{{ 'addon.badges.noalignment' | translate}}</p> | ||||
|                     </ion-label> | ||||
|  | ||||
| @ -99,7 +99,7 @@ | ||||
|         </ion-col> | ||||
|     </ion-row> | ||||
| 
 | ||||
|     <core-empty-box *ngIf="filteredCourses.length == 0" image="assets/img/icons/courses.svg"> | ||||
|     <core-empty-box *ngIf="filteredCourses.length === 0" image="assets/img/icons/courses.svg"> | ||||
|         <p *ngIf="hasCourses" class="item-heading"> | ||||
|             {{'addon.block_myoverview.noresult' | translate}} | ||||
|         </p> | ||||
|  | ||||
| @ -8,7 +8,7 @@ | ||||
|     </div> | ||||
| </ion-item-divider> | ||||
| <core-loading [hideUntil]="loaded"> | ||||
|     <core-empty-box *ngIf="courses.length == 0" image="assets/img/icons/courses.svg" | ||||
|     <core-empty-box *ngIf="courses.length === 0" image="assets/img/icons/courses.svg" | ||||
|         [message]="'addon.block_recentlyaccessedcourses.nocourses' | translate"></core-empty-box> | ||||
|     <!-- List of courses. --> | ||||
|     <div [hidden]="courses.length === 0" [id]="scrollElementId" class="core-horizontal-scroll" | ||||
|  | ||||
| @ -8,7 +8,7 @@ | ||||
|     </div> | ||||
| </ion-item-divider> | ||||
| <core-loading [hideUntil]="loaded"> | ||||
|     <core-empty-box *ngIf="courses.length == 0" image="assets/img/icons/courses.svg" | ||||
|     <core-empty-box *ngIf="courses.length === 0" image="assets/img/icons/courses.svg" | ||||
|         [message]="'addon.block_starredcourses.nocourses' | translate"></core-empty-box> | ||||
|     <!-- List of courses. --> | ||||
|     <div [hidden]="courses.length === 0" [id]="scrollElementId" class="core-horizontal-scroll" | ||||
|  | ||||
| @ -20,10 +20,10 @@ | ||||
|             <ion-label>{{ 'addon.blog.showonlyyourentries' | translate }}</ion-label> | ||||
|             <ion-toggle [(ngModel)]="onlyMyEntries" (ionChange)="onlyMyEntriesToggleChanged(onlyMyEntries)" slot="end"></ion-toggle> | ||||
|         </ion-item> | ||||
|         <core-empty-box *ngIf="entries && entries.length == 0" icon="far-newspaper" [message]="'addon.blog.noentriesyet' | translate"> | ||||
|         <core-empty-box *ngIf="entries && entries.length === 0" icon="far-newspaper" [message]="'addon.blog.noentriesyet' | translate"> | ||||
|         </core-empty-box> | ||||
|         <ng-container *ngFor="let entry of entries"> | ||||
|             <ion-card *ngIf="!onlyMyEntries || entry.userid == currentUserId"> | ||||
|             <ion-card *ngIf="!onlyMyEntries || entry.userid === currentUserId"> | ||||
|                 <ion-item class="ion-text-wrap"> | ||||
|                     <core-user-avatar [user]="entry.user" slot="start" [courseId]="entry.courseid"></core-user-avatar> | ||||
|                     <ion-label> | ||||
|  | ||||
| @ -71,7 +71,7 @@ | ||||
|                                 <!-- In tablet, display list of events. --> | ||||
|                                 <div class="ion-hide-md-down addon-calendar-day-events" *ngIf="day.filteredEvents"> | ||||
|                                     <ng-container *ngFor="let event of day.filteredEvents | slice:0:4; let index = index"> | ||||
|                                         <div *ngIf="index < 3 || day.filteredEvents.length == 4" class="addon-calendar-event" | ||||
|                                         <div *ngIf="index < 3 || day.filteredEvents.length === 4" class="addon-calendar-event" | ||||
|                                             [class.addon-calendar-event-past]="event.ispast" (ariaButtonClick)="eventClicked(event, $event)" | ||||
|                                             [tabindex]="activeView ? 0 : -1"> | ||||
|                                             <span class="calendar_event_type calendar_event_{{event.formattedType}}"></span> | ||||
|  | ||||
| @ -41,7 +41,7 @@ | ||||
|                 <ion-label> | ||||
|                     <p class="item-heading" [core-mark-required]="true">{{ 'addon.calendar.eventkind' | translate }}</p> | ||||
|                 </ion-label> | ||||
|                 <p *ngIf="eventTypes.length == 1" slot="end">{{eventTypes[0].name | translate }}</p> | ||||
|                 <p *ngIf="eventTypes.length === 1" slot="end">{{eventTypes[0].name | translate }}</p> | ||||
|                 <ion-select *ngIf="eventTypes.length > 1" formControlName="eventtype" interface="action-sheet" | ||||
|                     [cancelText]="'core.cancel' | translate" [interfaceOptions]="{header: 'addon.calendar.eventkind' | translate}"> | ||||
|                     <ion-select-option *ngFor="let type of eventTypes" [value]="type.value"> | ||||
|  | ||||
| @ -86,7 +86,7 @@ | ||||
|             <ion-item class="ion-text-wrap" *ngIf="coursemodules"> | ||||
|                 <ion-label> | ||||
|                     <p class="item-heading">{{ 'addon.competency.activities' | translate }}</p> | ||||
|                     <p *ngIf="coursemodules.length == 0"> | ||||
|                     <p *ngIf="coursemodules.length === 0"> | ||||
|                         {{ 'addon.competency.noactivities' | translate }} | ||||
|                     </p> | ||||
|                     <ion-item class="ion-text-wrap" *ngFor="let activity of coursemodules" [href]="activity.url" | ||||
| @ -130,7 +130,7 @@ | ||||
| 
 | ||||
|         <div *ngIf="competency"> | ||||
|             <h2 class="ion-margin-horizontal">{{ 'addon.competency.evidence' | translate }}</h2> | ||||
|             <p class="ion-margin-horizontal" *ngIf="competency.evidence.length == 0"> | ||||
|             <p class="ion-margin-horizontal" *ngIf="competency.evidence.length === 0"> | ||||
|                 {{ 'addon.competency.noevidence' | translate }} | ||||
|             </p> | ||||
|             <ion-card *ngFor="let evidence of competency.evidence"> | ||||
|  | ||||
| @ -58,7 +58,7 @@ | ||||
|                 </ion-label> | ||||
|             </ion-item> | ||||
|         </ion-card> | ||||
|         <core-empty-box *ngIf="courseCompetencies && courseCompetencies.statistics.competencycount == 0" icon="fas-award" | ||||
|         <core-empty-box *ngIf="courseCompetencies && courseCompetencies.statistics.competencycount === 0" icon="fas-award" | ||||
|             message="{{ 'addon.competency.nocompetenciesincourse' | translate }}"> | ||||
|         </core-empty-box> | ||||
| 
 | ||||
| @ -121,7 +121,7 @@ | ||||
|                         </div> | ||||
|                         <div> | ||||
|                             <p class="item-heading">{{ 'addon.competency.activities' | translate }}</p> | ||||
|                             <p *ngIf="competency.coursemodules.length == 0"> | ||||
|                             <p *ngIf="competency.coursemodules.length === 0"> | ||||
|                                 {{ 'addon.competency.noactivities' | translate }} | ||||
|                             </p> | ||||
|                             <ion-item class="ion-text-wrap core-course-module-handler" [attr.aria-label]="activity.name" core-link | ||||
| @ -137,7 +137,7 @@ | ||||
|                         </div> | ||||
|                         <div *ngIf="competency.plans"> | ||||
|                             <p class="item-heading">{{ 'addon.competency.userplans' | translate }}</p> | ||||
|                             <p *ngIf="competency.plans.length == 0"> | ||||
|                             <p *ngIf="competency.plans.length === 0"> | ||||
|                                 {{ 'addon.competency.nouserplanswithcompetency' | translate }} | ||||
|                             </p> | ||||
|                             <ion-item class="ion-text-wrap" *ngFor="let plan of competency.plans" [href]="plan.url" | ||||
|  | ||||
| @ -75,7 +75,7 @@ | ||||
|                 <ion-card-title>{{ 'addon.competency.learningplancompetencies' | translate }}</ion-card-title> | ||||
|             </ion-card-header> | ||||
|             <ion-list> | ||||
|                 <ion-item class="ion-text-wrap" *ngIf="plan.competencycount == 0"> | ||||
|                 <ion-item class="ion-text-wrap" *ngIf="plan.competencycount === 0"> | ||||
|                     <ion-label> | ||||
|                         <p>{{ 'addon.competency.nocompetencies' | translate }}</p> | ||||
|                     </ion-label> | ||||
|  | ||||
| @ -21,9 +21,10 @@ import { CoreSite  } from '@classes/sites/site'; | ||||
| import { CoreStatusWithWarningsWSResponse, CoreWSExternalWarning } from '@services/ws'; | ||||
| import { makeSingleton } from '@singletons'; | ||||
| import { CoreError } from '@classes/errors/error'; | ||||
| import { asyncObservable, firstValueFrom } from '@/core/utils/rxjs'; | ||||
| import { asyncObservable } from '@/core/utils/rxjs'; | ||||
| import { map } from 'rxjs/operators'; | ||||
| import { CoreSiteWSPreSets, WSObservable } from '@classes/sites/authenticated-site'; | ||||
| import { firstValueFrom } from 'rxjs'; | ||||
| 
 | ||||
| const ROOT_CACHE_KEY = 'mmaCourseCompletion:'; | ||||
| 
 | ||||
|  | ||||
| @ -147,7 +147,7 @@ export class VideoJSOgvJS extends Tech { | ||||
|         if (el.hasOwnProperty(name)) { | ||||
|             el[name] = value; | ||||
|         } | ||||
|     }; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Check if browser/device is supported by Ogv.JS. | ||||
| @ -156,7 +156,7 @@ export class VideoJSOgvJS extends Tech { | ||||
|      */ | ||||
|     static isSupported(): boolean { | ||||
|         return OGVCompat.supported('OGVPlayer'); | ||||
|     }; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Check if the tech can support the given type. | ||||
| @ -166,7 +166,7 @@ export class VideoJSOgvJS extends Tech { | ||||
|      */ | ||||
|     static canPlayType(type: string): string { | ||||
|         return (type.indexOf('/ogg') !== -1 || type.indexOf('/webm')) ? 'maybe' : ''; | ||||
|     }; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Check if the tech can support the given source. | ||||
| @ -176,7 +176,7 @@ export class VideoJSOgvJS extends Tech { | ||||
|      */ | ||||
|     static canPlaySource(srcObj: TechSourceObject): string { | ||||
|         return VideoJSOgvJS.canPlayType(srcObj.type); | ||||
|     }; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Check if the volume can be changed in this browser/device. | ||||
| @ -194,7 +194,7 @@ export class VideoJSOgvJS extends Tech { | ||||
| 
 | ||||
|         // eslint-disable-next-line no-prototype-builtins
 | ||||
|         return player.hasOwnProperty('volume'); | ||||
|     }; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Check if the volume can be muted in this browser/device. | ||||
| @ -203,7 +203,7 @@ export class VideoJSOgvJS extends Tech { | ||||
|      */ | ||||
|     static canMuteVolume(): boolean { | ||||
|         return true; | ||||
|     }; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Check if the playback rate can be changed in this browser/device. | ||||
| @ -212,7 +212,7 @@ export class VideoJSOgvJS extends Tech { | ||||
|      */ | ||||
|     static canControlPlaybackRate(): boolean { | ||||
|         return true; | ||||
|     }; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Check to see if native 'TextTracks' are supported by this browser/device. | ||||
| @ -221,7 +221,7 @@ export class VideoJSOgvJS extends Tech { | ||||
|      */ | ||||
|     static supportsNativeTextTracks(): boolean { | ||||
|         return false; | ||||
|     }; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Check if the fullscreen resize is supported by this browser/device. | ||||
| @ -230,7 +230,7 @@ export class VideoJSOgvJS extends Tech { | ||||
|      */ | ||||
|     static supportsFullscreenResize(): boolean { | ||||
|         return true; | ||||
|     }; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Check if the progress events is supported by this browser/device. | ||||
| @ -239,7 +239,7 @@ export class VideoJSOgvJS extends Tech { | ||||
|      */ | ||||
|     static supportsProgressEvents(): boolean { | ||||
|         return true; | ||||
|     }; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Check if the time update events is supported by this browser/device. | ||||
| @ -248,7 +248,7 @@ export class VideoJSOgvJS extends Tech { | ||||
|      */ | ||||
|     static supportsTimeupdateEvents(): boolean { | ||||
|         return true; | ||||
|     }; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Create the 'OgvJS' Tech's DOM element. | ||||
|  | ||||
| @ -43,7 +43,7 @@ | ||||
|                         <ion-item class="ion-text-wrap addon-messages-conversation-item" | ||||
|                             *ngIf="contact.profileimageurl || contact.profileimageurlsmall" [attr.aria-label]="contact.fullname" | ||||
|                             (click)="gotoDiscussion(contact.id)" [detail]="true" button | ||||
|                             [attr.aria-current]="contact.id == discussionUserId ? 'page' : 'false'"> | ||||
|                             [attr.aria-current]="contact.id === discussionUserId ? 'page' : 'false'"> | ||||
|                             <core-user-avatar [user]="contact" slot="start" [checkOnline]="contact.showonlinestatus"></core-user-avatar> | ||||
|                             <ion-label> | ||||
|                                 <p class="item-heading">{{ contact.fullname }}</p> | ||||
|  | ||||
| @ -29,7 +29,7 @@ | ||||
|                         <ion-list class="ion-no-margin" *ngIf="confirmedContacts.length"> | ||||
|                             <ion-item class="ion-text-wrap addon-messages-conversation-item" (click)="selectUser(contact.id)" button | ||||
|                                 *ngFor="let contact of confirmedContacts" [attr.aria-label]="contact.fullname" [detail]="true" | ||||
|                                 [attr.aria-current]="contact.id == selectedUserId ? 'page' : 'false'"> | ||||
|                                 [attr.aria-current]="contact.id === selectedUserId ? 'page' : 'false'"> | ||||
|                                 <core-user-avatar slot="start" [user]="contact" [checkOnline]="contact.showonlinestatus" | ||||
|                                     [linkProfile]="false"> | ||||
|                                 </core-user-avatar> | ||||
| @ -67,7 +67,7 @@ | ||||
|                         <ion-list class="ion-no-margin" *ngIf="requests.length"> | ||||
|                             <ion-item class="ion-text-wrap addon-messages-conversation-item" *ngFor="let request of requests" | ||||
|                                 [attr.aria-label]="request.fullname" (click)="selectUser(request.id)" button | ||||
|                                 [attr.aria-current]="request.id == selectedUserId ? 'page' : 'false'" [detail]="true"> | ||||
|                                 [attr.aria-current]="request.id === selectedUserId ? 'page' : 'false'" [detail]="true"> | ||||
|                                 <core-user-avatar slot="start" [user]="request" [linkProfile]="false"></core-user-avatar> | ||||
|                                 <ion-label> | ||||
|                                     <core-format-text [text]="request.fullname" contextLevel="system" [contextInstanceId]="0"> | ||||
|  | ||||
| @ -76,7 +76,7 @@ | ||||
|                     {{ message.timecreated | coreFormatDate: "strftimedayshort" }} | ||||
|                 </h3> | ||||
| 
 | ||||
|                 <ion-chip class="addon-messages-unreadfrom" *ngIf="unreadMessageFrom > 0 && message.id == unreadMessageFrom" color="light"> | ||||
|                 <ion-chip class="addon-messages-unreadfrom" *ngIf="unreadMessageFrom > 0 && message.id === unreadMessageFrom" color="light"> | ||||
|                     <ion-label>{{ 'addon.messages.newmessages' | translate }}</ion-label> | ||||
|                     <ion-icon name="fas-arrow-down" aria-hidden="true"></ion-icon> | ||||
|                 </ion-chip> | ||||
|  | ||||
| @ -46,7 +46,7 @@ | ||||
|                     </ion-item-divider> | ||||
|                     <ion-item class="ion-text-wrap addon-message-discussion" *ngFor="let result of search.results" button | ||||
|                         [attr.aria-label]="result.fullname" (click)="gotoDiscussion(result.userid, result.messageid)" | ||||
|                         [attr.aria-current]="result.userid == discussionUserId ? 'page' : 'false'" [detail]="false"> | ||||
|                         [attr.aria-current]="result.userid === discussionUserId ? 'page' : 'false'" [detail]="false"> | ||||
|                         <core-user-avatar [user]="result" slot="start" [checkOnline]="result.showonlinestatus"></core-user-avatar> | ||||
|                         <ion-label> | ||||
|                             <p class="item-heading">{{ result.fullname }}</p> | ||||
| @ -60,7 +60,7 @@ | ||||
|                 <ng-container *ngIf="!search.showResults"> | ||||
|                     <ion-item class="ion-text-wrap addon-message-discussion" *ngFor="let discussion of discussions" button | ||||
|                         [attr.aria-label]="discussion.fullname" (click)="gotoDiscussion(discussion.message!.user)" | ||||
|                         [attr.aria-current]="discussion.message!.user == discussionUserId ? 'page' : 'false'" [detail]="false"> | ||||
|                         [attr.aria-current]="discussion.message!.user === discussionUserId ? 'page' : 'false'" [detail]="false"> | ||||
|                         <core-user-avatar [user]="discussion" slot="start" checkOnline="false"></core-user-avatar> | ||||
|                         <ion-label> | ||||
|                             <div class="flex-row ion-justify-content-between"> | ||||
|  | ||||
| @ -60,7 +60,7 @@ | ||||
|                     <!-- The infinite loading cannot be inside the ng-template, it fails because it doesn't find ion-content. --> | ||||
|                     <core-infinite-loading [enabled]="favourites.canLoadMore" (action)="loadMoreConversations(favourites, $event)" | ||||
|                         [error]="favourites.loadMoreError"></core-infinite-loading> | ||||
|                     <ion-item class="ion-text-wrap" *ngIf="favourites.conversations && favourites.conversations.length == 0"> | ||||
|                     <ion-item class="ion-text-wrap" *ngIf="favourites.conversations && favourites.conversations.length === 0"> | ||||
|                         <ion-label> | ||||
|                             <p>{{ 'addon.messages.nofavourites' | translate }}</p> | ||||
|                         </ion-label> | ||||
| @ -94,7 +94,7 @@ | ||||
|                     <!-- The infinite loading cannot be inside the ng-template, it fails because it doesn't find ion-content. --> | ||||
|                     <core-infinite-loading [enabled]="group.canLoadMore" (action)="loadMoreConversations(group, $event)" | ||||
|                         [error]="group.loadMoreError"></core-infinite-loading> | ||||
|                     <ion-item class="ion-text-wrap" *ngIf="group.conversations && group.conversations.length == 0"> | ||||
|                     <ion-item class="ion-text-wrap" *ngIf="group.conversations && group.conversations.length === 0"> | ||||
|                         <ion-label> | ||||
|                             <p>{{ 'addon.messages.nogroupconversations' | translate }}</p> | ||||
|                         </ion-label> | ||||
| @ -128,7 +128,7 @@ | ||||
|                     <!-- The infinite loading cannot be inside the ng-template, it fails because it doesn't find ion-content. --> | ||||
|                     <core-infinite-loading [enabled]="individual.canLoadMore" (action)="loadMoreConversations(individual, $event)" | ||||
|                         [error]="individual.loadMoreError"></core-infinite-loading> | ||||
|                     <ion-item class="ion-text-wrap" *ngIf="individual.conversations && individual.conversations.length == 0"> | ||||
|                     <ion-item class="ion-text-wrap" *ngIf="individual.conversations && individual.conversations.length === 0"> | ||||
|                         <ion-label> | ||||
|                             <p>{{ 'addon.messages.noindividualconversations' | translate }}</p> | ||||
|                         </ion-label> | ||||
| @ -150,17 +150,17 @@ | ||||
|     <ion-item class="ion-text-wrap addon-message-discussion" *ngFor="let conversation of conversations" button [detail]="false" | ||||
|         (click)="gotoConversation(conversation.id, conversation.userid)" | ||||
|         [attr.aria-current]="((conversation.id && | ||||
|             conversation.id == selectedConversationId) || (conversation.userid && conversation.userid == selectedUserId)) ? 'page': 'false'" | ||||
|             conversation.id === selectedConversationId) || (conversation.userid && conversation.userid === selectedUserId)) ? 'page': 'false'" | ||||
|         id="addon-message-conversation-{{ conversation.id ? conversation.id : 'user-' + conversation.userid }}" | ||||
|         [attr.aria-label]="conversation.name"> | ||||
|         <!-- Group conversation image. --> | ||||
|         <ion-avatar slot="start" *ngIf="conversation.type == typeGroup"> | ||||
|         <ion-avatar slot="start" *ngIf="conversation.type === typeGroup"> | ||||
|             <img [src]="conversation.imageurl" [alt]="conversation.name" core-external-content | ||||
|                 onError="this.src='assets/img/group-avatar.svg'"> | ||||
|         </ion-avatar> | ||||
| 
 | ||||
|         <!-- Avatar for individual conversations. --> | ||||
|         <core-user-avatar *ngIf="conversation.type != typeGroup" core-user-avatar [user]="conversation.otherUser" [linkProfile]="false" | ||||
|         <core-user-avatar *ngIf="conversation.type !== typeGroup" core-user-avatar [user]="conversation.otherUser" [linkProfile]="false" | ||||
|             [checkOnline]="conversation.showonlinestatus" slot="start"></core-user-avatar> | ||||
| 
 | ||||
|         <ion-label> | ||||
| @ -189,7 +189,7 @@ | ||||
|                 <span *ngIf="conversation.sentfromcurrentuser" class="addon-message-last-message-user"> | ||||
|                     {{ 'addon.messages.you' | translate }} | ||||
|                 </span> | ||||
|                 <span *ngIf="!conversation.sentfromcurrentuser && conversation.type == typeGroup && conversation.members[0]" | ||||
|                 <span *ngIf="!conversation.sentfromcurrentuser && conversation.type === typeGroup && conversation.members[0]" | ||||
|                     class="addon-message-last-message-user">{{ conversation.members[0].fullname + ':' }}</span> | ||||
|                 <core-format-text clean="true" singleLine="true" [text]="conversation.lastmessage" class="addon-message-last-message-text" | ||||
|                     contextLevel="system" [contextInstanceId]="0"></core-format-text> | ||||
|  | ||||
| @ -45,7 +45,7 @@ | ||||
| 
 | ||||
|         <!-- List of results --> | ||||
|         <ion-item class="addon-message-discussion ion-text-wrap" *ngFor="let result of item.results" [attr.aria-label]="result.fullname" | ||||
|             (click)="openConversation(result)" [attr.aria-current]="result == selectedResult ? 'page' : 'false'" [detail]="true" button> | ||||
|             (click)="openConversation(result)" [attr.aria-current]="result === selectedResult ? 'page' : 'false'" [detail]="true" button> | ||||
|             <core-user-avatar slot="start" [user]="result" [checkOnline]="true" [linkProfile]="false"></core-user-avatar> | ||||
|             <ion-label> | ||||
|                 <p class="item-heading"> | ||||
|  | ||||
| @ -45,11 +45,11 @@ | ||||
|                     <ion-item class="ion-text-wrap" *ngIf="currentAttempt && !isGrading"> | ||||
|                         <ion-label> | ||||
|                             <p class="item-heading">{{ 'addon.mod_assign.attemptnumber' | translate }}</p> | ||||
|                             <p *ngIf="assign!.maxattempts == unlimitedAttempts"> | ||||
|                             <p *ngIf="assign!.maxattempts === unlimitedAttempts"> | ||||
|                                 {{ 'addon.mod_assign.outof' | translate : | ||||
|                                 {'$a': {'current': currentAttempt, 'total': maxAttemptsText} } }} | ||||
|                             </p> | ||||
|                             <p *ngIf="assign!.maxattempts != unlimitedAttempts"> | ||||
|                             <p *ngIf="assign!.maxattempts !== unlimitedAttempts"> | ||||
|                                 {{ 'addon.mod_assign.outof' | translate : | ||||
|                                 {'$a': {'current': currentAttempt, 'total': assign!.maxattempts} } }} | ||||
|                             </p> | ||||
| @ -128,7 +128,7 @@ | ||||
| 
 | ||||
|                     <!-- Last modified. --> | ||||
|                     <ion-item class="ion-text-wrap" | ||||
|                         *ngIf="userSubmission && userSubmission!.status != statusNew && userSubmission!.timemodified"> | ||||
|                         *ngIf="userSubmission && userSubmission!.status !== statusNew && userSubmission!.timemodified"> | ||||
|                         <ion-label> | ||||
|                             <p class="item-heading">{{ 'addon.mod_assign.timemodified' | translate }}</p> | ||||
|                             <p>{{ userSubmission!.timemodified * 1000 | coreFormatDate }}</p> | ||||
| @ -175,7 +175,7 @@ | ||||
|                                     </ion-button> | ||||
|                                     <!-- If no submission or is new, show add submission. --> | ||||
|                                     <ion-button expand="block" class="ion-text-wrap" (click)="goToEdit()" *ngIf="!hasOffline && | ||||
|                                         (!userSubmission || !userSubmission!.status || userSubmission!.status == statusNew)"> | ||||
|                                         (!userSubmission || !userSubmission!.status || userSubmission!.status === statusNew)"> | ||||
|                                         <ng-container *ngIf="!assign?.timelimit || userSubmission?.timestarted"> | ||||
|                                             {{ 'addon.mod_assign.addsubmission' | translate }} | ||||
|                                         </ng-container> | ||||
| @ -184,7 +184,7 @@ | ||||
|                                         </ng-container> | ||||
|                                     </ion-button> | ||||
|                                     <!-- If reopened, show addfromprevious and addnewattempt. --> | ||||
|                                     <ng-container *ngIf="!hasOffline && userSubmission?.status == statusReopened"> | ||||
|                                     <ng-container *ngIf="!hasOffline && userSubmission?.status === statusReopened"> | ||||
|                                         <ion-button *ngIf="!isPreviousAttemptEmpty" expand="block" class="ion-text-wrap" | ||||
|                                             (click)="copyPrevious()"> | ||||
|                                             {{ 'addon.mod_assign.addnewattemptfromprevious' | translate }} | ||||
| @ -195,8 +195,8 @@ | ||||
|                                     </ng-container> | ||||
|                                     <!-- Else show editsubmission. --> | ||||
|                                     <ion-button expand="block" class="ion-text-wrap" *ngIf="!hasOffline && userSubmission && | ||||
|                                         userSubmission!.status && userSubmission!.status != statusNew && | ||||
|                                         userSubmission!.status != statusReopened" (click)="goToEdit()"> | ||||
|                                         userSubmission!.status && userSubmission!.status !== statusNew && | ||||
|                                         userSubmission!.status !== statusReopened" (click)="goToEdit()"> | ||||
|                                         {{ 'addon.mod_assign.editsubmission' | translate }} | ||||
|                                     </ion-button> | ||||
|                                 </ng-container> | ||||
| @ -367,15 +367,15 @@ | ||||
|                     </ion-item> | ||||
| 
 | ||||
|                     <!-- Attempt status. --> | ||||
|                     <ng-container *ngIf="isGrading && assign!.attemptreopenmethod != attemptReopenMethodNone"> | ||||
|                     <ng-container *ngIf="isGrading && assign!.attemptreopenmethod !== attemptReopenMethodNone"> | ||||
|                         <ion-item class="ion-text-wrap"> | ||||
|                             <ion-label> | ||||
|                                 <p class="item-heading">{{ 'addon.mod_assign.attemptsettings' | translate }}</p> | ||||
|                                 <p *ngIf="assign!.maxattempts == unlimitedAttempts"> | ||||
|                                 <p *ngIf="assign!.maxattempts === unlimitedAttempts"> | ||||
|                                     {{ 'addon.mod_assign.outof' | translate : | ||||
|                                     {'$a': {'current': currentAttempt, 'total': maxAttemptsText} } }} | ||||
|                                 </p> | ||||
|                                 <p *ngIf="assign!.maxattempts != unlimitedAttempts"> | ||||
|                                 <p *ngIf="assign!.maxattempts !== unlimitedAttempts"> | ||||
|                                     {{ 'addon.mod_assign.outof' | translate : | ||||
|                                     {'$a': {'current': currentAttempt, 'total': assign!.maxattempts} } }} | ||||
|                                 </p> | ||||
|  | ||||
| @ -23,7 +23,7 @@ | ||||
|         <ion-item class="ion-text-wrap" *ngFor="let chapter of chapters" [class.item-dimmed]="chapter.hidden" button [detail]="true" | ||||
|             (click)="openBook(chapter.id)"> | ||||
|             <ion-label> | ||||
|                 <p [class.ion-padding-start]="addPadding && chapter.level == 1 ? true : null"> | ||||
|                 <p [class.ion-padding-start]="addPadding && chapter.level === 1 ? true : null"> | ||||
|                     <span *ngIf="showNumbers" class="addon-mod-book-number">{{chapter.indexNumber}} </span> | ||||
|                     <span *ngIf="showBullets" class="addon-mod-book-bullet">• </span> | ||||
|                     <core-format-text [text]="chapter.title" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId"> | ||||
|  | ||||
| @ -14,9 +14,10 @@ | ||||
|     <nav> | ||||
|         <ion-list> | ||||
|             <ion-item class="ion-text-wrap" *ngFor="let chapter of chapters" (click)="loadChapter(chapter.id)" | ||||
|                 [attr.aria-current]="selected == chapter.id ? 'page' : 'false'" button [class.item-dimmed]="chapter.hidden" [detail]="false"> | ||||
|                 [attr.aria-current]="selected === chapter.id ? 'page' : 'false'" button [class.item-dimmed]="chapter.hidden" | ||||
|                 [detail]="false"> | ||||
|                 <ion-label> | ||||
|                     <p [class.ion-padding-start]="addPadding && chapter.level == 1 ? true : null" class="item-heading"> | ||||
|                     <p [class.ion-padding-start]="addPadding && chapter.level === 1 ? true : null" class="item-heading"> | ||||
|                         <span *ngIf="showNumbers" class="addon-mod-book-number">{{chapter.indexNumber}} </span> | ||||
|                         <span *ngIf="showBullets" class="addon-mod-book-bullet">• </span> | ||||
|                         <core-format-text [text]="chapter.title" contextLevel="module" [contextInstanceId]="moduleId" [courseId]="courseId"> | ||||
|  | ||||
| @ -12,12 +12,12 @@ | ||||
| </ion-header> | ||||
| <ion-content> | ||||
|     <core-loading [hideUntil]="usersLoaded"> | ||||
|         <ion-item class="ion-text-wrap" *ngFor="let user of users" [class.addon-mod-chat-user]="currentUserId != user.id && isOnline"> | ||||
|         <ion-item class="ion-text-wrap" *ngFor="let user of users" [class.addon-mod-chat-user]="currentUserId !== user.id && isOnline"> | ||||
| 
 | ||||
|             <core-user-avatar [user]="user" slot="start" [linkProfile]="false"></core-user-avatar> | ||||
|             <ion-label> | ||||
|                 <p class="item-heading">{{ user.fullname }}</p> | ||||
|                 <ng-container *ngIf="currentUserId != user.id && isOnline"> | ||||
|                 <ng-container *ngIf="currentUserId !== user.id && isOnline"> | ||||
|                     <ion-button fill="clear" (click)="talkTo(user)"> | ||||
|                         <ion-icon name="fas-comments" slot="start" aria-hidden="true"></ion-icon> | ||||
|                         {{ 'addon.mod_chat.talk' | translate }} | ||||
|  | ||||
| @ -51,7 +51,7 @@ | ||||
|                     </ion-badge> | ||||
| 
 | ||||
|                     <ion-badge class="ion-text-wrap" color="primary" | ||||
|                         *ngIf="message.userid != currentUserId && message.beep == currentUserId"> | ||||
|                         *ngIf="message.userid !== currentUserId && message.beep === currentUserId"> | ||||
|                         <span> | ||||
|                             <ion-icon name="fas-bell" aria-hidden="true"></ion-icon> | ||||
|                             {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} | ||||
|  | ||||
| @ -46,7 +46,7 @@ | ||||
|                     </ion-badge> | ||||
| 
 | ||||
|                     <ion-badge class="ion-text-wrap" color="primary" | ||||
|                         *ngIf="message.userid != currentUserId && message.beep == currentUserId"> | ||||
|                         *ngIf="message.userid !== currentUserId && message.beep === currentUserId"> | ||||
|                         <span> | ||||
|                             <ion-icon name="fas-bell" aria-hidden="true"></ion-icon> | ||||
|                             {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} | ||||
|  | ||||
| @ -42,4 +42,4 @@ export interface AddonModDataActionsMenuItem { | ||||
|     text: string; | ||||
|     icon: string; | ||||
|     action: () => void; | ||||
| }; | ||||
| } | ||||
|  | ||||
| @ -27,7 +27,7 @@ | ||||
|         </ng-container> | ||||
|     </ion-list> | ||||
| 
 | ||||
|     <core-empty-box *ngIf="!contents || (contents.files.length + contents.folders.length == 0)" icon="far-folder-open" | ||||
|     <core-empty-box *ngIf="!contents || (contents.files.length + contents.folders.length === 0)" icon="far-folder-open" | ||||
|         [message]=" 'addon.mod_folder.emptyfilelist' | translate"></core-empty-box> | ||||
| 
 | ||||
| </core-loading> | ||||
|  | ||||
| @ -61,7 +61,7 @@ | ||||
|                 </ion-label> | ||||
|             </ion-item> | ||||
|         </ion-card-header> | ||||
|         <ion-card-content [class]="post.parentid == 0 ? 'ion-padding-top' : ''"> | ||||
|         <ion-card-content [class]="post.parentid === 0 ? 'ion-padding-top' : ''"> | ||||
|             <div class="ion-padding-bottom" *ngIf="post.isprivatereply"> | ||||
|                 <ion-note color="danger">{{ 'addon.mod_forum.postisprivatereply' | translate }}</ion-note> | ||||
|             </div> | ||||
|  | ||||
| @ -14,7 +14,7 @@ | ||||
|     <ion-list id="addon-mod-forum-sort-selector" role="listbox" aria-labelledby="addon-mod-forum-sort-order-label"> | ||||
|         <ng-container *ngFor="let sortOrder of sortOrders"> | ||||
|             <ion-item class="ion-text-wrap" [detail]="false" role="combobox" | ||||
|                 [attr.aria-current]="selected == sortOrder.value ? 'page' : 'false'" [attr.aria-label]="sortOrder.label | translate" | ||||
|                 [attr.aria-current]="selected === sortOrder.value ? 'page' : 'false'" [attr.aria-label]="sortOrder.label | translate" | ||||
|                 (click)="selectSortOrder(sortOrder)" button aria-haspopup="dialog"> | ||||
|                 <ion-label> | ||||
|                     <p class="item-heading">{{ sortOrder.label | translate }}</p> | ||||
|  | ||||
| @ -14,7 +14,7 @@ | ||||
|     <nav> | ||||
|         <ion-list> | ||||
|             <ion-item *ngFor="let item of items" (click)="loadItem(item.href)" | ||||
|                 [attr.aria-current]="selected == item.href ? 'page' : 'false'" button [detail]="false"> | ||||
|                 [attr.aria-current]="selected === item.href ? 'page' : 'false'" button [detail]="false"> | ||||
|                 <ion-label [class.core-bold]="!item.href"> | ||||
|                     <p class="item-heading"> | ||||
|                         <span class="ion-padding-start" *ngFor="let i of getNumberForPadding(item.level)"></span> | ||||
|  | ||||
| @ -88,39 +88,39 @@ | ||||
| 
 | ||||
|                                 <ion-col class="ion-text-center"> | ||||
|                                     <p class="item-heading">{{ 'addon.mod_lesson.highscore' | translate }}</p> | ||||
|                                     <p *ngIf="overview.highscore != null"> | ||||
|                                     <p *ngIf="overview.highscore !== null"> | ||||
|                                         {{ 'core.percentagenumber' | translate:{$a: overview.highscore} }} | ||||
|                                     </p> | ||||
|                                     <p *ngIf="overview.highscore == null">{{ 'addon.mod_lesson.notcompleted' | translate }}</p> | ||||
|                                     <p *ngIf="overview.highscore === null">{{ 'addon.mod_lesson.notcompleted' | translate }}</p> | ||||
|                                 </ion-col> | ||||
| 
 | ||||
|                                 <ion-col class="ion-text-center"> | ||||
|                                     <p class="item-heading">{{ 'addon.mod_lesson.lowscore' | translate }}</p> | ||||
|                                     <p *ngIf="overview.lowscore != null"> | ||||
|                                     <p *ngIf="overview.lowscore !== null"> | ||||
|                                         {{ 'core.percentagenumber' | translate:{$a: overview.lowscore} }} | ||||
|                                     </p> | ||||
|                                     <p *ngIf="overview.lowscore == null">{{ 'addon.mod_lesson.notcompleted' | translate }}</p> | ||||
|                                     <p *ngIf="overview.lowscore === null">{{ 'addon.mod_lesson.notcompleted' | translate }}</p> | ||||
|                                 </ion-col> | ||||
|                             </ion-row> | ||||
|                             <ion-row> | ||||
|                                 <ion-col class="ion-text-center"> | ||||
|                                     <p class="item-heading">{{ 'addon.mod_lesson.averagetime' | translate }}</p> | ||||
|                                     <p *ngIf="overview.avetime != null && overview.numofattempts">{{ avetimeReadable }}</p> | ||||
|                                     <p *ngIf="overview.avetime == null || !overview.numofattempts"> | ||||
|                                     <p *ngIf="overview.avetime !== null && overview.numofattempts">{{ avetimeReadable }}</p> | ||||
|                                     <p *ngIf="overview.avetime === null || !overview.numofattempts"> | ||||
|                                         {{ 'addon.mod_lesson.notcompleted' | translate }} | ||||
|                                     </p> | ||||
|                                 </ion-col> | ||||
| 
 | ||||
|                                 <ion-col class="ion-text-center"> | ||||
|                                     <p class="item-heading">{{ 'addon.mod_lesson.hightime' | translate }}</p> | ||||
|                                     <p *ngIf="overview.hightime != null">{{ hightimeReadable }}</p> | ||||
|                                     <p *ngIf="overview.hightime == null">{{ 'addon.mod_lesson.notcompleted' | translate }}</p> | ||||
|                                     <p *ngIf="overview.hightime !== null">{{ hightimeReadable }}</p> | ||||
|                                     <p *ngIf="overview.hightime === null">{{ 'addon.mod_lesson.notcompleted' | translate }}</p> | ||||
|                                 </ion-col> | ||||
| 
 | ||||
|                                 <ion-col class="ion-text-center"> | ||||
|                                     <p class="item-heading">{{ 'addon.mod_lesson.lowtime' | translate }}</p> | ||||
|                                     <p *ngIf="overview.lowtime != null">{{ lowtimeReadable }}</p> | ||||
|                                     <p *ngIf="overview.lowtime == null">{{ 'addon.mod_lesson.notcompleted' | translate }}</p> | ||||
|                                     <p *ngIf="overview.lowtime !== null">{{ lowtimeReadable }}</p> | ||||
|                                     <p *ngIf="overview.lowtime === null">{{ 'addon.mod_lesson.notcompleted' | translate }}</p> | ||||
|                                 </ion-col> | ||||
|                             </ion-row> | ||||
|                         </ion-grid> | ||||
| @ -138,8 +138,8 @@ | ||||
| 
 | ||||
|                                 <ion-col [ngClass]="{'ion-text-center': overview.lessonscored}"> | ||||
|                                     <p class="item-heading">{{ 'addon.mod_lesson.averagetime' | translate }}</p> | ||||
|                                     <p *ngIf="overview.avetime != null && overview.numofattempts">{{ avetimeReadable }}</p> | ||||
|                                     <p *ngIf="overview.avetime == null || !overview.numofattempts"> | ||||
|                                     <p *ngIf="overview.avetime !== null && overview.numofattempts">{{ avetimeReadable }}</p> | ||||
|                                     <p *ngIf="overview.avetime === null || !overview.numofattempts"> | ||||
|                                         {{ 'addon.mod_lesson.notcompleted' | translate }} | ||||
|                                     </p> | ||||
|                                 </ion-col> | ||||
| @ -147,31 +147,31 @@ | ||||
|                             <ion-row> | ||||
|                                 <ion-col class="ion-text-center" *ngIf="overview.lessonscored"> | ||||
|                                     <p class="item-heading">{{ 'addon.mod_lesson.highscore' | translate }}</p> | ||||
|                                     <p *ngIf="overview.highscore != null"> | ||||
|                                     <p *ngIf="overview.highscore !== null"> | ||||
|                                         {{ 'core.percentagenumber' | translate:{$a: overview.highscore} }} | ||||
|                                     </p> | ||||
|                                     <p *ngIf="overview.highscore == null">{{ 'addon.mod_lesson.notcompleted' | translate }}</p> | ||||
|                                     <p *ngIf="overview.highscore === null">{{ 'addon.mod_lesson.notcompleted' | translate }}</p> | ||||
|                                 </ion-col> | ||||
| 
 | ||||
|                                 <ion-col [ngClass]="{'ion-text-center': overview.lessonscored}"> | ||||
|                                     <p class="item-heading">{{ 'addon.mod_lesson.hightime' | translate }}</p> | ||||
|                                     <p *ngIf="overview.hightime != null">{{ hightimeReadable }}</p> | ||||
|                                     <p *ngIf="overview.hightime == null">{{ 'addon.mod_lesson.notcompleted' | translate }}</p> | ||||
|                                     <p *ngIf="overview.hightime !== null">{{ hightimeReadable }}</p> | ||||
|                                     <p *ngIf="overview.hightime === null">{{ 'addon.mod_lesson.notcompleted' | translate }}</p> | ||||
|                                 </ion-col> | ||||
|                             </ion-row> | ||||
|                             <ion-row> | ||||
|                                 <ion-col class="ion-text-center" *ngIf="overview.lessonscored"> | ||||
|                                     <p class="item-heading">{{ 'addon.mod_lesson.lowscore' | translate }}</p> | ||||
|                                     <p *ngIf="overview.lowscore != null"> | ||||
|                                     <p *ngIf="overview.lowscore !== null"> | ||||
|                                         {{ 'core.percentagenumber' | translate:{$a: overview.lowscore} }} | ||||
|                                     </p> | ||||
|                                     <p *ngIf="overview.lowscore == null">{{ 'addon.mod_lesson.notcompleted' | translate }}</p> | ||||
|                                     <p *ngIf="overview.lowscore === null">{{ 'addon.mod_lesson.notcompleted' | translate }}</p> | ||||
|                                 </ion-col> | ||||
| 
 | ||||
|                                 <ion-col [ngClass]="{'ion-text-center': overview.lessonscored}"> | ||||
|                                     <p class="item-heading">{{ 'addon.mod_lesson.lowtime' | translate }}</p> | ||||
|                                     <p *ngIf="overview.lowtime != null">{{ lowtimeReadable }}</p> | ||||
|                                     <p *ngIf="overview.lowtime == null">{{ 'addon.mod_lesson.notcompleted' | translate }}</p> | ||||
|                                     <p *ngIf="overview.lowtime !== null">{{ lowtimeReadable }}</p> | ||||
|                                     <p *ngIf="overview.lowtime === null">{{ 'addon.mod_lesson.notcompleted' | translate }}</p> | ||||
|                                 </ion-col> | ||||
|                             </ion-row> | ||||
|                         </ion-grid> | ||||
|  | ||||
| @ -40,7 +40,7 @@ | ||||
|                 <div *ngIf="!pageInstance.loadingMenu"> | ||||
|                     <ng-container *ngFor="let page of pageInstance.lessonPages"> | ||||
|                         <ion-item class="ion-text-wrap" *ngIf="page.display && page.displayinmenublock" (click)="loadPage(page.id)" | ||||
|                             [attr.aria-current]="!pageInstance.eolData && pageInstance.currentPage == page.id ? 'page' : 'false'" button | ||||
|                             [attr.aria-current]="!pageInstance.eolData && pageInstance.currentPage === page.id ? 'page' : 'false'" button | ||||
|                             [detail]="true"> | ||||
|                             <ion-label> | ||||
|                                 <core-format-text [text]="page.title" contextLevel="module" [courseId]="pageInstance.courseId" | ||||
|  | ||||
| @ -14,7 +14,7 @@ | ||||
|     <nav> | ||||
|         <ion-list> | ||||
|             <ion-item button class="ion-text-wrap {{question.stateClass}}" *ngFor="let question of navigation" | ||||
|                 [attr.aria-current]="!summaryShown && currentPage == question.page ? 'page' : 'false'" | ||||
|                 [attr.aria-current]="!summaryShown && currentPage === question.page ? 'page' : 'false'" | ||||
|                 (click)="loadPage(question.page, question.slot)" [detail]="false"> | ||||
| 
 | ||||
|                 <ion-label class="ion-text-wrap"> | ||||
| @ -38,8 +38,8 @@ | ||||
|                     [attr.aria-label]="question.status" slot="end"> | ||||
|                 </ion-icon> | ||||
|                 <ion-icon *ngIf="question.stateClass === 'core-question-incorrect' || | ||||
|                     question.stateClass === 'core-question-notanswered'" name="fas-xmark" color="danger" [attr.aria-label]="question.status" | ||||
|                     slot="end"> | ||||
|                     question.stateClass === 'core-question-notanswered'" name="fas-xmark" color="danger" | ||||
|                     [attr.aria-label]="question.status" slot="end"> | ||||
|                 </ion-icon> | ||||
|             </ion-item> | ||||
|         </ion-list> | ||||
|  | ||||
| @ -77,7 +77,7 @@ | ||||
|                     {{ 'core.next' | translate }} | ||||
|                     <ion-icon name="fas-chevron-right" slot="end" aria-hidden="true"></ion-icon> | ||||
|                 </ion-button> | ||||
|                 <ion-button expand="block" (click)="changePage(nextPage)" class="ion-text-wrap" *ngIf="nextPage == -1"> | ||||
|                 <ion-button expand="block" (click)="changePage(nextPage)" class="ion-text-wrap" *ngIf="nextPage === -1"> | ||||
|                     {{ 'core.submit' | translate }} | ||||
|                 </ion-button> | ||||
|             </ion-col> | ||||
|  | ||||
| @ -34,7 +34,7 @@ | ||||
|                             <p class="item-heading">{{ 'addon.mod_scorm.noattemptsallowed' | translate }}</p> | ||||
|                         </ion-label> | ||||
|                         <p slot="end"> | ||||
|                             <span *ngIf="scorm.maxattempt == 0">{{ 'core.unlimited' | translate }}</span> | ||||
|                             <span *ngIf="scorm.maxattempt === 0">{{ 'core.unlimited' | translate }}</span> | ||||
|                             <span *ngIf="scorm.maxattempt! > 0">{{ scorm.maxattempt }}</span> | ||||
|                         </p> | ||||
|                     </ion-item> | ||||
| @ -59,8 +59,8 @@ | ||||
|                                 <p class="item-heading">{{ 'addon.mod_scorm.gradeforattempt' | translate }} {{attempt.num}}</p> | ||||
|                             </ion-label> | ||||
|                             <p slot="end"> | ||||
|                                 <span *ngIf="attempt.grade != -1">{{ attempt.gradeFormatted }}</span> | ||||
|                                 <span *ngIf="attempt.grade == -1">{{ 'addon.mod_scorm.cannotcalculategrade' | translate }}</span> | ||||
|                                 <span *ngIf="attempt.grade !== -1">{{ attempt.gradeFormatted }}</span> | ||||
|                                 <span *ngIf="attempt.grade === -1">{{ 'addon.mod_scorm.cannotcalculategrade' | translate }}</span> | ||||
|                             </p> | ||||
|                         </ion-item> | ||||
|                     </ng-container> | ||||
| @ -76,8 +76,8 @@ | ||||
|                         </p> | ||||
|                     </ion-label> | ||||
|                     <p slot="end"> | ||||
|                         <span *ngIf="attempt.grade != -1">{{ attempt.gradeFormatted }}</span> | ||||
|                         <span *ngIf="attempt.grade == -1">{{ 'addon.mod_scorm.cannotcalculategrade' | translate }}</span> | ||||
|                         <span *ngIf="attempt.grade !== -1">{{ attempt.gradeFormatted }}</span> | ||||
|                         <span *ngIf="attempt.grade === -1">{{ 'addon.mod_scorm.cannotcalculategrade' | translate }}</span> | ||||
|                     </p> | ||||
|                 </ion-item> | ||||
|                 <ion-item class="ion-text-wrap" *ngIf="scorm.displayattemptstatus && gradeMethodReadable"> | ||||
| @ -91,8 +91,8 @@ | ||||
|                         <p class="item-heading">{{ 'addon.mod_scorm.gradereported' | translate }}</p> | ||||
|                     </ion-label> | ||||
|                     <p slot="end"> | ||||
|                         <span *ngIf="grade != -1">{{ gradeFormatted }}</span> | ||||
|                         <span *ngIf="grade == -1">{{ 'addon.mod_scorm.cannotcalculategrade' | translate }}</span> | ||||
|                         <span *ngIf="grade !== -1">{{ gradeFormatted }}</span> | ||||
|                         <span *ngIf="grade === -1">{{ 'addon.mod_scorm.cannotcalculategrade' | translate }}</span> | ||||
|                     </p> | ||||
|                 </ion-item> | ||||
|                 <ion-item class="ion-text-wrap" *ngIf="syncTime"> | ||||
| @ -174,7 +174,7 @@ | ||||
|             </ng-container> | ||||
| 
 | ||||
|             <!-- Warning that user doesn't have any more attempts. --> | ||||
|             <ion-card *ngIf="!errorMessage && attemptsLeft == 0" class="core-danger-card"> | ||||
|             <ion-card *ngIf="!errorMessage && attemptsLeft === 0" class="core-danger-card"> | ||||
|                 <ion-item class="ion-text-wrap"> | ||||
|                     <ion-label> | ||||
|                         <p>{{ 'addon.mod_scorm.exceededmaxattempts' | translate }}</p> | ||||
|  | ||||
| @ -33,7 +33,7 @@ | ||||
|             <ng-container *ngFor="let sco of toc"> | ||||
|                 <ion-item *ngIf="sco.isvisible" class="ion-text-wrap" [detail]="sco.prereq && sco.launch" | ||||
|                     [ngClass]="'core-padding-' + sco.level + ' addon-mod_scorm-type-' + sco.scormtype" | ||||
|                     [attr.aria-current]="selected == sco.id ? 'page' : 'false'" (click)="loadSco(sco)" | ||||
|                     [attr.aria-current]="selected === sco.id ? 'page' : 'false'" (click)="loadSco(sco)" | ||||
|                     [disabled]="!sco.prereq || !sco.launch ? true : null" [button]="sco.prereq && sco.launch"> | ||||
|                     <ion-icon *ngIf="sco.icon" [name]="sco.icon.icon" [attr.aria-label]="sco.icon.description" slot="start"> | ||||
|                     </ion-icon> | ||||
|  | ||||
| @ -32,7 +32,7 @@ | ||||
|                 class="ion-no-padding ion-text-wrap"> | ||||
|                 <!-- Parent question (Category header) --> | ||||
|                 <ng-container *ngIf="question.multiArray?.length"> | ||||
|                     <h3 class="ion-padding-horizontal" [class.ion-padding-top]="questionIndex == 1">{{ question.text }}</h3> | ||||
|                     <h3 class="ion-padding-horizontal" [class.ion-padding-top]="questionIndex === 1">{{ question.text }}</h3> | ||||
|                     <ion-row class="ion-align-items-center ion-hide-md-down ion-padding"> | ||||
|                         <ion-col size="7" class="ion-padding">{{ 'addon.mod_survey.responses' | translate }}</ion-col> | ||||
|                         <ion-col size="1" class="ion-text-center option-name" | ||||
| @ -81,7 +81,7 @@ | ||||
|                 </ng-container> | ||||
| 
 | ||||
|                 <!-- Single question (don't belong to a category) --> | ||||
|                 <ng-container *ngIf="(!question.multiArray || question.multiArray.length == 0) && question.parent === 0"> | ||||
|                 <ng-container *ngIf="(!question.multiArray || question.multiArray.length === 0) && question.parent === 0"> | ||||
|                     <ion-row class="ion-align-items-center ion-padding" *ngIf="question.type > 0" [class.even]="isEven"> | ||||
|                         <ion-col size="7"> | ||||
|                             <span id="addon-mod_survey-{{question.name}}" [core-mark-required]="question.required"> | ||||
|  | ||||
| @ -25,7 +25,7 @@ | ||||
|                     </ion-label> | ||||
|                 </ion-item-divider> | ||||
|                 <ion-item class="ion-text-wrap" *ngFor="let page of letter.pages" (click)="goToPage(page)" | ||||
|                     [attr.aria-current]="selectedTitle == page.title ? 'page' : 'false'" button [detail]="false"> | ||||
|                     [attr.aria-current]="selectedTitle === page.title ? 'page' : 'false'" button [detail]="false"> | ||||
|                     <ion-icon name="fas-house" slot="start" *ngIf="page.firstpage" aria-hidden="true"></ion-icon> | ||||
|                     <ion-label> | ||||
|                         <core-format-text [text]="page.title" contextLevel="module" [contextInstanceId]="moduleId" [courseId]="courseId"> | ||||
|  | ||||
| @ -13,7 +13,7 @@ | ||||
|             <p *ngIf="access.canviewallsubmissions && showGrade(assessment.gradinggradeover)" class="core-overriden-grade"> | ||||
|                 {{ 'addon.mod_workshop.gradinggradeof' | translate:{$a: workshop.gradinggrade } }}: {{assessment.gradinggradeover}} | ||||
|             </p> | ||||
|             <p *ngIf="assessment.weight && assessment.weight != 1"> | ||||
|             <p *ngIf="assessment.weight && assessment.weight !== 1"> | ||||
|                 {{ 'addon.mod_workshop.weightinfo' | translate:{$a: assessment.weight } }} | ||||
|             </p> | ||||
|             <ion-badge *ngIf="!assessment.grade" color="danger">{{ 'addon.mod_workshop.notassessed' | translate }}</ion-badge> | ||||
|  | ||||
| @ -31,7 +31,7 @@ | ||||
|                 <ion-icon slot="start" name="fas-circle-info" color="info" *ngIf="task.completed === 'info'" | ||||
|                     [attr.aria-label]="'addon.mod_workshop.taskinfo' | translate"> | ||||
|                 </ion-icon> | ||||
|                 <ion-icon slot="start" name="fas-circle-check" color="success" *ngIf="task.completed == '1'" | ||||
|                 <ion-icon slot="start" name="fas-circle-check" color="success" *ngIf="task.completed === '1'" | ||||
|                     [attr.aria-label]="'addon.mod_workshop.taskdone' | translate"> | ||||
|                 </ion-icon> | ||||
|                 <ion-label> | ||||
| @ -45,7 +45,7 @@ | ||||
|     </ion-card> | ||||
| 
 | ||||
|     <!-- Description (setup phase only) --> | ||||
|     <ion-card *ngIf="description && workshop && workshop.phase == PHASE_SETUP"> | ||||
|     <ion-card *ngIf="description && workshop && workshop.phase === PHASE_SETUP"> | ||||
|         <ion-item class="ion-text-wrap"> | ||||
|             <ion-label> | ||||
|                 <h3 class="item-heading">{{ 'core.description' | translate }}</h3> | ||||
| @ -92,7 +92,7 @@ | ||||
|         </ng-container> | ||||
| 
 | ||||
|         <!-- SUBMISSION PHASE --> | ||||
|         <ion-card *ngIf="workshop.phase == PHASE_SUBMISSION && workshop.instructauthors"> | ||||
|         <ion-card *ngIf="workshop.phase === PHASE_SUBMISSION && workshop.instructauthors"> | ||||
|             <ion-item class="ion-text-wrap"> | ||||
|                 <ion-label> | ||||
|                     <h3 class="item-heading">{{ 'addon.mod_workshop.areainstructauthors' | translate }}</h3> | ||||
| @ -106,10 +106,10 @@ | ||||
|         <ion-card *ngIf="canSubmit"> | ||||
|             <ion-item-divider class="ion-text-wrap"> | ||||
|                 <ion-label> | ||||
|                     <h3 class="item-heading" *ngIf="workshop.phase != PHASE_CLOSED || !submission"> | ||||
|                     <h3 class="item-heading" *ngIf="workshop.phase !== PHASE_CLOSED || !submission"> | ||||
|                         {{ 'addon.mod_workshop.yoursubmission' | translate }} | ||||
|                     </h3> | ||||
|                     <h3 class="item-heading" *ngIf="workshop.phase == PHASE_CLOSED && submission"> | ||||
|                     <h3 class="item-heading" *ngIf="workshop.phase === PHASE_CLOSED && submission"> | ||||
|                         {{ 'addon.mod_workshop.yoursubmissionwithassessments' | translate }} | ||||
|                     </h3> | ||||
|                 </ion-label> | ||||
| @ -142,7 +142,7 @@ | ||||
| 
 | ||||
|         <!-- ASSESSMENT PHASE --> | ||||
|         <ng-container *ngIf="workshop.phase >= PHASE_ASSESSMENT"> | ||||
|             <ion-card *ngIf="workshop.phase == PHASE_ASSESSMENT && workshop.instructreviewers"> | ||||
|             <ion-card *ngIf="workshop.phase === PHASE_ASSESSMENT && workshop.instructreviewers"> | ||||
|                 <ion-item class="ion-text-wrap"> | ||||
|                     <ion-label> | ||||
|                         <h3 class="item-heading">{{ 'addon.mod_workshop.areainstructreviewers' | translate }}</h3> | ||||
| @ -178,7 +178,7 @@ | ||||
|             ((grades && grades.length) || (groupInfo && (groupInfo.separateGroups || groupInfo.visibleGroups)))"> | ||||
|             <ion-item-divider class="ion-text-wrap"> | ||||
|                 <ion-label> | ||||
|                     <h3 class="item-heading" *ngIf="workshop.phase == PHASE_SUBMISSION">{{ 'addon.mod_workshop.submissionsreport' | | ||||
|                     <h3 class="item-heading" *ngIf="workshop.phase === PHASE_SUBMISSION">{{ 'addon.mod_workshop.submissionsreport' | | ||||
|                         translate }}</h3> | ||||
|                     <h3 class="item-heading" *ngIf="workshop.phase > PHASE_SUBMISSION">{{ 'addon.mod_workshop.gradesreport' | translate }} | ||||
|                     </h3> | ||||
|  | ||||
| @ -13,10 +13,10 @@ | ||||
| <ion-content> | ||||
|     <ion-list> | ||||
|         <ng-container *ngFor="let phase of phases"> | ||||
|             <ion-item-divider [attr.aria-current]="workshopPhase == phase.code ? 'page' : 'false'"> | ||||
|             <ion-item-divider [attr.aria-current]="workshopPhase === phase.code ? 'page' : 'false'"> | ||||
|                 <ion-label> | ||||
|                     <h2>{{ phase.title }}</h2> | ||||
|                     <p class="ion-text-wrap" *ngIf="workshopPhase == phase.code"> | ||||
|                     <p class="ion-text-wrap" *ngIf="workshopPhase === phase.code"> | ||||
|                         {{ 'addon.mod_workshop.userplancurrentphase' | translate }} | ||||
|                     </p> | ||||
|                 </ion-label> | ||||
| @ -31,13 +31,13 @@ | ||||
|             <ion-item class="ion-text-wrap" *ngFor="let task of phase.tasks" | ||||
|                 [class.item-dimmed]="phase.code !== workshopPhase || (task.code === 'submit' && !showSubmit)" (click)="runTask(task)" | ||||
|                 [detail]="false" button> | ||||
|                 <ion-icon slot="start" name="far-circle" *ngIf="task.completed == null" | ||||
|                 <ion-icon slot="start" name="far-circle" *ngIf="task.completed === null" | ||||
|                     [attr.aria-label]="'addon.mod_workshop.tasktodo' | translate"></ion-icon> | ||||
|                 <ion-icon slot="start" name="fas-circle-xmark" color="danger" *ngIf="task.completed === ''" | ||||
|                     [attr.aria-label]="'addon.mod_workshop.taskfail' | translate"></ion-icon> | ||||
|                 <ion-icon slot="start" name="fas-circle-info" color="info" *ngIf="task.completed === 'info'" | ||||
|                     [attr.aria-label]="'addon.mod_workshop.taskinfo' | translate"></ion-icon> | ||||
|                 <ion-icon slot="start" name="fas-circle-check" color="success" *ngIf="task.completed == '1'" | ||||
|                 <ion-icon slot="start" name="fas-circle-check" color="success" *ngIf="task.completed === '1'" | ||||
|                     [attr.aria-label]="'addon.mod_workshop.taskdone' | translate"></ion-icon> | ||||
|                 <ion-label> | ||||
|                     <p class="item-heading ion-text-wrap">{{task.title}}</p> | ||||
|  | ||||
| @ -41,7 +41,7 @@ | ||||
|                     class="core-overriden-grade"> | ||||
|                     {{ 'addon.mod_workshop.gradinggradeover' | translate }}: {{assessment.gradinggradeover}} | ||||
|                 </p> | ||||
|                 <p *ngIf="assessment && assessment.weight && assessment.weight != 1"> | ||||
|                 <p *ngIf="assessment && assessment.weight && assessment.weight !== 1"> | ||||
|                     {{ 'addon.mod_workshop.weightinfo' | translate:{$a: assessment.weight } }} | ||||
|                 </p> | ||||
|                 <ion-badge *ngIf="!assessment || !showGrade(assessment.grade)" color="danger"> | ||||
|  | ||||
| @ -59,7 +59,7 @@ | ||||
|             </ion-item> | ||||
|         </ion-card> | ||||
| 
 | ||||
|         <core-empty-box *ngIf="notes && notes.length == 0" icon="fas-receipt" [message]="'addon.notes.nonotes' | translate"> | ||||
|         <core-empty-box *ngIf="notes && notes.length === 0" icon="fas-receipt" [message]="'addon.notes.nonotes' | translate"> | ||||
|         </core-empty-box> | ||||
| 
 | ||||
|         <ng-container *ngIf="notes && notes.length > 0"> | ||||
|  | ||||
| @ -63,9 +63,9 @@ | ||||
|                                     [adaptImg]="false"> | ||||
|                                 </core-format-text> | ||||
|                             </p> | ||||
|                             <ion-badge [color]="section.downloadStatus == statusDownloaded ? 'success' : 'light'" | ||||
|                             <ion-badge [color]="section.downloadStatus === statusDownloaded ? 'success' : 'light'" | ||||
|                                 *ngIf="!section.calculatingSize && section.totalSize > 0"> | ||||
|                                 <ion-icon name="fam-cloud-done" *ngIf="section.downloadStatus == statusDownloaded" | ||||
|                                 <ion-icon name="fam-cloud-done" *ngIf="section.downloadStatus === statusDownloaded" | ||||
|                                     [attr.aria-label]="'core.downloaded' | translate"> | ||||
|                                 </ion-icon>{{ section.totalSize | coreBytesToSize }} | ||||
|                             </ion-badge> | ||||
| @ -74,14 +74,14 @@ | ||||
|                             </ion-badge> | ||||
|                             <!-- Download progress. --> | ||||
|                             <p *ngIf="downloadEnabled && section.isDownloading"> | ||||
|                                 <core-progress-bar [progress]="section.total == 0 ? -1 : section.count / section.total"> | ||||
|                                 <core-progress-bar [progress]="section.total === 0 ? -1 : section.count / section.total"> | ||||
|                                 </core-progress-bar> | ||||
|                             </p> | ||||
|                         </ion-label> | ||||
|                         <div class="storage-buttons" slot="end" | ||||
|                             *ngIf="(!section.calculatingSize && section.totalSize > 0) || downloadEnabled"> | ||||
|                             <div *ngIf="downloadEnabled" slot="end" class="core-button-spinner"> | ||||
|                                 <core-download-refresh *ngIf="!section.isDownloading && section.downloadStatus != statusDownloaded" | ||||
|                                 <core-download-refresh *ngIf="!section.isDownloading && section.downloadStatus !== statusDownloaded" | ||||
|                                     [status]="section.downloadStatus" [enabled]="true" (action)="prefecthSection(section)" | ||||
|                                     [loading]="section.isDownloading || section.isCalculating" [canTrustDownload]="true"> | ||||
|                                 </core-download-refresh> | ||||
| @ -116,9 +116,9 @@ | ||||
|                                             [contextInstanceId]="module.id" [adaptImg]="false"> | ||||
|                                         </core-format-text> | ||||
|                                     </p> | ||||
|                                     <ion-badge [color]="module.downloadStatus == statusDownloaded ? 'success' : 'light'" | ||||
|                                     <ion-badge [color]="module.downloadStatus === statusDownloaded ? 'success' : 'light'" | ||||
|                                         *ngIf="!module.calculatingSize && module.totalSize > 0"> | ||||
|                                         <ion-icon name="fam-cloud-done" *ngIf="module.downloadStatus == statusDownloaded" | ||||
|                                         <ion-icon name="fam-cloud-done" *ngIf="module.downloadStatus === statusDownloaded" | ||||
|                                             [attr.aria-label]="'core.downloaded' | translate"> | ||||
|                                         </ion-icon>{{ module.totalSize | coreBytesToSize }} | ||||
|                                     </ion-badge> | ||||
| @ -129,7 +129,7 @@ | ||||
| 
 | ||||
|                                 <div class="storage-buttons" slot="end"> | ||||
|                                     <core-download-refresh *ngIf="downloadEnabled && module.handlerData?.showDownloadButton && | ||||
|                                         module.downloadStatus != statusDownloaded" [status]="module.downloadStatus" [enabled]="true" | ||||
|                                         module.downloadStatus !== statusDownloaded" [status]="module.downloadStatus" [enabled]="true" | ||||
|                                         [canTrustDownload]="true" [loading]="module.spinner || module.handlerData.spinner" | ||||
|                                         (action)="prefetchModule(module)"> | ||||
|                                     </core-download-refresh> | ||||
|  | ||||
| @ -6,10 +6,10 @@ | ||||
|                 [courseId]="courseId" [wsNotFiltered]="true"> | ||||
|             </core-format-text> | ||||
|         </p> | ||||
|         <p *ngIf="value != '0'"> | ||||
|         <p *ngIf="value !== '0'"> | ||||
|             {{ 'core.yes' | translate }} | ||||
|         </p> | ||||
|         <p *ngIf="value == '0'"> | ||||
|         <p *ngIf="value === '0'"> | ||||
|             {{ 'core.no' | translate }} | ||||
|         </p> | ||||
|     </ion-label> | ||||
|  | ||||
| @ -19,7 +19,7 @@ import { ElementController } from './ElementController'; | ||||
|  * | ||||
|  * @todo Remove frame TAG support. | ||||
|  */ | ||||
| export type FrameElement = HTMLIFrameElement | HTMLFrameElement | HTMLObjectElement | HTMLEmbedElement; | ||||
| export type FrameElement = HTMLIFrameElement | HTMLObjectElement | HTMLEmbedElement; | ||||
| 
 | ||||
| /** | ||||
|  * Wrapper class to control the interactivity of a frame element. | ||||
|  | ||||
| @ -33,9 +33,8 @@ import { CoreLang, CoreLangFormat } from '@services/lang'; | ||||
| import { CoreSites, CoreSitesReadingStrategy } from '@services/sites'; | ||||
| import { CoreSilentError } from '../errors/silenterror'; | ||||
| import { CorePromisedValue } from '@classes/promised-value'; | ||||
| import { Observable, ObservableInput, ObservedValueOf, OperatorFunction, Subject } from 'rxjs'; | ||||
| import { Observable, ObservableInput, ObservedValueOf, OperatorFunction, Subject, firstValueFrom } from 'rxjs'; | ||||
| import { finalize, map, mergeMap } from 'rxjs/operators'; | ||||
| import { firstValueFrom } from '../../utils/rxjs'; | ||||
| import { CoreSiteError } from '@classes/errors/siteerror'; | ||||
| import { CoreUserAuthenticatedSupportConfig } from '@features/user/classes/support/authenticated-support-config'; | ||||
| import { CoreSiteInfo, CoreSiteInfoResponse, CoreSitePublicConfigResponse, CoreUnauthenticatedSite } from './unauthenticated-site'; | ||||
| @ -1279,7 +1278,7 @@ export class CoreAuthenticatedSite extends CoreUnauthenticatedSite { | ||||
|         const ignoreCache = CoreSitesReadingStrategy.ONLY_NETWORK || CoreSitesReadingStrategy.PREFER_NETWORK; | ||||
|         if (!ignoreCache && this.publicConfig) { | ||||
|             return this.publicConfig; | ||||
|         }; | ||||
|         } | ||||
| 
 | ||||
|         const method = 'tool_mobile_get_public_config'; | ||||
|         const cacheId = this.getCacheId(method, {}); | ||||
|  | ||||
| @ -48,10 +48,10 @@ import { | ||||
|     WS_CACHE_TABLE, | ||||
| } from '@services/database/sites'; | ||||
| import { map } from 'rxjs/operators'; | ||||
| import { firstValueFrom } from '../../utils/rxjs'; | ||||
| import { CoreFilepool } from '@services/filepool'; | ||||
| import { CoreSiteInfo } from './unauthenticated-site'; | ||||
| import { CoreAuthenticatedSite, CoreAuthenticatedSiteOptionalData, CoreSiteWSPreSets, WSObservable } from './authenticated-site'; | ||||
| import { firstValueFrom } from 'rxjs'; | ||||
| 
 | ||||
| /** | ||||
|  * Class that represents a site (combination of site + user). | ||||
|  | ||||
| @ -174,7 +174,7 @@ export class CoreUnauthenticatedSite { | ||||
|             options.readingStrategy ===  CoreSitesReadingStrategy.PREFER_NETWORK; | ||||
|         if (!ignoreCache && this.publicConfig) { | ||||
|             return this.publicConfig; | ||||
|         }; | ||||
|         } | ||||
| 
 | ||||
|         if (options.readingStrategy === CoreSitesReadingStrategy.ONLY_CACHE) { | ||||
|             throw new CoreError('Cache not available to read public config'); | ||||
|  | ||||
| @ -1,18 +1,18 @@ | ||||
| <ng-container *ngIf="enabled && !loading"> | ||||
|     <!-- Download button. --> | ||||
|     <ion-button *ngIf="status == statusNotDownloaded" fill="clear" (click)="download($event, false)" @coreShowHideAnimation | ||||
|     <ion-button *ngIf="status === statusNotDownloaded" fill="clear" (click)="download($event, false)" @coreShowHideAnimation | ||||
|         [attr.aria-label]="(statusTranslatable || 'core.download') | translate"> | ||||
|         <ion-icon slot="icon-only" name="fas-cloud-arrow-down" aria-hidden="true"></ion-icon> | ||||
|     </ion-button> | ||||
| 
 | ||||
|     <!-- Refresh button. --> | ||||
|     <ion-button *ngIf="status == statusOutdated || (status == statusDownloaded && !canTrustDownload)" fill="clear" | ||||
|     <ion-button *ngIf="status === statusOutdated || (status === statusDownloaded && !canTrustDownload)" fill="clear" | ||||
|         (click)="download($event, true)" @coreShowHideAnimation [attr.aria-label]="(statusTranslatable || 'core.refresh') | translate"> | ||||
|         <ion-icon slot="icon-only" name="fam-cloud-refresh" aria-hidden="true"></ion-icon> | ||||
|     </ion-button> | ||||
| 
 | ||||
|     <!-- Downloaded status icon. --> | ||||
|     <ion-icon *ngIf="status == statusDownloaded && canTrustDownload" class="core-icon-downloaded ion-padding-horizontal" color="success" | ||||
|     <ion-icon *ngIf="status === statusDownloaded && canTrustDownload" class="core-icon-downloaded ion-padding-horizontal" color="success" | ||||
|         name="fam-cloud-done" [attr.aria-label]="(statusTranslatable || 'core.downloaded') | translate" role="status"></ion-icon> | ||||
| 
 | ||||
|     <ion-spinner *ngIf="status === statusDownloading" @coreShowHideAnimation | ||||
|  | ||||
| @ -8,12 +8,12 @@ | ||||
|             </ion-button> | ||||
|             <ion-slides [options]="slidesOpts" [dir]="direction" role="tablist" [attr.aria-label]="description"> | ||||
|                 <ng-container *ngFor="let tab of tabs"> | ||||
|                     <ion-slide *ngIf="tab.id" role="presentation" [id]="tab.id + '-tab'" tabindex="-1" [class.selected]="selected == tab.id" | ||||
|                         class="{{tab.class}}"> | ||||
|                     <ion-slide *ngIf="tab.id" role="presentation" [id]="tab.id + '-tab'" tabindex="-1" | ||||
|                         [class.selected]="selected === tab.id" class="{{tab.class}}"> | ||||
|                         <ion-tab-button (ionTabButtonClick)="selectTab(tab.id, $event)" (keydown)="tabAction.keyDown(tab.id, $event)" | ||||
|                             (keyup)="tabAction.keyUp(tab.id, $event)" [tab]="tab.page" [layout]="layout" role="tab" | ||||
|                             [attr.aria-controls]="tab.id" [attr.aria-selected]="selected == tab.id" | ||||
|                             [tabindex]="selected == tab.id ? 0 : -1"> | ||||
|                             [attr.aria-controls]="tab.id" [attr.aria-selected]="selected === tab.id" | ||||
|                             [tabindex]="selected === tab.id ? 0 : -1"> | ||||
|                             <ion-icon *ngIf="tab.icon" [name]="tab.icon" aria-hidden="true"></ion-icon> | ||||
|                             <ion-label> | ||||
|                                 <h2>{{ tab.title | translate}}</h2> | ||||
|  | ||||
| @ -267,7 +267,7 @@ export enum CoreAutoLogoutType { | ||||
|      * then, the user must login again. | ||||
|      */ | ||||
|     CUSTOM = 2, | ||||
| }; | ||||
| } | ||||
| 
 | ||||
| export type CoreAutoLogoutConfig = CoreAutoLogoutSessionConfig | CoreAutoLogoutOtherConfig; | ||||
| 
 | ||||
|  | ||||
| @ -18,7 +18,7 @@ | ||||
|             </ng-container> | ||||
|         </ion-list> | ||||
| 
 | ||||
|         <core-empty-box *ngIf="blocks.length == 0" icon="fas-table-cells-large" [message]="'core.block.noblocks' | translate"> | ||||
|         <core-empty-box *ngIf="blocks.length === 0" icon="fas-table-cells-large" [message]="'core.block.noblocks' | translate"> | ||||
|         </core-empty-box> | ||||
|     </core-loading> | ||||
| </ion-content> | ||||
|  | ||||
| @ -8,7 +8,7 @@ | ||||
|     <core-loading [hideUntil]="loaded"> | ||||
| 
 | ||||
|         <!-- Single section. --> | ||||
|         <div *ngIf="selectedSection && selectedSection.id != allSectionsId"> | ||||
|         <div *ngIf="selectedSection && selectedSection.id !== allSectionsId"> | ||||
|             <core-dynamic-component [component]="singleSectionComponent" [data]="data"> | ||||
|                 <ng-container *ngTemplateOutlet="sectionTemplate; context: {section: selectedSection}"></ng-container> | ||||
|                 <core-empty-box *ngIf="!selectedSection.hasContent" icon="fas-table-cells-large" | ||||
| @ -18,7 +18,7 @@ | ||||
|         </div> | ||||
| 
 | ||||
|         <!-- Multiple sections. --> | ||||
|         <div *ngIf="selectedSection && selectedSection.id == allSectionsId"> | ||||
|         <div *ngIf="selectedSection && selectedSection.id === allSectionsId"> | ||||
|             <core-dynamic-component [component]="allSectionsComponent" [data]="data"> | ||||
|                 <ng-container *ngFor="let section of sections; index as i"> | ||||
|                     <ng-container *ngIf="i <= lastShownSectionIndex"> | ||||
| @ -64,7 +64,7 @@ | ||||
| 
 | ||||
| <!-- Template to render a section. --> | ||||
| <ng-template #sectionTemplate let-section="section"> | ||||
|     <section *ngIf="!section.hiddenbynumsections && section.id != allSectionsId && section.id != stealthModulesSectionId" | ||||
|     <section *ngIf="!section.hiddenbynumsections && section.id !== allSectionsId && section.id !== stealthModulesSectionId" | ||||
|         class="core-course-module-list-wrapper" [id]="section.id" | ||||
|         [attr.aria-labelledby]="section.name ? 'core-section-name-' + section.id : null"> | ||||
|         <ion-item-divider class="course-section ion-text-wrap" [class.item-dimmed]="section.visible === 0 || section.uservisible === false"> | ||||
|  | ||||
| @ -14,7 +14,7 @@ | ||||
|     <core-loading [hideUntil]="loaded"> | ||||
|         <ion-list *ngIf="loaded" id="core-course-section-selector" role="listbox" aria-labelledby="core-course-section-selector-label"> | ||||
|             <ng-container *ngFor="let section of sectionsToRender"> | ||||
|                 <ion-item *ngIf="allSectionId == section.id" class="divider core-course-index-all" | ||||
|                 <ion-item *ngIf="allSectionId === section.id" class="divider core-course-index-all" | ||||
|                     (click)="selectSectionOrModule($event, section.id)" button [class.item-current]="selectedId === section.id" | ||||
|                     [detail]="false"> | ||||
|                     <ion-label> | ||||
| @ -24,7 +24,7 @@ | ||||
|                         </h2> | ||||
|                     </ion-label> | ||||
|                 </ion-item> | ||||
|                 <ng-container *ngIf="allSectionId != section.id"> | ||||
|                 <ng-container *ngIf="allSectionId !== section.id"> | ||||
|                     <ion-item class="divider section" (click)="selectSectionOrModule($event, section.id)" button | ||||
|                         [class.item-current]="selectedId === section.id" [class.item-dimmed]="!section.visible" | ||||
|                         [class.item-hightlighted]="section.highlighted" [detail]="false"> | ||||
|  | ||||
| @ -49,6 +49,6 @@ export class CoreCourseModuleDescriptionComponent { | ||||
| 
 | ||||
|     @HostBinding('class.deprecated') get isDeprecated(): boolean { | ||||
|         return true; | ||||
|     }; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -26,7 +26,7 @@ | ||||
|             <div class="list-item-limited-width"> | ||||
|                 <ion-item class="ion-text-wrap course-name"> | ||||
|                     <ion-label> | ||||
|                         <p *ngIf="course.displayname && course.shortname && course.fullname != course.displayname" | ||||
|                         <p *ngIf="course.displayname && course.shortname && course.fullname !== course.displayname" | ||||
|                             class="core-course-shortname"> | ||||
|                             <core-format-text [text]="course.shortname" contextLevel="course" [contextInstanceId]="course.id"> | ||||
|                             </core-format-text> | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| <ion-header [collapsible]="(tabsComponent?.selectedIndex == 0 || tabsComponent?.selectedIndex === undefined) && !fullScreenEnabled"> | ||||
| <ion-header [collapsible]="(tabsComponent?.selectedIndex === 0 || tabsComponent?.selectedIndex === undefined) && !fullScreenEnabled"> | ||||
|     <ion-toolbar> | ||||
|         <ion-buttons slot="start"> | ||||
|             <ion-back-button [text]="'core.back' | translate"></ion-back-button> | ||||
|  | ||||
| @ -53,7 +53,8 @@ import { CoreDatabaseTable } from '@classes/database/database-table'; | ||||
| import { CoreDatabaseCachingStrategy } from '@classes/database/database-table-proxy'; | ||||
| import { SQLiteDB } from '@classes/sqlitedb'; | ||||
| import { CorePlatform } from '@services/platform'; | ||||
| import { asyncObservable, firstValueFrom } from '@/core/utils/rxjs'; | ||||
| import { asyncObservable } from '@/core/utils/rxjs'; | ||||
| import { firstValueFrom } from 'rxjs'; | ||||
| import { map } from 'rxjs/operators'; | ||||
| import { CoreSiteWSPreSets, WSObservable } from '@classes/sites/authenticated-site'; | ||||
| 
 | ||||
|  | ||||
| @ -40,7 +40,7 @@ | ||||
| 
 | ||||
|         <ion-label> | ||||
|             <div class="core-course-maininfo"> | ||||
|                 <p *ngIf="course.displayname && course.shortname && course.fullname != course.displayname" | ||||
|                 <p *ngIf="course.displayname && course.shortname && course.fullname !== course.displayname" | ||||
|                     class="core-course-shortname core-course-additional-info"> | ||||
|                     <core-format-text [text]="course.shortname" contextLevel="course" [contextInstanceId]="course.id"> | ||||
|                     </core-format-text> | ||||
| @ -74,7 +74,7 @@ | ||||
|                     </ion-label> | ||||
|                 </ion-chip> | ||||
| 
 | ||||
|                 <ion-chip color="info" *ngIf="course.visible == 0" | ||||
|                 <ion-chip color="info" *ngIf="course.visible === 0" | ||||
|                     class="core-course-additional-info ion-text-wrap core-course-hidden-message"> | ||||
|                     <ion-label> | ||||
|                         {{ 'core.course.hiddenfromstudents' | translate }} | ||||
|  | ||||
| @ -37,6 +37,6 @@ export class CoreCoursesCourseProgressComponent { | ||||
| 
 | ||||
|     @HostBinding('class.deprecated') get isDeprecated(): boolean { | ||||
|         return true; | ||||
|     }; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -26,8 +26,8 @@ import { makeSingleton, Translate } from '@singletons'; | ||||
| import { CoreWSExternalFile } from '@services/ws'; | ||||
| import { AddonCourseCompletion } from '@addons/coursecompletion/services/coursecompletion'; | ||||
| import moment from 'moment-timezone'; | ||||
| import { of } from 'rxjs'; | ||||
| import { firstValueFrom, zipIncludingComplete } from '@/core/utils/rxjs'; | ||||
| import { of, firstValueFrom } from 'rxjs'; | ||||
| import { zipIncludingComplete } from '@/core/utils/rxjs'; | ||||
| import { catchError, map } from 'rxjs/operators'; | ||||
| import { chainRequests, WSObservable } from '@classes/sites/authenticated-site'; | ||||
| 
 | ||||
|  | ||||
| @ -19,8 +19,8 @@ import { makeSingleton } from '@singletons'; | ||||
| import { CoreWarningsWSResponse, CoreWSExternalFile, CoreWSExternalWarning } from '@services/ws'; | ||||
| import { CoreEvents } from '@singletons/events'; | ||||
| import { CoreCourseAnyCourseDataWithExtraInfoAndOptions, CoreCourseWithImageAndColor } from './courses-helper'; | ||||
| import { asyncObservable, firstValueFrom, ignoreErrors, zipIncludingComplete } from '@/core/utils/rxjs'; | ||||
| import { of } from 'rxjs'; | ||||
| 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'; | ||||
|  | ||||
| @ -20,7 +20,8 @@ import { CoreStatusWithWarningsWSResponse } from '@services/ws'; | ||||
| import { makeSingleton } from '@singletons'; | ||||
| import { CoreError } from '@classes/errors/error'; | ||||
| import { map } from 'rxjs/operators'; | ||||
| import { asyncObservable, firstValueFrom } from '@/core/utils/rxjs'; | ||||
| import { firstValueFrom } from 'rxjs'; | ||||
| import { asyncObservable } from '@/core/utils/rxjs'; | ||||
| import { CoreSiteWSPreSets, WSObservable } from '@classes/sites/authenticated-site'; | ||||
| 
 | ||||
| const ROOT_CACHE_KEY = 'CoreCoursesDashboard:'; | ||||
|  | ||||
| @ -31,7 +31,7 @@ import { CoreEvents, CoreEventSiteData } from '@singletons/events'; | ||||
| import { CoreLogger } from '@singletons/logger'; | ||||
| import { CoreSite } from '@classes/sites/site'; | ||||
| import { CoreCourseHelper } from '@features/course/services/course-helper'; | ||||
| import { firstValueFrom } from '@/core/utils/rxjs'; | ||||
| import { firstValueFrom } from 'rxjs'; | ||||
| 
 | ||||
| /** | ||||
|  * Helper service to provide filter functionalities. | ||||
|  | ||||
| @ -23,7 +23,7 @@ | ||||
|             {{'core.login.onboardingwelcome' | translate}} | ||||
|         </h1> | ||||
| 
 | ||||
|         <div *ngIf="step == 0" class="core-login-onboarding-step"> | ||||
|         <div *ngIf="step === 0" class="core-login-onboarding-step"> | ||||
|             <ion-button expand="block" (click)="skip($event)" class="ion-margin-bottom" fill="outline"> | ||||
|                 {{'core.login.onboardingimalearner' | translate}} | ||||
|             </ion-button> | ||||
| @ -32,7 +32,7 @@ | ||||
|             </ion-button> | ||||
|         </div> | ||||
| 
 | ||||
|         <div *ngIf="step == 1" class="core-login-onboarding-step"> | ||||
|         <div *ngIf="step === 1" class="core-login-onboarding-step"> | ||||
|             <p class="core-login-onboarding-text"> | ||||
|                 {{ 'core.login.onboardingtoconnect' | translate }} | ||||
|             </p> | ||||
| @ -44,7 +44,7 @@ | ||||
|             </ion-button> | ||||
|         </div> | ||||
| 
 | ||||
|         <div *ngIf="step == 2" class="core-login-onboarding-step"> | ||||
|         <div *ngIf="step === 2" class="core-login-onboarding-step"> | ||||
|             <ul class="core-login-onboarding-text ion-text-start"> | ||||
|                 <li> | ||||
|                     <ion-icon name="far-circle-check" aria-hidden="true"> | ||||
|  | ||||
| @ -16,7 +16,7 @@ | ||||
|     </ion-toolbar> | ||||
| </ion-header> | ||||
| <core-loading [hideUntil]="loaded"> | ||||
|     <core-empty-box *ngIf="tabs.length == 0" icon="fas-house" [message]="'core.courses.nocourses' | translate"></core-empty-box> | ||||
|     <core-empty-box *ngIf="tabs.length === 0" icon="fas-house" [message]="'core.courses.nocourses' | translate"></core-empty-box> | ||||
| </core-loading> | ||||
| <core-tabs-outlet *ngIf="tabs.length > 0" [hideUntil]="loaded" [tabs]="tabs" (ionChange)="tabSelected()"> | ||||
| </core-tabs-outlet> | ||||
|  | ||||
| @ -7,7 +7,7 @@ | ||||
| 
 | ||||
|         <ion-tab-button *ngFor="let tab of tabs" (keydown)="tabAction.keyDown(tab.page, $event)" (keyup)="tabAction.keyUp(tab.page, $event)" | ||||
|             [hidden]="!loaded && tab.hide" [tab]="tab.page" [disabled]="tab.hide" layout="label-hide" class="{{tab.class}}" | ||||
|             [selected]="tab.page === selectedTab" [tabindex]="selectedTab == tab.page ? 0 : -1" [attr.aria-controls]="tab.id"> | ||||
|             [selected]="tab.page === selectedTab" [tabindex]="selectedTab === tab.page ? 0 : -1" [attr.aria-controls]="tab.id"> | ||||
|             <ion-icon class="core-tab-icon" [name]="tab.icon" aria-hidden="true"></ion-icon> | ||||
|             <ion-label aria-hidden="true">{{ tab.title | translate }}</ion-label> | ||||
|             <ion-badge class="core-tab-badge" *ngIf="tab.badge" aria-hidden="true">{{ tab.badge }}</ion-badge> | ||||
| @ -18,7 +18,7 @@ | ||||
|         </ion-tab-button> | ||||
| 
 | ||||
|         <ion-tab-button (keydown)="tabAction.keyDown(morePageName, $event)" (keyup)="tabAction.keyUp(morePageName, $event)" | ||||
|             [hidden]="!loaded" [tab]="morePageName" layout="label-hide" [tabindex]="selectedTab == morePageName ? 0 : -1" | ||||
|             [hidden]="!loaded" [tab]="morePageName" layout="label-hide" [tabindex]="selectedTab === morePageName ? 0 : -1" | ||||
|             [attr.aria-controls]="morePageName"> | ||||
|             <ion-icon class="core-tab-icon" name="ellipsis-horizontal" aria-hidden="true"></ion-icon> | ||||
|             <ion-label aria-hidden="true">{{ 'core.more' | translate }}</ion-label> | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| <ion-item class="ion-text-wrap" *ngIf="item && (item!.canrate || item!.rating != null) && !disabled"> | ||||
| <ion-item class="ion-text-wrap" *ngIf="item && (item!.canrate || item!.rating !== null) && !disabled"> | ||||
|     <ion-label>{{ 'core.rating.rating' | translate }}</ion-label> | ||||
|     <ion-select class="ion-text-start" [(ngModel)]="rating" (ngModelChange)="userRatingChanged()" interface="action-sheet" | ||||
|         [cancelText]="'core.cancel' | translate" [disabled]="!item!.canrate" | ||||
|  | ||||
| @ -24,7 +24,7 @@ | ||||
|                 </ion-note> | ||||
|             </ion-item> | ||||
|         </ion-list> | ||||
|         <core-empty-box *ngIf="ratings.length == 0" icon="fas-star-half-stroke" [message]="'core.rating.noratings' | translate"> | ||||
|         <core-empty-box *ngIf="ratings.length === 0" icon="fas-star-half-stroke" [message]="'core.rating.noratings' | translate"> | ||||
|         </core-empty-box> | ||||
|     </core-loading> | ||||
| </ion-content> | ||||
|  | ||||
| @ -264,4 +264,4 @@ export type CoreReportBuilderReportDetailSettingsData = { | ||||
|     cardviewVisibleColumns: number; | ||||
| }; | ||||
| 
 | ||||
| export interface CoreReportBuilderReport extends CoreReportBuilderReportWSResponse {}; | ||||
| export interface CoreReportBuilderReport extends CoreReportBuilderReportWSResponse {} | ||||
|  | ||||
| @ -16,6 +16,7 @@ import { Component, OnInit } from '@angular/core'; | ||||
| import { CoreConstants } from '@/core/constants'; | ||||
| import { Http } from '@singletons'; | ||||
| import { IonSearchbar } from '@ionic/angular'; | ||||
| import { firstValueFrom } from 'rxjs'; | ||||
| 
 | ||||
| /** | ||||
|  * Defines license info | ||||
| @ -63,7 +64,7 @@ export class CoreSettingsLicensesPage implements OnInit { | ||||
|      */ | ||||
|     async ngOnInit(): Promise<void> { | ||||
|         try { | ||||
|             const licenses = await Http.get(this.licensesUrl).toPromise(); | ||||
|             const licenses = await firstValueFrom(Http.get(this.licensesUrl)); | ||||
|             this.allLicenses = Object.keys(licenses).map((name) => { | ||||
|                 const license = licenses[name]; | ||||
| 
 | ||||
|  | ||||
| @ -26,6 +26,7 @@ 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'; | ||||
| 
 | ||||
| /* | ||||
|  * Service to handle language features, like changing the current language. | ||||
| @ -501,7 +502,7 @@ export class CoreLangProvider { | ||||
|             responseType: 'json', | ||||
|         }); | ||||
| 
 | ||||
|         return <Record<string, string>> await observable.toPromise(); | ||||
|         return <Record<string, string>> await firstValueFrom(observable); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | ||||
| @ -65,6 +65,7 @@ import { CoreAutoLogoutType, CoreAutoLogout } from '@features/autologout/service | ||||
| import { CoreCacheManager } from '@services/cache-manager'; | ||||
| import { CoreSiteInfo, CoreSiteInfoResponse, CoreSitePublicConfigResponse } from '@classes/sites/unauthenticated-site'; | ||||
| import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site'; | ||||
| import { firstValueFrom } from 'rxjs'; | ||||
| 
 | ||||
| export const CORE_SITE_SCHEMAS = new InjectionToken<CoreSiteSchema[]>('CORE_SITE_SCHEMAS'); | ||||
| export const CORE_SITE_CURRENT_SITE_ID_CONFIG = 'current_site_id'; | ||||
| @ -441,7 +442,7 @@ export class CoreSitesProvider { | ||||
|         let data: CoreSitesLoginTokenResponse; | ||||
| 
 | ||||
|         try { | ||||
|             data = await Http.post(loginUrl, params).pipe(timeout(CoreWS.getRequestTimeout())).toPromise(); | ||||
|             data = await firstValueFrom(Http.post(loginUrl, params).pipe(timeout(CoreWS.getRequestTimeout()))); | ||||
|         } catch (error) { | ||||
|             throw new CoreError( | ||||
|                 this.isLoggedIn() | ||||
|  | ||||
| @ -18,7 +18,7 @@ import { HttpResponse, HttpParams, HttpErrorResponse } from '@angular/common/htt | ||||
| import { FileEntry } from '@ionic-native/file/ngx'; | ||||
| import { FileUploadOptions, FileUploadResult } from '@ionic-native/file-transfer/ngx'; | ||||
| import { Md5 } from 'ts-md5/dist/md5'; | ||||
| import { Observable } from 'rxjs'; | ||||
| import { Observable, firstValueFrom } from 'rxjs'; | ||||
| import { timeout } from 'rxjs/operators'; | ||||
| 
 | ||||
| import { CoreNativeToAngularHttpResponse } from '@classes/native-to-angular-http'; | ||||
| @ -691,7 +691,7 @@ export class CoreWSProvider { | ||||
|         const requestUrl = siteUrl + '&wsfunction=' + method; | ||||
| 
 | ||||
|         // Perform the post request.
 | ||||
|         const promise = Http.post(requestUrl, ajaxData, options).pipe(timeout(this.getRequestTimeout())).toPromise(); | ||||
|         const promise = firstValueFrom(Http.post(requestUrl, ajaxData, options).pipe(timeout(this.getRequestTimeout()))); | ||||
| 
 | ||||
|         // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | ||||
|         return promise.then(async (data: any) => { | ||||
| @ -1178,7 +1178,7 @@ export class CoreWSProvider { | ||||
|                 observable = observable.pipe(timeout(angularOptions.timeout)); | ||||
|             } | ||||
| 
 | ||||
|             return observable.toPromise(); | ||||
|             return firstValueFrom(observable); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -16,6 +16,7 @@ import { Http } from '@singletons'; | ||||
| import { CoreConstants } from '../constants'; | ||||
| import { CoreLogger } from './logger'; | ||||
| import aliases from '@/assets/fonts/font-awesome/aliases.json'; | ||||
| import { firstValueFrom } from 'rxjs'; | ||||
| 
 | ||||
| /** | ||||
|  * Singleton with helper functions for icon management. | ||||
| @ -65,8 +66,7 @@ export class CoreIcons { | ||||
|         } | ||||
| 
 | ||||
|         if (!(src in CoreIcons.DEV_ICONS_STATUS)) { | ||||
|             CoreIcons.DEV_ICONS_STATUS[src] = Http.get(src, { responseType: 'text' }) | ||||
|                 .toPromise() | ||||
|             CoreIcons.DEV_ICONS_STATUS[src] = firstValueFrom(Http.get(src, { responseType: 'text' })) | ||||
|                 .then(() => true) | ||||
|                 .catch(() => false); | ||||
|         } | ||||
|  | ||||
| @ -57,20 +57,20 @@ export class CoreSubscriptions { | ||||
|             subscription?.unsubscribe(); | ||||
|         }; | ||||
| 
 | ||||
|         subscription = subscribable.subscribe( | ||||
|             value => { | ||||
|         subscription = subscribable.subscribe({ | ||||
|             next: value => { | ||||
|                 unsubscribe(); | ||||
|                 runCallback(() => onSuccess(value)); | ||||
|             }, | ||||
|             error => { | ||||
|             error: error => { | ||||
|                 unsubscribe(); | ||||
|                 runCallback(() => onError?.(error)); | ||||
|             }, | ||||
|             () => { | ||||
|             complete: () => { | ||||
|                 unsubscribe(); | ||||
|                 runCallback(() => onComplete?.()); | ||||
|             }, | ||||
|         ); | ||||
|         }); | ||||
| 
 | ||||
|         return () => subscription?.unsubscribe(); | ||||
|     } | ||||
|  | ||||
| @ -13,9 +13,7 @@ | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { FormControl } from '@angular/forms'; | ||||
| import { CoreError } from '@classes/errors/error'; | ||||
| import { CoreSubscriptions } from '@singletons/subscriptions'; | ||||
| import { BehaviorSubject, Observable, of, OperatorFunction, Subscription } from 'rxjs'; | ||||
| import { Observable, of, OperatorFunction, Subscription } from 'rxjs'; | ||||
| import { catchError, filter } from 'rxjs/operators'; | ||||
| 
 | ||||
| /** | ||||
| @ -79,27 +77,6 @@ export function asyncObservable<T>(createObservable: () => Promise<Observable<T> | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Create a Promise resolved with the first value returned from an observable. The difference with toPromise is that | ||||
|  * this function returns the value as soon as it's emitted, it doesn't wait until the observable completes. | ||||
|  * This function can be removed when the app starts using rxjs v7. | ||||
|  * | ||||
|  * @param observable Observable. | ||||
|  * @returns Promise resolved with the first value returned. | ||||
|  */ | ||||
| export function firstValueFrom<T>(observable: Observable<T>): Promise<T> { | ||||
|     return new Promise((resolve, reject) => { | ||||
|         CoreSubscriptions.once(observable, resolve, reject, () => { | ||||
|             // Subscription is completed, check if we can get its value.
 | ||||
|             if (observable instanceof BehaviorSubject) { | ||||
|                 resolve(observable.getValue()); | ||||
|             } | ||||
| 
 | ||||
|             reject(new CoreError('Couldn\'t get first value from observable because it\'s already completed')); | ||||
|         }); | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Ignore errors from an observable, returning a certain value instead. | ||||
|  * | ||||
|  | ||||
| @ -14,7 +14,6 @@ | ||||
| 
 | ||||
| import { | ||||
|     asyncObservable, | ||||
|     firstValueFrom, | ||||
|     formControlValue, | ||||
|     ignoreErrors, | ||||
|     resolved, | ||||
| @ -23,7 +22,7 @@ import { | ||||
| } from '@/core/utils/rxjs'; | ||||
| import { mock } from '@/testing/utils'; | ||||
| import { FormControl } from '@angular/forms'; | ||||
| import { BehaviorSubject, Observable, of, Subject } from 'rxjs'; | ||||
| import { Observable, of, Subject } from 'rxjs'; | ||||
| import { TestScheduler } from 'rxjs/testing'; | ||||
| 
 | ||||
| describe('RXJS Utils', () => { | ||||
| @ -156,35 +155,6 @@ describe('RXJS Utils', () => { | ||||
|         }); | ||||
|     }); | ||||
| 
 | ||||
|     it('firstValueFrom returns first value emitted by an observable', async () => { | ||||
|         const subject = new Subject(); | ||||
|         setTimeout(() => subject.next('foo'), 10); | ||||
| 
 | ||||
|         await expect(firstValueFrom(subject)).resolves.toEqual('foo'); | ||||
| 
 | ||||
|         // Check that running it again doesn't get last value, it gets the new one.
 | ||||
|         setTimeout(() => subject.next('bar'), 10); | ||||
|         await expect(firstValueFrom(subject)).resolves.toEqual('bar'); | ||||
| 
 | ||||
|         // Check we cannot get first value if a subject is already completed.
 | ||||
|         subject.complete(); | ||||
|         await expect(firstValueFrom(subject)).rejects.toThrow(); | ||||
| 
 | ||||
|         // Check that we get last value when using BehaviourSubject.
 | ||||
|         const behaviorSubject = new BehaviorSubject('baz'); | ||||
|         await expect(firstValueFrom(behaviorSubject)).resolves.toEqual('baz'); | ||||
| 
 | ||||
|         // Check we get last value even if behaviour subject is completed.
 | ||||
|         behaviorSubject.complete(); | ||||
|         await expect(firstValueFrom(behaviorSubject)).resolves.toEqual('baz'); | ||||
| 
 | ||||
|         // Check that Promise is rejected if the observable emits an error.
 | ||||
|         const errorSubject = new Subject(); | ||||
|         setTimeout(() => errorSubject.error('foo error'), 10); | ||||
| 
 | ||||
|         await expect(firstValueFrom(errorSubject)).rejects.toMatch('foo error'); | ||||
|     }); | ||||
| 
 | ||||
|     it('ignoreErrors ignores observable errors', (done) => { | ||||
|         const subject = new Subject(); | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user