Merge pull request #3855 from crazyserver/MOBILE-3947

Mobile 3947: Pre Ionic 7 changes
main
Dani Palou 2023-11-20 16:19:20 +01:00 committed by GitHub
commit b81e49b46d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
334 changed files with 871 additions and 820 deletions

19
.editorconfig 100644
View File

@ -0,0 +1,19 @@
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true
[*.json]
indent_size = 2
[*.ts]
quote_type = single
[*.md]
max_line_length = off
trim_trailing_whitespace = false

View File

@ -1,18 +0,0 @@
name: Migration checks
on: workflow_dispatch
jobs:
checks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v3
with:
node-version-file: '.nvmrc'
- run: npm ci --no-audit
- run: result=$(find src -type f -iname '*.html' -exec sh -c 'cat {} | tr "\n" " " | grep -Eo "class=\"[^\"]+\"[^>]+class=\"" ' \; | wc -l); test $result -eq 0
- run: npm install -D @ionic/v4-migration-tslint
- run: npx tslint -c ionic-migration.json -p tsconfig.json

66
.gitignore vendored
View File

@ -6,32 +6,70 @@
.tmp
*.tmp
*.tmp.*
*.sublime-project
*.sublime-workspace
.DS_Store
Thumbs.db
UserInterfaceState.xcuserstate
$RECYCLE.BIN/
*.log
log.txt
npm-debug.log*
/.idea
/.ionic
/.sass-cache
/.sourcemaps
/.versions
/coverage
/dist
/node_modules
# Ionic
/.ionic
/www
/platforms
/plugins
/www
# Compiled output
/dist
/tmp
/out-tsc
/bazel-out
# Node
/node_modules
npm-debug.log
yarn-error.log
# IDEs and editors
.idea/
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-project
*.sublime-workspace
# Visual Studio Code
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# Miscellaneous
/.angular
/.angular/cache
.sass-cache/
/connect.lock
/coverage
/libpeerconnection.log
testem.log
/typings
# System files
.DS_Store
Thumbs.db
# Moodle App
/src/assets/lib
/src/assets/lang/*
/src/assets/env.json
/moodle.config.*.json
!/moodle.config.example.json
/src/assets/lang/*
/src/assets/env.json

View File

@ -1,5 +1,6 @@
{
"recommendations": [
"ionic.ionic",
"dbaeumer.vscode-eslint",
"angular.ng-template"
]

View File

@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<widget android-versionCode="43001" id="com.moodle.moodlemobile" ios-CFBundleVersion="4.3.0.1" version="4.3.0" versionCode="43001" xmlns="http://www.w3.org/ns/widgets" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:cdv="http://cordova.apache.org/ns/1.0">
<widget android-versionCode="44000" id="com.moodle.moodlemobile" ios-CFBundleVersion="4.4.0.0" version="4.4.0" versionCode="44000" xmlns="http://www.w3.org/ns/widgets" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:cdv="http://cordova.apache.org/ns/1.0">
<name>Moodle</name>
<description>Moodle official app</description>
<author email="mobile@moodle.com" href="http://moodle.com">Moodle Mobile team</author>
@ -28,7 +28,7 @@
<preference name="UIWebViewBounce" value="false" />
<preference name="DisallowOverscroll" value="true" />
<preference name="prerendered-icon" value="true" />
<preference name="AppendUserAgent" value="MoodleMobile 4.3.0 (43001)" />
<preference name="AppendUserAgent" value="MoodleMobile 4.4.0 (44000)" />
<preference name="BackupWebStorage" value="none" />
<preference name="ScrollEnabled" value="false" />
<preference name="KeyboardDisplayRequiresUserAction" value="false" />
@ -220,7 +220,7 @@
<true />
</edit-config>
<edit-config file="*-Info.plist" mode="merge" target="CFBundleShortVersionString">
<string>4.3.0</string>
<string>4.4.0</string>
</edit-config>
<edit-config file="*-Info.plist" mode="overwrite" target="CFBundleLocalizations">
<array>

View File

@ -1,43 +0,0 @@
{
"rulesDirectory": ["@ionic/v4-migration-tslint/rules"],
"rules": {
"ion-action-sheet-method-create-parameters-renamed": true,
"ion-alert-method-create-parameters-renamed": true,
"ion-back-button-not-added-by-default": { "options": [true], "severity": "warning" },
"ion-button-attributes-renamed": true,
"ion-button-is-now-an-element": true,
"ion-buttons-attributes-renamed": true,
"ion-col-attributes-renamed": true,
"ion-datetime-capitalization-changed": true,
"ion-fab-attributes-renamed": true,
"ion-fab-button-is-now-an-element": true,
"ion-fab-fixed-content": true,
"ion-icon-attribute-is-active-removed": true,
"ion-item-attributes-renamed": true,
"ion-item-divider-ion-label-required": true,
"ion-item-ion-label-required": true,
"ion-item-is-now-an-element": true,
"ion-item-option-is-now-an-element": true,
"ion-item-option-method-get-sliding-percent-renamed": true,
"ion-item-options-attribute-values-renamed": true,
"ion-label-attributes-renamed": true,
"ion-list-header-ion-label-required": true,
"ion-loading-method-create-parameters-renamed": true,
"ion-menu-events-renamed": true,
"ion-menu-toggle-is-now-an-element": true,
"ion-navbar-is-now-ion-toolbar": true,
"ion-option-is-now-ion-select-option": true,
"ion-overlay-method-create-should-use-await": true,
"ion-overlay-method-present-should-use-await": { "options": [true], "severity": "warning" },
"ion-radio-attributes-renamed": true,
"ion-radio-group-is-now-an-element": true,
"ion-radio-slot-required": true,
"ion-range-attributes-renamed": true,
"ion-segment-button-ion-label-required": true,
"ion-spinner-attribute-values-renamed": true,
"ion-tabs-refactored": { "options": [true], "severity": "warning" },
"ion-text-is-now-an-element": true
}
}

View File

@ -1,8 +1,8 @@
{
"app_id": "com.moodle.moodlemobile",
"appname": "Moodle Mobile",
"versioncode": 43001,
"versionname": "4.3.0",
"versioncode": 44000,
"versionname": "4.4.0",
"cache_update_frequency_usually": 420000,
"cache_update_frequency_often": 1200000,
"cache_update_frequency_sometimes": 3600000,

2
package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "moodlemobile",
"version": "4.3.0",
"version": "4.4.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "moodlemobile",
"version": "4.3.0",
"version": "4.4.0",
"description": "The official app for Moodle.",
"author": {
"name": "Moodle Pty Ltd.",

View File

@ -13,7 +13,6 @@
// limitations under the License.
import { Component, OnDestroy, OnInit } from '@angular/core';
import { IonRefresher } from '@ionic/angular';
import { CoreTimeUtils } from '@services/utils/time';
import { CoreDomUtils } from '@services/utils/dom';
import { CoreSites } from '@services/sites';
@ -130,7 +129,7 @@ export class AddonBadgesIssuedBadgePage implements OnInit, OnDestroy {
*
* @param refresher Refresher.
*/
async refreshBadges(refresher?: IonRefresher): Promise<void> {
async refreshBadges(refresher?: HTMLIonRefresherElement): Promise<void> {
await CoreUtils.ignoreErrors(Promise.all([
AddonBadges.invalidateUserBadges(this.courseId, this.userId),
]));

View File

@ -19,7 +19,7 @@
<ion-list *ngIf="!badges.empty" class="ion-no-margin">
<ion-item button class="ion-text-wrap" *ngFor="let badge of badges.items" [attr.aria-label]="badge.name"
(click)="badges.select(badge)" [attr.aria-current]="badges.getItemAriaCurrent(badge)" detail="true">
(click)="badges.select(badge)" [attr.aria-current]="badges.getItemAriaCurrent(badge)" [detail]="true">
<ion-avatar slot="start">
<img [src]="badge.badgeurl" [alt]="badge.name" core-external-content>
</ion-avatar>

View File

@ -13,7 +13,6 @@
// limitations under the License.
import { AfterViewInit, Component, OnDestroy, ViewChild } from '@angular/core';
import { IonRefresher } from '@ionic/angular';
import { AddonBadges, AddonBadgesUserBadge } from '../../services/badges';
import { CoreTimeUtils } from '@services/utils/time';
import { CoreDomUtils } from '@services/utils/dom';
@ -90,7 +89,7 @@ export class AddonBadgesUserBadgesPage implements AfterViewInit, OnDestroy {
*
* @param refresher Refresher.
*/
async refreshBadges(refresher?: IonRefresher): Promise<void> {
async refreshBadges(refresher?: HTMLIonRefresherElement): Promise<void> {
await CoreUtils.ignoreErrors(
AddonBadges.invalidateUserBadges(
this.badges.getSource().COURSE_ID,

View File

@ -1,10 +1,10 @@
<ion-item-divider sticky="true">
<ion-item-divider [sticky]="true">
<ion-label>
<h2>{{ 'addon.block_activitymodules.pluginname' | translate }}</h2>
</ion-label>
</ion-item-divider>
<core-loading [hideUntil]="loaded">
<ion-item class="ion-text-wrap" *ngFor="let entry of entries" detail="true" button (click)="gotoCoureListModType(entry)">
<ion-item class="ion-text-wrap" *ngFor="let entry of entries" [detail]="true" button (click)="gotoCoureListModType(entry)">
<core-mod-icon slot="start" [modicon]="entry.icon" [modname]="entry.iconModName" [showAlt]="false">
</core-mod-icon>
<ion-label>{{ entry.name }}</ion-label>

View File

@ -1,4 +1,4 @@
<ion-item-divider sticky="true">
<ion-item-divider [sticky]="true">
<ion-label>
<h2>{{ 'addon.block_myoverview.pluginname' | translate }}</h2>
</ion-label>
@ -88,11 +88,11 @@
</core-combobox>
</ion-col>
<ion-col size="auto" *ngIf="isLayoutSwitcherAvailable">
<ion-button *ngIf="layout == 'card'" fill="outline" (click)="toggleLayout('list')"
<ion-button *ngIf="layout === 'card'" fill="outline" (click)="toggleLayout('list')"
[attr.aria-label]="'addon.block_myoverview.aria:list' | translate">
<ion-icon slot="icon-only" name="fas-list" aria-hidden="true"></ion-icon>
</ion-button>
<ion-button *ngIf="layout == 'list'" fill="outline" (click)="toggleLayout('card')"
<ion-button *ngIf="layout === 'list'" fill="outline" (click)="toggleLayout('card')"
[attr.aria-label]="'addon.block_myoverview.aria:card' | translate">
<ion-icon slot="icon-only" name="fas-table-cells-large" aria-hidden="true"></ion-icon>
</ion-button>
@ -123,7 +123,7 @@
<!-- List of courses. -->
<div class="safe-area-padding" *ngIf="hasCourses">
<ion-grid class="ion-no-padding" [class.core-no-grid]="layout != 'card'" [class.list-item-limited-width]="layout != 'card'">
<ion-grid class="ion-no-padding" [class.core-no-grid]="layout !== 'card'" [class.list-item-limited-width]="layout !== 'card'">
<ion-row class="ion-no-padding">
<ion-col *ngFor="let course of filteredCourses" class="ion-no-padding" size="12" size-sm="6" size-md="6" size-lg="4"
size-xl="3">

View File

@ -31,7 +31,7 @@ import { CoreUtils } from '@services/utils/utils';
import { CoreDomUtils } from '@services/utils/dom';
import { CoreTextUtils } from '@services/utils/text';
import { AddonCourseCompletion } from '@addons/coursecompletion/services/coursecompletion';
import { IonRefresher, IonSearchbar } from '@ionic/angular';
import { IonSearchbar } from '@ionic/angular';
import { CoreNavigator } from '@services/navigator';
import { PageLoadWatcher } from '@classes/page-load-watcher';
import { PageLoadsManager } from '@classes/page-loads-manager';
@ -191,7 +191,7 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
* @param done Function to call when done.
* @returns Promise resolved when done.
*/
async doRefresh(refresher?: IonRefresher, done?: () => void): Promise<void> {
async doRefresh(refresher?: HTMLIonRefresherElement, done?: () => void): Promise<void> {
if (this.loaded) {
return this.refreshContent().finally(() => {
refresher?.complete();

View File

@ -1,4 +1,4 @@
@import "~theme/globals";
@use "theme/globals" as *;
:host {
--core-avatar-size: 30px;

View File

@ -1,4 +1,4 @@
@import "~theme/globals";
@use "theme/globals" as *;
:host .core-block-content ::ng-deep {
.activitydate, .activityhead {

View File

@ -1,4 +1,4 @@
<ion-item-divider sticky="true">
<ion-item-divider [sticky]="true">
<ion-label>
<h2>{{ 'addon.block_recentlyaccessedcourses.pluginname' | translate }}</h2>
</ion-label>

View File

@ -1,4 +1,4 @@
<ion-item-divider sticky="true">
<ion-item-divider [sticky]="true">
<ion-label>
<h2>{{ 'addon.block_recentlyaccesseditems.pluginname' | translate }}</h2>
</ion-label>
@ -14,7 +14,7 @@
<div class="safe-area-pseudo-padding-start"></div>
<div *ngFor="let item of items" class="core-horizontal-scroll-item">
<ion-card>
<ion-item class="core-course-module-handler ion-text-wrap" detail="false" (click)="action($event, item)" button>
<ion-item class="core-course-module-handler ion-text-wrap" [detail]="false" (click)="action($event, item)" button>
<core-mod-icon slot="start" *ngIf="item.iconUrl" [modicon]="item.iconUrl" [modname]="item.modname"
[componentId]="item.cmid" [showAlt]="false" [purpose]="item.purpose">
</core-mod-icon>

View File

@ -1,4 +1,4 @@
@import "~theme/globals";
@use "theme/globals" as *;
:host {
.core-horizontal-scroll div.core-horizontal-scroll-item {

View File

@ -1,4 +1,4 @@
<ion-item-divider sticky="true">
<ion-item-divider [sticky]="true">
<ion-label>
<h2>{{ 'addon.block_sitemainmenu.pluginname' | translate }}</h2>
</ion-label>

View File

@ -1,4 +1,4 @@
<ion-item-divider sticky="true">
<ion-item-divider [sticky]="true">
<ion-label>
<h2>{{ 'addon.block_starredcourses.pluginname' | translate }}</h2>
</ion-label>

View File

@ -44,7 +44,7 @@ export class AddonBlockTimelineSection {
this.overdue = overdue;
this.dateRange = dateRange;
this.course = course;
this.dataSubject$ = new BehaviorSubject({
this.dataSubject$ = new BehaviorSubject<AddonBlockTimelineSectionData>({
events: [],
lastEventId: canLoadMore,
canLoadMore: typeof canLoadMore !== 'undefined',

View File

@ -14,7 +14,7 @@
</ion-label>
</ion-item>
<ng-container *ngFor="let event of dayEvents.events">
<ion-item class="addon-block-timeline-activity" detail="false" (click)="action($event, event.url)" [attr.aria-label]="event.name"
<ion-item class="addon-block-timeline-activity" [detail]="false" (click)="action($event, event.url)" [attr.aria-label]="event.name"
button lines="full">
<ion-label>
<ion-row class="ion-justify-content-between ion-align-items-center ion-no-padding">

View File

@ -1,4 +1,4 @@
@import "~theme/globals";
@use "theme/globals" as *;
h3 {
font-weight: bold;

View File

@ -1,4 +1,4 @@
<ion-item-divider sticky="true">
<ion-item-divider [sticky]="true">
<ion-label>
<h2>{{ 'addon.block_timeline.pluginname' | translate }}</h2>
</ion-label>

View File

@ -56,7 +56,7 @@ export class AddonBlockTimelineComponent implements OnInit, ICoreBlockComponent
constructor() {
this.logger = CoreLogger.getInstance('AddonBlockTimelineComponent');
this.search$ = new BehaviorSubject(null);
this.search$ = new BehaviorSubject<string | null>(null);
this.initializeSort();
this.initializeFilter();
this.initializeSections();

View File

@ -65,7 +65,7 @@
<core-file *ngFor="let file of entry.attachmentfiles" [file]="file" [component]="this.component"
[componentId]="entry.id">
</core-file>
<ion-item *ngIf="entry.uniquehash" [href]="entry.uniquehash" core-link detail="true">
<ion-item *ngIf="entry.uniquehash" [href]="entry.uniquehash" core-link [detail]="true">
<ion-label>{{ 'addon.blog.linktooriginalentry' | translate }}</ion-label>
</ion-item>
</ion-card-content>

View File

@ -19,7 +19,6 @@ import { CoreComments } from '@features/comments/services/comments';
import { CoreMainMenuDeepLinkManager } from '@features/mainmenu/classes/deep-link-manager';
import { CoreTag } from '@features/tag/services/tag';
import { CoreUser, CoreUserProfile } from '@features/user/services/user';
import { IonRefresher } from '@ionic/angular';
import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics';
import { CoreNavigator } from '@services/navigator';
import { CoreSites } from '@services/sites';
@ -272,7 +271,7 @@ export class AddonBlogEntriesPage implements OnInit {
*
* @param refresher Refresher instance.
*/
refresh(refresher?: IonRefresher): void {
refresh(refresher?: HTMLIonRefresherElement): void {
const promises = this.entries.map((entry) =>
CoreComments.invalidateCommentsData('user', entry.userid, this.component, entry.id, 'format_blog'));

View File

@ -1,4 +1,4 @@
@import "~theme/globals";
@use "theme/globals" as *;
:host {

View File

@ -1,4 +1,4 @@
@import "~theme/globals";
@use "theme/globals" as *;
:host {
--addon-calendar-blank-day-background-color: var(--light);

View File

@ -1,4 +1,4 @@
@import "~theme/globals";
@use "theme/globals" as *;
:host {
ion-item {

View File

@ -13,7 +13,6 @@
// limitations under the License.
import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
import { IonRefresher } from '@ionic/angular';
import { CoreNetwork } from '@services/network';
import { CoreEventObserver, CoreEvents } from '@singletons/events';
import { CoreSites } from '@services/sites';
@ -301,7 +300,7 @@ export class AddonCalendarDayPage implements OnInit, OnDestroy {
* @param done Function to call when done.
* @returns Promise resolved when done.
*/
async doRefresh(refresher?: IonRefresher, done?: () => void): Promise<void> {
async doRefresh(refresher?: HTMLIonRefresherElement, done?: () => void): Promise<void> {
if (!this.loaded) {
return;
}

View File

@ -51,7 +51,7 @@
</ion-item>
<!-- Category. -->
<ion-item class="ion-text-wrap" *ngIf="typeControl.value == 'category'">
<ion-item class="ion-text-wrap" *ngIf="typeControl.value === 'category'">
<ion-label>
<p class="item-heading" [core-mark-required]="true">{{ 'core.category' | translate }}</p>
</ion-label>
@ -64,7 +64,7 @@
</ion-item>
<!-- Course. -->
<ion-item class="ion-text-wrap" *ngIf="typeControl.value == 'course'">
<ion-item class="ion-text-wrap" *ngIf="typeControl.value === 'course'">
<ion-label>
<p class="item-heading" [core-mark-required]="true">{{ 'core.course' | translate }}</p>
</ion-label>
@ -75,7 +75,7 @@
</ion-item>
<!-- Group. -->
<ng-container *ngIf="typeControl.value == 'group'">
<ng-container *ngIf="typeControl.value === 'group'">
<!-- Select the course. -->
<ion-item class="ion-text-wrap">
<ion-label>

View File

@ -14,7 +14,6 @@
import { Component, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/core';
import { FormControl, FormGroup, FormBuilder, Validators } from '@angular/forms';
import { IonRefresher } from '@ionic/angular';
import { CoreEvents } from '@singletons/events';
import { CoreGroup, CoreGroups } from '@services/groups';
import { CoreSites } from '@services/sites';
@ -383,7 +382,7 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
*
* @param refresher Refresher.
*/
refreshData(refresher?: IonRefresher): void {
refreshData(refresher?: HTMLIonRefresherElement): void {
const promises = [
AddonCalendar.invalidateAccessInformation(this.courseId),
AddonCalendar.invalidateAllowedEventTypes(this.courseId),

View File

@ -72,7 +72,7 @@
<p>{{ 'addon.calendar.type' + event.formattedType | translate }}</p>
</ion-label>
</ion-item>
<ion-item class="ion-text-wrap" *ngIf="courseName" [href]="courseUrl" core-link capture="true" detail="true">
<ion-item class="ion-text-wrap" *ngIf="courseName" [href]="courseUrl" core-link capture="true" [detail]="true">
<ion-label>
<p class="item-heading">{{ 'core.course' | translate}}</p>
<p>

View File

@ -13,7 +13,6 @@
// limitations under the License.
import { Component, OnDestroy, OnInit } from '@angular/core';
import { IonRefresher } from '@ionic/angular';
import { AlertOptions } from '@ionic/core';
import {
AddonCalendar,
@ -419,7 +418,7 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
* @param showErrors Whether to show sync errors to the user.
* @returns Promise resolved when done.
*/
async doRefresh(refresher?: IonRefresher, done?: () => void, showErrors= false): Promise<void> {
async doRefresh(refresher?: HTMLIonRefresherElement, done?: () => void, showErrors= false): Promise<void> {
if (!this.eventLoaded) {
return;
}

View File

@ -13,7 +13,6 @@
// limitations under the License.
import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
import { IonRefresher } from '@ionic/angular';
import { CoreNetwork } from '@services/network';
import { CoreEventObserver, CoreEvents } from '@singletons/events';
import { CoreSites } from '@services/sites';
@ -263,7 +262,7 @@ export class AddonCalendarIndexPage implements OnInit, OnDestroy {
* @param showErrors Whether to show sync errors to the user.
* @returns Promise resolved when done.
*/
async doRefresh(refresher?: IonRefresher, done?: () => void, showErrors?: boolean): Promise<void> {
async doRefresh(refresher?: HTMLIonRefresherElement, done?: () => void, showErrors?: boolean): Promise<void> {
if (!this.loaded) {
return;
}

View File

@ -20,7 +20,7 @@
<ion-list>
<ion-item class="ion-text-wrap" *ngFor="let competency of competencies.items"
[attr.aria-label]="competency.competency.shortname" (click)="competencies.select(competency)"
[attr.aria-current]="competencies.getItemAriaCurrent(competency)" button detail="true">
[attr.aria-current]="competencies.getItemAriaCurrent(competency)" button [detail]="true">
<ion-label>
<p class="item-heading">
<core-format-text [text]="competency.competency.shortname" [contextLevel]="contextLevel"

View File

@ -13,7 +13,6 @@
// limitations under the License.
import { AfterViewInit, Component, OnDestroy, ViewChild } from '@angular/core';
import { IonRefresher } from '@ionic/angular';
import { CoreDomUtils } from '@services/utils/dom';
import { CoreSplitViewComponent } from '@components/split-view/split-view';
import {
@ -121,7 +120,7 @@ export class AddonCompetencyCompetenciesPage implements AfterViewInit, OnDestroy
*
* @param refresher Refresher.
*/
async refreshCompetencies(refresher?: IonRefresher): Promise<void> {
async refreshCompetencies(refresher?: HTMLIonRefresherElement): Promise<void> {
await this.competencies.getSource().invalidateCache();
this.competencies.getSource().setDirty(true);

View File

@ -30,7 +30,6 @@ import {
AddonCompetencyProvider,
} from '@addons/competency/services/competency';
import { CoreNavigator } from '@services/navigator';
import { IonRefresher } from '@ionic/angular';
import { ContextLevel } from '@/core/constants';
import { CoreUtils } from '@services/utils/utils';
import { ADDON_COMPETENCY_SUMMARY_PAGE } from '@addons/competency/competency.module';
@ -173,7 +172,7 @@ export class AddonCompetencyCompetencyPage implements OnInit, OnDestroy {
*
* @param refresher Refresher.
*/
async refreshCompetency(refresher: IonRefresher): Promise<void> {
async refreshCompetency(refresher: HTMLIonRefresherElement): Promise<void> {
const source = this.competencies.getSource();
await CoreUtils.ignoreErrors(

View File

@ -15,7 +15,6 @@
import { Component, OnInit } from '@angular/core';
import { ContextLevel } from '@/core/constants';
import { AddonCompetencySummary, AddonCompetency } from '@addons/competency/services/competency';
import { IonRefresher } from '@ionic/angular';
import { CoreNavigator } from '@services/navigator';
import { CoreDomUtils } from '@services/utils/dom';
import { CoreUtils } from '@services/utils/utils';
@ -113,7 +112,7 @@ export class AddonCompetencyCompetencySummaryPage implements OnInit {
*
* @param refresher Refresher.
*/
refreshCompetency(refresher: IonRefresher): void {
refreshCompetency(refresher: HTMLIonRefresherElement): void {
AddonCompetency.invalidateCompetencySummary(this.competencyId).finally(() => {
this.fetchCompetency().finally(() => {
refresher?.complete();

View File

@ -65,7 +65,7 @@
<div *ngIf="competencies.loaded">
<ion-card *ngFor="let competency of competencies.items">
<ion-item class="ion-text-wrap" (click)="competencies.select(competency)"
[attr.aria-label]="competency.competency.shortname" detail="true" button>
[attr.aria-label]="competency.competency.shortname" [detail]="true" button>
<ion-label>
<p class="item-heading">
<core-format-text [text]="competency.competency.shortname" contextLevel="course" [contextInstanceId]="courseId">

View File

@ -18,7 +18,6 @@ import {
AddonCompetencyDataForCourseCompetenciesPageCompetency,
} from '@addons/competency/services/competency';
import { CoreUserProfile } from '@features/user/services/user';
import { IonRefresher } from '@ionic/angular';
import { CoreNavigator } from '@services/navigator';
import { CoreDomUtils } from '@services/utils/dom';
import { ContextLevel } from '@/core/constants';
@ -148,7 +147,7 @@ export class AddonCompetencyCourseCompetenciesPage implements OnInit, OnDestroy
*
* @param refresher Refresher.
*/
async refreshCourseCompetencies(refresher?: IonRefresher): Promise<void> {
async refreshCourseCompetencies(refresher?: HTMLIonRefresherElement): Promise<void> {
await this.competencies.getSource().invalidateCache();
this.fetchCourseCompetencies().finally(() => {

View File

@ -81,7 +81,7 @@
</ion-label>
</ion-item>
<ion-item class="ion-text-wrap" *ngFor="let competency of competencies.items" (click)="competencies.select(competency)"
[attr.aria-label]="competency.competency.shortname" detail="true" button>
[attr.aria-label]="competency.competency.shortname" [detail]="true" button>
<ion-label>
<p class="item-heading">
<core-format-text [text]="competency.competency.shortname" contextLevel="user"

View File

@ -17,7 +17,6 @@ import { CoreDomUtils } from '@services/utils/dom';
import { AddonCompetencyDataForPlanPageCompetency, AddonCompetencyDataForPlanPageWSResponse } from '../../services/competency';
import { CoreNavigator } from '@services/navigator';
import { CoreUserProfile } from '@features/user/services/user';
import { IonRefresher } from '@ionic/angular';
import { CoreSwipeNavigationItemsManager } from '@classes/items-management/swipe-navigation-items-manager';
import { CoreRoutedItemsManagerSourcesTracker } from '@classes/items-management/routed-items-manager-sources-tracker';
import { AddonCompetencyPlansSource } from '@addons/competency/classes/competency-plans-source';
@ -111,7 +110,7 @@ export class AddonCompetencyPlanPage implements OnInit, OnDestroy {
*
* @param refresher Refresher.
*/
async refreshLearningPlan(refresher: IonRefresher): Promise<void> {
async refreshLearningPlan(refresher: HTMLIonRefresherElement): Promise<void> {
await this.competencies.getSource().invalidateCache();
this.fetchLearningPlan().finally(() => {

View File

@ -19,7 +19,7 @@
</core-empty-box>
<ion-list *ngIf="!plans.empty" class="ion-no-margin">
<ion-item class="ion-text-wrap" *ngFor="let plan of plans.items" [attr.aria-label]="plan.name" (click)="plans.select(plan)"
[attr.aria-current]="plans.getItemAriaCurrent(plan)" button detail="true">
[attr.aria-current]="plans.getItemAriaCurrent(plan)" button [detail]="true">
<ion-label>
<p class="item-heading">
<core-format-text [text]="plan.name" contextLevel="user" [contextInstanceId]="plan.userid">

View File

@ -13,7 +13,6 @@
// limitations under the License.
import { AfterViewInit, Component, OnDestroy, ViewChild } from '@angular/core';
import { IonRefresher } from '@ionic/angular';
import { CoreDomUtils } from '@services/utils/dom';
import { CoreSplitViewComponent } from '@components/split-view/split-view';
import { CoreNavigator } from '@services/navigator';
@ -86,7 +85,7 @@ export class AddonCompetencyPlanListPage implements AfterViewInit, OnDestroy {
*
* @param refresher Refresher.
*/
async refreshLearningPlans(refresher: IonRefresher): Promise<void> {
async refreshLearningPlans(refresher: HTMLIonRefresherElement): Promise<void> {
await this.plans.getSource().invalidateCache();
this.plans.getSource().setDirty(true);

View File

@ -18,7 +18,6 @@ import {
} from '@addons/coursecompletion/services/coursecompletion';
import { Component, OnInit } from '@angular/core';
import { CoreUser, CoreUserProfile } from '@features/user/services/user';
import { IonRefresher } from '@ionic/angular';
import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics';
import { CoreNavigator } from '@services/navigator';
import { CoreSites } from '@services/sites';
@ -112,7 +111,7 @@ export class AddonCourseCompletionReportPage implements OnInit {
*
* @param refresher Refresher instance.
*/
async refreshCompletion(refresher?: IonRefresher): Promise<void> {
async refreshCompletion(refresher?: HTMLIonRefresherElement): Promise<void> {
await AddonCourseCompletion.invalidateCourseCompletion(this.courseId, this.userId).finally(() => {
this.fetchCompletion().finally(() => {
refresher?.complete();

View File

@ -98,7 +98,7 @@ export class AddonEnrolGuestHandlerService implements CoreEnrolGuestHandler {
return false;
}
const validatePassword = async (password: string): Promise<CorePasswordModalResponse> => {
const validatePassword = async (password = ''): Promise<CorePasswordModalResponse> => {
const modal = await CoreDomUtils.showModalLoading('core.loading', true);
try {

View File

@ -407,8 +407,8 @@ type MathJaxWindow = Window & {
MathJax?: any; // eslint-disable-line @typescript-eslint/naming-convention, @typescript-eslint/no-explicit-any
M?: { // eslint-disable-line @typescript-eslint/naming-convention
filter_mathjaxloader?: { // eslint-disable-line @typescript-eslint/naming-convention
_lang: ''; // eslint-disable-line @typescript-eslint/naming-convention
_configured: false; // eslint-disable-line @typescript-eslint/naming-convention
_lang: string; // eslint-disable-line @typescript-eslint/naming-convention
_configured: boolean; // eslint-disable-line @typescript-eslint/naming-convention
// Add the configuration to the head and set the lang.
configure: (params: Record<string, unknown>) => void;
_setLocale: () => void; // eslint-disable-line @typescript-eslint/naming-convention

View File

@ -13,7 +13,6 @@
// limitations under the License.
import { Component, OnDestroy, OnInit } from '@angular/core';
import { IonRefresher } from '@ionic/angular';
import { CoreDomUtils } from '@services/utils/dom';
import { CorePushNotifications } from '@features/pushnotifications/services/pushnotifications';
@ -123,7 +122,7 @@ export class AddonMessageOutputAirnotifierDevicesPage implements OnInit, OnDestr
*
* @param refresher Refresher.
*/
async refreshDevices(refresher: IonRefresher): Promise<void> {
async refreshDevices(refresher: HTMLIonRefresherElement): Promise<void> {
try {
await CoreUtils.ignoreErrors(AddonMessageOutputAirnotifier.invalidateUserDevices());

View File

@ -13,7 +13,6 @@
// limitations under the License.
import { Component, Input, OnInit } from '@angular/core';
import { IonRefresher } from '@ionic/angular';
import {
AddonMessagesConversationFormatted,
AddonMessagesConversationMember,
@ -116,7 +115,7 @@ export class AddonMessagesConversationInfoComponent implements OnInit {
* @param refresher Refresher.
* @returns Promise resolved when done.
*/
async refreshData(refresher?: IonRefresher): Promise<void> {
async refreshData(refresher?: HTMLIonRefresherElement): Promise<void> {
const promises: Promise<void>[] = [];
promises.push(AddonMessages.invalidateConversation(this.conversationId));

View File

@ -35,7 +35,7 @@
</ion-item>
<ion-item class="ion-text-wrap addon-messages-conversation-item" *ngFor="let member of members" (click)="closeModal(member.id)"
detail="true" button>
[detail]="true" button>
<core-user-avatar [user]="member" [linkProfile]="false" [checkOnline]="member.showonlinestatus" slot="start">
</core-user-avatar>
<ion-label>

View File

@ -1,4 +1,4 @@
@import "~theme/globals";
@use "theme/globals" as *;
:host {
.addon-messages-conversation-item,

View File

@ -22,10 +22,10 @@
autocorrect="off" spellcheck="false" lengthCheck="2" [disabled]="!loaded" searchArea="AddonMessagesContacts"></core-search-box>
<core-loading [hideUntil]="loaded" [message]="loadingMessage">
<core-empty-box *ngIf="!hasContacts && searchString == ''" icon="fas-address-book"
<core-empty-box *ngIf="!hasContacts && searchString === ''" icon="fas-address-book"
[message]="'addon.messages.contactlistempty' | translate"></core-empty-box>
<core-empty-box *ngIf="!hasContacts && searchString != ''" icon="fas-address-book"
<core-empty-box *ngIf="!hasContacts && searchString !== ''" icon="fas-address-book"
[message]="'addon.messages.nousersfound' | translate"></core-empty-box>
<ion-list *ngFor="let contactType of contactTypes" class="ion-no-margin">
@ -42,7 +42,7 @@
<!-- Don't show deleted users -->
<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
(click)="gotoDiscussion(contact.id)" [detail]="true" button
[attr.aria-current]="contact.id == discussionUserId ? 'page' : 'false'">
<core-user-avatar [user]="contact" slot="start" [checkOnline]="contact.showonlinestatus"></core-user-avatar>
<ion-label>

View File

@ -13,7 +13,6 @@
// limitations under the License.
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { IonRefresher } from '@ionic/angular';
import { CoreSites } from '@services/sites';
import {
AddonMessagesProvider,
@ -129,7 +128,7 @@ export class AddonMessagesContacts35Page implements OnInit, OnDestroy {
* @param refresher Refresher.
* @returns Promise resolved when done.
*/
async refreshData(refresher?: IonRefresher): Promise<void> {
async refreshData(refresher?: HTMLIonRefresherElement): Promise<void> {
try {
if (this.searchString) {
// User has searched, update the search.

View File

@ -28,7 +28,7 @@
<core-loading [hideUntil]="confirmedLoaded">
<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"
*ngFor="let contact of confirmedContacts" [attr.aria-label]="contact.fullname" [detail]="true"
[attr.aria-current]="contact.id == selectedUserId ? 'page' : 'false'">
<core-user-avatar slot="start" [user]="contact" [checkOnline]="contact.showonlinestatus"
[linkProfile]="false">
@ -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">

View File

@ -23,7 +23,6 @@ import {
import { CoreNavigator } from '@services/navigator';
import { CoreScreen } from '@services/screen';
import { CoreDomUtils } from '@services/utils/dom';
import { IonRefresher } from '@ionic/angular';
import { CoreSplitViewComponent } from '@components/split-view/split-view';
/**
@ -216,7 +215,7 @@ export class AddonMessagesContactsPage implements OnInit, OnDestroy {
* @param refresher Refresher.
* @returns Promise resolved when done.
*/
async refreshData(refresher?: IonRefresher): Promise<void> {
async refreshData(refresher?: HTMLIonRefresherElement): Promise<void> {
try {
if (this.selected == 'confirmed') {
// No need to invalidate contacts, we always try to get the latest.

View File

@ -103,17 +103,17 @@
</ion-fab>
</ion-content>
<ion-footer class="footer-adjustable" *ngIf="loaded && (!conversationId || conversation)">
<ion-toolbar [color]="footerType == 'message' ? null : 'light'">
<p *ngIf="footerType == 'unable'" class="ion-text-center ion-margin-horizontal">
<ion-toolbar [color]="footerType === 'message' ? null : 'light'">
<p *ngIf="footerType === 'unable'" class="ion-text-center ion-margin-horizontal">
{{ 'addon.messages.unabletomessage' | translate }}
</p>
<div *ngIf="footerType == 'blocked'" class="ion-padding-horizontal">
<div *ngIf="footerType === 'blocked'" class="ion-padding-horizontal">
<p class="ion-text-center">{{ 'addon.messages.youhaveblockeduser' | translate }}</p>
<ion-button expand="block" class="ion-text-wrap ion-margin-bottom" (click)="unblockUser()">
{{ 'addon.messages.unblockuser' | translate }}
</ion-button>
</div>
<div *ngIf="footerType == 'requiresContact' && otherMember" class="ion-padding-horizontal">
<div *ngIf="footerType === 'requiresContact' && otherMember" class="ion-padding-horizontal">
<p class="ion-text-center">
<strong>{{ 'addon.messages.isnotinyourcontacts' | translate: {$a: otherMember.fullname} }}</strong>
</p>
@ -122,7 +122,7 @@
{{ 'addon.messages.sendcontactrequest' | translate }}
</ion-button>
</div>
<div *ngIf="footerType == 'requestReceived' && otherMember" class="ion-padding-horizontal">
<div *ngIf="footerType === 'requestReceived' && otherMember" class="ion-padding-horizontal">
<p class="ion-text-center">{{ 'addon.messages.userwouldliketocontactyou' | translate: {$a: otherMember.fullname} }}</p>
<ion-button expand="block" class="ion-text-wrap ion-margin-bottom" (click)="confirmContactRequest()">
{{ 'addon.messages.acceptandaddcontact' | translate }}
@ -131,13 +131,13 @@
{{ 'addon.messages.decline' | translate }}
</ion-button>
</div>
<div *ngIf="footerType == 'requestSent' || (footerType == 'message' && requestContactSent)" class="ion-padding-horizontal">
<div *ngIf="footerType === 'requestSent' || (footerType === 'message' && requestContactSent)" class="ion-padding-horizontal">
<p class="ion-text-center"><strong>{{ 'addon.messages.contactrequestsent' | translate }}</strong></p>
<p class="ion-text-center" *ngIf="otherMember">
{{ 'addon.messages.yourcontactrequestpending' | translate: {$a: otherMember.fullname} }}
</p>
</div>
<core-send-message-form *ngIf="footerType == 'message'" (onSubmit)="sendMessage($event)" [showKeyboard]="showKeyboard"
<core-send-message-form *ngIf="footerType === 'message'" (onSubmit)="sendMessage($event)" [showKeyboard]="showKeyboard"
[placeholder]="'addon.messages.newmessage' | translate"></core-send-message-form>
</ion-toolbar>
</ion-footer>

View File

@ -1,5 +1,4 @@
@import "~theme/components/discussion.scss";
@import "~theme/globals.scss";
@use "theme/globals" as *;
:host {

View File

@ -51,7 +51,7 @@ import { CoreDom } from '@singletons/dom';
@Component({
selector: 'page-addon-messages-discussion',
templateUrl: 'discussion.html',
styleUrls: ['discussion.scss'],
styleUrls: ['../../../../theme/components/discussion.scss', 'discussion.scss'],
})
export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterViewInit {

View File

@ -28,7 +28,7 @@
<ion-list class="ion-no-margin">
<ion-item class="ion-text-wrap addon-message-discussion" (click)="gotoContacts()"
[attr.aria-label]="'addon.messages.contacts' | translate" detail="true" button>
[attr.aria-label]="'addon.messages.contacts' | translate" [detail]="true" button>
<ion-icon name="fas-address-book" slot="start" aria-hidden="true"></ion-icon>
<ion-label>
<p class="item-heading">{{ 'addon.messages.contacts' | translate }}</p>
@ -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">

View File

@ -29,7 +29,6 @@ import { CorePushNotificationsNotificationBasicData } from '@features/pushnotifi
import { CorePushNotificationsDelegate } from '@features/pushnotifications/services/push-delegate';
import { Subscription } from 'rxjs';
import { Translate } from '@singletons';
import { IonRefresher } from '@ionic/angular';
import { CoreNavigator } from '@services/navigator';
import { CoreScreen } from '@services/screen';
import { CoreMainMenuDeepLinkManager } from '@features/mainmenu/classes/deep-link-manager';
@ -166,7 +165,7 @@ export class AddonMessagesDiscussions35Page implements OnInit, OnDestroy {
* @param refreshUnreadCounts Whteher to refresh unread counts.
* @returns Promise resolved when done.
*/
async refreshData(refresher?: IonRefresher, refreshUnreadCounts: boolean = true): Promise<void> {
async refreshData(refresher?: HTMLIonRefresherElement, refreshUnreadCounts: boolean = true): Promise<void> {
const promises: Promise<void>[] = [];
promises.push(AddonMessages.invalidateDiscussionsCache(this.siteId));

View File

@ -27,7 +27,7 @@
<core-loading [hideUntil]="loaded" [message]="loadingMessage">
<ion-list>
<ion-item class="ion-text-wrap addon-message-discussion" (click)="gotoContacts()" detail="true" button>
<ion-item class="ion-text-wrap addon-message-discussion" (click)="gotoContacts()" [detail]="true" button>
<ion-icon name="fas-address-book" slot="start" aria-hidden="true"></ion-icon>
<ion-label>
<h2>{{ 'addon.messages.contacts' | translate }}</h2>
@ -38,10 +38,10 @@
</span>
</ion-item>
<!-- Favourite conversations. -->
<ion-item button class="ion-text-wrap divider" (click)="toggle(favourites)" sticky="true"
<ion-item button class="ion-text-wrap divider" (click)="toggle(favourites)"
[attr.aria-label]="(favourites.expanded ? 'core.collapse' : 'core.expand') | translate"
[attr.aria-expanded]="favourites.expanded" aria-controls="addon-messages-groupconversations-favourite" role="heading"
detail="false">
[detail]="false">
<ion-icon name="fas-chevron-right" flip-rtl slot="start" aria-hidden="true" class="expandable-status-icon"
[class.expandable-status-icon-expanded]="favourites.expanded">
</ion-icon>
@ -73,9 +73,9 @@
</ion-item>
<!-- Group conversations. -->
<ion-item button class="divider ion-text-wrap" (click)="toggle(group)" sticky="true"
<ion-item button class="divider ion-text-wrap" (click)="toggle(group)"
[attr.aria-label]="(group.expanded ? 'core.collapse' : 'core.expand') | translate" [attr.aria-expanded]="group.expanded"
aria-controls="addon-messages-groupconversations-group" role="heading" detail="false">
aria-controls="addon-messages-groupconversations-group" role="heading" [detail]="false">
<ion-icon name="fas-chevron-right" flip-rtl slot="start" aria-hidden="true" class="expandable-status-icon"
[class.expandable-status-icon-expanded]="group.expanded">
</ion-icon>
@ -106,10 +106,10 @@
</ion-label>
</ion-item>
<ion-item button class="divider ion-text-wrap" (click)="toggle(individual)" sticky="true"
<ion-item button class="divider ion-text-wrap" (click)="toggle(individual)"
[attr.aria-label]="(individual.expanded ? 'core.collapse' : 'core.expand') | translate"
[attr.aria-expanded]="individual.expanded" aria-controls="addon-messages-groupconversations-individual" role="heading"
detail="false">
[detail]="false">
<ion-icon name="fas-chevron-right" flip-rtl slot="start" aria-hidden="true" class="expandable-status-icon"
[class.expandable-status-icon-expanded]="individual.expanded">
</ion-icon>
@ -147,7 +147,7 @@
<!-- Template to render a list of conversations. -->
<ng-template #conversationsTemplate let-conversations="conversations">
<ion-item class="ion-text-wrap addon-message-discussion" *ngFor="let conversation of conversations" button detail="false"
<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'"

View File

@ -13,7 +13,7 @@
// limitations under the License.
import { Component, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/core';
import { IonContent, IonRefresher } from '@ionic/angular';
import { IonContent } from '@ionic/angular';
import { CoreEventObserver, CoreEvents } from '@singletons/events';
import { CoreSites } from '@services/sites';
import {
@ -719,7 +719,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
* @param refreshUnreadCounts Whether to refresh unread counts.
* @returns Promise resolved when done.
*/
async refreshData(refresher?: IonRefresher, refreshUnreadCounts: boolean = true): Promise<void> {
async refreshData(refresher?: HTMLIonRefresherElement, refreshUnreadCounts: boolean = true): Promise<void> {
// Don't invalidate conversations and so, they always try to get latest data.
try {
await AddonMessages.invalidateContactRequestsCountCache(this.siteId);

View File

@ -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">
@ -68,7 +68,7 @@
</ion-item>
<!-- Load more button for contacts and non-contacts -->
<ng-container *ngIf="item.type != 'messages'">
<ng-container *ngIf="item.type !== 'messages'">
<div class="ion-padding-horizontal" *ngIf="item.canLoadMore && !item.loadingMore">
<ion-button expand="block" fill="outline" (click)="search(query, item.type)">
{{ 'core.loadmore' | translate }}

View File

@ -25,7 +25,6 @@ import { CoreEvents } from '@singletons/events';
import { CoreSites } from '@services/sites';
import { CoreDomUtils } from '@services/utils/dom';
import { CoreConstants } from '@/core/constants';
import { IonRefresher } from '@ionic/angular';
import { AddonNotificationsPreferencesNotificationProcessorState } from '@addons/notifications/services/notifications';
import { CorePlatform } from '@services/platform';
@ -251,7 +250,7 @@ export class AddonMessagesSettingsPage implements OnInit, OnDestroy {
*
* @param refresher Refresher.
*/
refreshPreferences(refresher?: IonRefresher): void {
refreshPreferences(refresher?: HTMLIonRefresherElement): void {
AddonMessages.invalidateMessagePreferences().finally(() => {
this.fetchPreferences().finally(() => {
refresher?.complete();

View File

@ -46,7 +46,8 @@
</ion-item>
<!-- Summary of all submissions. -->
<ion-item class="ion-text-wrap" *ngIf="summary && summary.participantcount" (click)="goToSubmissionList()" detail="true" button>
<ion-item class="ion-text-wrap" *ngIf="summary && summary.participantcount" (click)="goToSubmissionList()" [detail]="true"
button>
<ion-label>
<p class="item-heading" *ngIf="assign.teamsubmission">{{ 'addon.mod_assign.numberofteams' | translate }}</p>
<p class="item-heading" *ngIf="!assign.teamsubmission">{{ 'addon.mod_assign.numberofparticipants' | translate }}</p>

View File

@ -54,7 +54,7 @@ import { AddonModAssignSubmissionComponent } from '../submission/submission';
})
export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityComponent implements OnInit, OnDestroy {
@ViewChild(AddonModAssignSubmissionComponent) submissionComponent?: AddonModAssignSubmissionComponent;
@ViewChild(AddonModAssignSubmissionComponent) submissionComponent?: AddonModAssignSubmissionComponent;
component = AddonModAssignProvider.COMPONENT;
pluginName = 'assign';

View File

@ -264,7 +264,7 @@
<ng-template>
<!-- Current grade if method is advanced. -->
<ion-item class="ion-text-wrap core-grading-summary"
*ngIf="feedback?.gradefordisplay && (!isGrading || grade.method != 'simple')">
*ngIf="feedback?.gradefordisplay && (!isGrading || grade.method !== 'simple')">
<ion-label>
<p class="item-heading">{{ 'addon.mod_assign.currentgrade' | translate }}</p>
<p>
@ -280,7 +280,7 @@
<ng-container *ngIf="isGrading">
<!-- Numeric grade.
Use a text input because otherwise we cannot readthe value if it has an invalid character. -->
<ion-item class="ion-text-wrap" *ngIf="grade.method == 'simple' && !grade.scale">
<ion-item class="ion-text-wrap" *ngIf="grade.method === 'simple' && !grade.scale">
<ion-label position="stacked">
<p class="item-heading">{{ 'addon.mod_assign.gradeoutof' | translate: {$a: gradeInfo!.grade} }}</p>
</ion-label>
@ -291,7 +291,7 @@
</ion-item>
<!-- Grade using a scale. -->
<ion-item class="ion-text-wrap" *ngIf="grade.method == 'simple' && grade.scale">
<ion-item class="ion-text-wrap" *ngIf="grade.method === 'simple' && grade.scale">
<ion-label>
<p class="item-heading">{{ 'addon.mod_assign.grade' | translate }}</p>
</ion-label>
@ -323,13 +323,13 @@
<ion-item class="ion-text-wrap" *ngIf="gradeInfo && grade.unreleasedGrade !== undefined">
<ion-label>
<p class="item-heading">{{ 'addon.mod_assign.currentassigngrade' | translate }}</p>
<p *ngIf="grade.method != 'simple' || !grade.scale">{{ grade.unreleasedGrade}} / {{ gradeInfo.grade }}</p>
<p *ngIf="grade.method == 'simple' && grade.scale">{{ grade.unreleasedGrade }}</p>
<p *ngIf="grade.method !== 'simple' || !grade.scale">{{ grade.unreleasedGrade}} / {{ gradeInfo.grade }}</p>
<p *ngIf="grade.method === 'simple' && grade.scale">{{ grade.unreleasedGrade }}</p>
</ion-label>
</ion-item>
<!-- Gradebook grade for simple grading. -->
<ion-item class="ion-text-wrap" *ngIf="grade.method == 'simple'">
<ion-item class="ion-text-wrap" *ngIf="grade.method === 'simple'">
<ion-label>
<p class="item-heading">{{ 'addon.mod_assign.currentgrade' | translate }}</p>
<p *ngIf="grade.gradebookGrade && !grade.scale">
@ -393,7 +393,7 @@
<!-- Data about the grader (teacher who graded). -->
<ion-item class="ion-text-wrap" *ngIf="grader" core-user-link [userId]="grader!.id" [courseId]="courseId"
[attr.aria-label]="grader!.fullname" detail="true">
[attr.aria-label]="grader!.fullname" [detail]="true">
<core-user-avatar [user]="grader" slot="start" [linkProfile]="false"></core-user-avatar>
<ion-label>
<p class="item-heading">{{ 'addon.mod_assign.gradedby' | translate }}</p>

View File

@ -5,7 +5,7 @@
</ion-buttons>
<ion-title>
<h1>
<core-format-text [text]="title" contextLevel="module" [contextInstanceId]="module?.id" [courseId]="courseId">
<core-format-text [text]="title" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
</core-format-text>
</h1>
</ion-title>

View File

@ -28,7 +28,7 @@
<!-- List of submissions. -->
<ng-container *ngFor="let submission of submissions.items">
<ion-item class="ion-text-wrap" (click)="submissions.select(submission)" button
[attr.aria-current]="submissions.getItemAriaCurrent(submission)" detail="true">
[attr.aria-current]="submissions.getItemAriaCurrent(submission)" [detail]="true">
<core-user-avatar [user]="submission" [linkProfile]="false" slot="start"></core-user-avatar>
<ion-label>
<p class="item-heading" *ngIf="submission.userfullname">{{submission.userfullname}}</p>

View File

@ -16,7 +16,6 @@ import { Component, OnDestroy, AfterViewInit, ViewChild } from '@angular/core';
import { CoreListItemsManager } from '@classes/items-management/list-items-manager';
import { CoreRoutedItemsManagerSourcesTracker } from '@classes/items-management/routed-items-manager-sources-tracker';
import { CoreSplitViewComponent } from '@components/split-view/split-view';
import { IonRefresher } from '@ionic/angular';
import { CoreGroupInfo } from '@services/groups';
import { CoreNavigator } from '@services/navigator';
import { CoreSites } from '@services/sites';
@ -208,7 +207,7 @@ export class AddonModAssignSubmissionListPage implements AfterViewInit, OnDestro
*
* @param refresher Refresher.
*/
refreshList(refresher?: IonRefresher): void {
refreshList(refresher?: HTMLIonRefresherElement): void {
this.refreshAllData(true).finally(() => {
refresher?.complete();
});

View File

@ -18,7 +18,6 @@ import { CoreRoutedItemsManagerSourcesTracker } from '@classes/items-management/
import { CoreSwipeNavigationItemsManager } from '@classes/items-management/swipe-navigation-items-manager';
import { CoreCourse } from '@features/course/services/course';
import { CanLeave } from '@guards/can-leave';
import { IonRefresher } from '@ionic/angular';
import { CoreNavigator } from '@services/navigator';
import { CoreScreen } from '@services/screen';
import { CoreDomUtils } from '@services/utils/dom';
@ -210,7 +209,7 @@ export class AddonModAssignSubmissionReviewPage implements OnInit, OnDestroy, Ca
*
* @param refresher Refresher.
*/
refreshSubmission(refresher?: IonRefresher): void {
refreshSubmission(refresher?: HTMLIonRefresherElement): void {
this.refreshAllData().finally(() => {
refresher?.complete();
});

View File

@ -28,6 +28,7 @@ import {
SUBMISSIONS_GRADES_TABLE,
SUBMISSIONS_TABLE,
} from './database/assign';
import { CoreArray } from '@singletons/array';
/**
* Service to handle offline assign.
@ -86,8 +87,8 @@ export class AddonModAssignOfflineProvider {
const results = await Promise.all(promises);
// Flatten array.
const flatten: (AddonModAssignSubmissionsDBRecord | AddonModAssignSubmissionsGradingDBRecord)[] =
[].concat.apply([], results);
const flatten = CoreArray
.flatten<AddonModAssignSubmissionsDBRecordFormatted | AddonModAssignSubmissionsGradingDBRecordFormatted>(results);
// Get assign id.
let assignIds: number[] = flatten.map((assign) => assign.assignid);

View File

@ -166,7 +166,7 @@ export class AddonModAssignPrefetchHandlerService extends CoreCourseActivityPref
// Get intro and activity files from the submission status if it's a student.
// It's ok if they were already obtained from the assignment instance, they won't be downloaded twice.
const files = canViewAllSubmissions ?
const files: CoreWSFile[] = canViewAllSubmissions ?
[] :
(submissionStatus.assignmentdata?.attachments?.intro || [])
.concat(submissionStatus.assignmentdata?.attachments?.activity || []);

View File

@ -1,4 +1,4 @@
<ion-item *ngIf="commentsEnabled" class="ion-text-wrap" (click)="showComments($event)" detail="true" button>
<ion-item *ngIf="commentsEnabled" class="ion-text-wrap" (click)="showComments($event)" [detail]="true" button>
<ion-label>
<h2>{{plugin.name}}</h2>
<core-comments contextLevel="module" [instanceId]="assign.cmid" component="assignsubmission_comments" [itemId]="submission.id"

View File

@ -10,7 +10,7 @@
<!-- Edit -->
<div *ngIf="edit">
<ion-item-divider class="ion-text-wrap" sticky="true">
<ion-item-divider class="ion-text-wrap" [sticky]="true">
<ion-label>
<h2>{{ plugin.name }}</h2>
</ion-label>

View File

@ -13,7 +13,7 @@
<!-- Edit -->
<div *ngIf="edit && loaded">
<ion-item-divider class="ion-text-wrap" sticky="true">
<ion-item-divider class="ion-text-wrap" [sticky]="true">
<ion-label>
<h2>{{ plugin.name }}</h2>
</ion-label>

View File

@ -92,7 +92,7 @@
</ion-item>
<ng-container *ngFor="let recording of recordings">
<ion-item button class="addon-mod_bbb-recording-title" [attr.aria-expanded]="recording.expanded" (click)="toggle(recording)"
[attr.aria-label]="(recording.expanded ? 'core.collapse' : 'core.expand') | translate" detail="false">
[attr.aria-label]="(recording.expanded ? 'core.collapse' : 'core.expand') | translate" [detail]="false">
<ion-icon name="fas-chevron-right" flip-rtl slot="start" aria-hidden="true" class="expandable-status-icon"
[class.expandable-status-icon-expanded]="recording.expanded">
</ion-icon>
@ -108,7 +108,7 @@
<p class="item-heading">{{ recording.playbackLabel }}</p>
</ion-label>
</ion-item>
<ion-item *ngFor="let playback of recording.playbacks" button (click)="openPlayback($event, playback)" detail="false"
<ion-item *ngFor="let playback of recording.playbacks" button (click)="openPlayback($event, playback)" [detail]="false"
class="ion-text-wrap addon-mod_bbb-recording-playback-item">
<ion-label>
<p>{{ playback.name }}</p>

View File

@ -5,7 +5,7 @@
</ion-buttons>
<ion-title>
<h1>
<core-format-text [text]="title" contextLevel="module" [contextInstanceId]="module?.id" [courseId]="courseId">
<core-format-text [text]="title" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
</core-format-text>
</h1>
</ion-title>

View File

@ -20,7 +20,7 @@
</ion-label>
</ion-item>
<ion-item class="ion-text-wrap" *ngFor="let chapter of chapters" [class.item-dimmed]="chapter.hidden" button detail="true"
<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">

View File

@ -14,7 +14,7 @@
<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">
<span *ngIf="showNumbers" class="addon-mod-book-number">{{chapter.indexNumber}} </span>

View File

@ -24,7 +24,6 @@ import { CoreCourse } from '@features/course/services/course';
import { CoreCourseModuleData } from '@features/course/services/course-helper';
import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate';
import { CoreTag, CoreTagItem } from '@features/tag/services/tag';
import { IonRefresher } from '@ionic/angular';
import { CoreNetwork } from '@services/network';
import { CoreNavigator } from '@services/navigator';
import { CoreDomUtils } from '@services/utils/dom';
@ -232,7 +231,7 @@ export class AddonModBookContentsPage implements OnInit, OnDestroy {
* @param refresher Refresher.
* @returns Promise resolved when done.
*/
async doRefresh(refresher?: IonRefresher): Promise<void> {
async doRefresh(refresher?: HTMLIonRefresherElement): Promise<void> {
if (this.manager) {
await CoreUtils.ignoreErrors(Promise.all([
this.manager.getSource().invalidateContent(),

View File

@ -5,7 +5,7 @@
</ion-buttons>
<ion-title>
<h1>
<core-format-text [text]="title" contextLevel="module" [contextInstanceId]="module?.id" [courseId]="courseId">
<core-format-text [text]="title" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
</core-format-text>
</h1>
</ion-title>

View File

@ -26,7 +26,7 @@
</p>
<div class="ion-text-center addon-mod_chat-notice" *ngIf="message.special">
<ion-badge class="ion-text-wrap" color="success" *ngIf="message.system && message.message == 'enter'">
<ion-badge class="ion-text-wrap" color="success" *ngIf="message.system && message.message === 'enter'">
<span>
<ion-icon name="fas-right-to-bracket" aria-hidden="true"></ion-icon>
{{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }}
@ -34,7 +34,7 @@
</span>
</ion-badge>
<ion-badge class="ion-text-wrap" color="danger" *ngIf="message.system && message.message == 'exit'">
<ion-badge class="ion-text-wrap" color="danger" *ngIf="message.system && message.message === 'exit'">
<span>
<ion-icon name="fas-right-from-bracket" aria-hidden="true"></ion-icon>
{{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }}
@ -42,7 +42,7 @@
</span>
</ion-badge>
<ion-badge class="ion-text-wrap" color="primary" *ngIf="message.beep == 'all'">
<ion-badge class="ion-text-wrap" color="primary" *ngIf="message.beep === 'all'">
<span>
<ion-icon name="fas-bell" aria-hidden="true"></ion-icon>
{{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }}
@ -60,7 +60,7 @@
</ion-badge>
<ion-badge class="ion-text-wrap" color="light"
*ngIf="message.userid == currentUserId && message.beep && message.beep != 'all'">
*ngIf="message.userid === currentUserId && message.beep && message.beep !== 'all'">
<span>
<ion-icon name="fas-bell" aria-hidden="true"></ion-icon>
{{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }}

View File

@ -1,5 +1,3 @@
@import "~theme/components/discussion.scss";
:host {
.addon-mod_chat-notice {
margin-top: 8px;

View File

@ -37,7 +37,7 @@ import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics';
@Component({
selector: 'page-addon-mod-chat-chat',
templateUrl: 'chat.html',
styleUrls: ['chat.scss'],
styleUrls: ['../../../../../theme/components/discussion.scss', 'chat.scss'],
})
export class AddonModChatChatPage implements OnInit, OnDestroy, CanLeave {

View File

@ -5,7 +5,7 @@
</ion-buttons>
<ion-title>
<h1>
<core-format-text [text]="title" contextLevel="module" [contextInstanceId]="module?.id" [courseId]="courseId">
<core-format-text [text]="title" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
</core-format-text>
</h1>
</ion-title>

View File

@ -21,7 +21,7 @@
</div>
<div class="ion-text-center addon-mod_chat-notice" *ngIf="message.special">
<ion-badge class="ion-text-wrap" color="success" *ngIf="message.issystem && message.message == 'enter'">
<ion-badge class="ion-text-wrap" color="success" *ngIf="message.issystem && message.message === 'enter'">
<span>
<ion-icon name="fas-right-to-bracket" aria-hidden="true"></ion-icon>
{{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }}
@ -29,7 +29,7 @@
</span>
</ion-badge>
<ion-badge class="ion-text-wrap" color="danger" *ngIf="message.issystem && message.message == 'exit'">
<ion-badge class="ion-text-wrap" color="danger" *ngIf="message.issystem && message.message === 'exit'">
<span>
<ion-icon name="fas-right-from-bracket" aria-hidden="true"></ion-icon>
{{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }}
@ -37,7 +37,7 @@
</span>
</ion-badge>
<ion-badge class="ion-text-wrap" color="primary" *ngIf="message.beep == 'all'">
<ion-badge class="ion-text-wrap" color="primary" *ngIf="message.beep === 'all'">
<span>
<ion-icon name="fas-bell" aria-hidden="true"></ion-icon>
{{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }}
@ -55,7 +55,7 @@
</ion-badge>
<ion-badge class="ion-text-wrap" color="light"
*ngIf="message.userid == currentUserId && message.beep && message.beep != 'all'">
*ngIf="message.userid === currentUserId && message.beep && message.beep !== 'all'">
<span>
<ion-icon name="fas-bell" aria-hidden="true"></ion-icon>
{{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }}

View File

@ -1,5 +1,3 @@
@import "~theme/components/discussion.scss";
:host {
.addon-mod_chat-notice {
margin-top: 8px;

View File

@ -14,7 +14,6 @@
import { Component, OnInit } from '@angular/core';
import { CoreUser } from '@features/user/services/user';
import { IonRefresher } from '@ionic/angular';
import { CoreNavigator } from '@services/navigator';
import { CoreSites } from '@services/sites';
import { CoreDomUtils } from '@services/utils/dom';
@ -31,7 +30,7 @@ import { CoreTime } from '@singletons/time';
@Component({
selector: 'page-addon-mod-chat-session-messages',
templateUrl: 'session-messages.html',
styleUrls: ['session-messages.scss'],
styleUrls: ['../../../../../theme/components/discussion.scss', 'session-messages.scss'],
})
export class AddonModChatSessionMessagesPage implements OnInit {
@ -158,7 +157,7 @@ export class AddonModChatSessionMessagesPage implements OnInit {
*
* @param refresher Refresher.
*/
async refreshMessages(refresher: IonRefresher): Promise<void> {
async refreshMessages(refresher: HTMLIonRefresherElement): Promise<void> {
try {
await CoreUtils.ignoreErrors(AddonModChat.invalidateSessionMessages(this.chatId, this.sessionStart, this.groupId));

View File

@ -16,7 +16,6 @@ import { AfterViewInit, Component, OnDestroy, OnInit, ViewChild } from '@angular
import { CoreListItemsManager } from '@classes/items-management/list-items-manager';
import { CoreRoutedItemsManagerSourcesTracker } from '@classes/items-management/routed-items-manager-sources-tracker';
import { CoreSplitViewComponent } from '@components/split-view/split-view';
import { IonRefresher } from '@ionic/angular';
import { CoreGroupInfo } from '@services/groups';
import { CoreNavigator } from '@services/navigator';
import { CoreDomUtils } from '@services/utils/dom';
@ -143,7 +142,7 @@ export class AddonModChatSessionsPage implements OnInit, AfterViewInit, OnDestro
*
* @param refresher Refresher.
*/
async refreshSessions(refresher: IonRefresher): Promise<void> {
async refreshSessions(refresher: HTMLIonRefresherElement): Promise<void> {
try {
this.sessions.getSource().setDirty(true);

View File

@ -89,7 +89,7 @@
<ion-col *ngIf="choice.publish && results" size="12" size-lg="7">
<ion-item-group *ngFor="let result of results">
<ion-item [button]="result.numberofuser > 0" class="divider ion-text-wrap" (click)="toggle(result)"
[attr.aria-label]="(result.expanded ? 'core.collapse' : 'core.expand') | translate" detail="false">
[attr.aria-label]="(result.expanded ? 'core.collapse' : 'core.expand') | translate" [detail]="false">
<ion-icon [name]="result.numberofuser > 0 ? 'fas-chevron-right' : ''" flip-rtl slot="start" aria-hidden="true"
class="expandable-status-icon" [class.expandable-status-icon-expanded]="result.expanded">
</ion-icon>

View File

@ -301,7 +301,7 @@ export class AddonModChoiceIndexComponent extends CoreCourseModuleMainActivityCo
this.data = [];
this.labels = [];
this.results = results.map((result: AddonModChoiceResultFormatted) => {
this.results = results.map<AddonModChoiceResultFormatted>((result) => {
if (result.numberofuser > 0) {
hasVotes = true;
}

View File

@ -5,7 +5,7 @@
</ion-buttons>
<ion-title>
<h1>
<core-format-text [text]="title" contextLevel="module" [contextInstanceId]="module?.id" [courseId]="courseId">
<core-format-text [text]="title" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
</core-format-text>
</h1>
</ion-title>

View File

@ -1,49 +1,49 @@
<ion-button size="small" *ngIf="action == 'actionsmenu'" fill="clear" (click)="actionsMenu($event)"
<ion-button size="small" *ngIf="action === 'actionsmenu'" fill="clear" (click)="actionsMenu($event)"
[attr.aria-label]="'addon.mod_data.actionsmenu' | translate">
<ion-icon name="fas-ellipsis-vertical" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button>
<ion-button size="small" *ngIf="action == 'more'" fill="clear" (click)="viewEntry()"
<ion-button size="small" *ngIf="action === 'more'" fill="clear" (click)="viewEntry()"
[attr.aria-label]="'addon.mod_data.showmore' | translate">
<ion-icon name="fas-magnifying-glass-plus" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button>
<ion-button size="small" *ngIf="action == 'edit'" fill="clear" (click)="editEntry()" [attr.aria-label]="'core.edit' | translate">
<ion-button size="small" *ngIf="action === 'edit'" fill="clear" (click)="editEntry()" [attr.aria-label]="'core.edit' | translate">
<ion-icon name="fas-pen" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button>
<ion-button size="small" *ngIf="action == 'delete' && !entry.deleted" fill="clear" color="danger" (click)="deleteEntry()"
<ion-button size="small" *ngIf="action === 'delete' && !entry.deleted" fill="clear" color="danger" (click)="deleteEntry()"
[attr.aria-label]="'core.delete' | translate">
<ion-icon name="fas-trash" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button>
<ion-button size="small" *ngIf="action == 'delete' && entry.deleted" fill="clear" color="danger" (click)="undoDelete()"
<ion-button size="small" *ngIf="action === 'delete' && entry.deleted" fill="clear" color="danger" (click)="undoDelete()"
[attr.aria-label]="'core.restore' | translate">
<ion-icon name="fas-rotate-left" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button>
<ion-button size="small" *ngIf="action == 'approve'" fill="clear" (click)="approveEntry()"
<ion-button size="small" *ngIf="action === 'approve'" fill="clear" (click)="approveEntry()"
[attr.aria-label]="'addon.mod_data.approve' | translate">
<ion-icon name="fas-thumbs-up" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button>
<ion-button size="small" *ngIf="action == 'disapprove'" fill="clear" (click)="disapproveEntry()"
<ion-button size="small" *ngIf="action === 'disapprove'" fill="clear" (click)="disapproveEntry()"
[attr.aria-label]="'addon.mod_data.disapprove' | translate">
<ion-icon name="far-thumbs-down" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button>
<core-comments *ngIf="action == 'comments' && mode == 'list'" contextLevel="module" [instanceId]="database.coursemodule"
<core-comments *ngIf="action === 'comments' && mode === 'list'" contextLevel="module" [instanceId]="database.coursemodule"
component="mod_data" [itemId]="entry.id" area="database_entry" [courseId]="database.course">
</core-comments>
<span *ngIf="action == 'timeadded'">{{ entry.timecreated * 1000 | coreFormatDate }}</span>
<span *ngIf="action == 'timemodified'">{{ entry.timemodified * 1000 | coreFormatDate }}</span>
<span *ngIf="action === 'timeadded'">{{ entry.timecreated * 1000 | coreFormatDate }}</span>
<span *ngIf="action === 'timemodified'">{{ entry.timemodified * 1000 | coreFormatDate }}</span>
<core-user-avatar *ngIf="action == 'userpicture'" [user]="entry" slot="start" [courseId]="database.course" [userId]="entry.userid"
<core-user-avatar *ngIf="action === 'userpicture'" [user]="entry" slot="start" [courseId]="database.course" [userId]="entry.userid"
[profileUrl]="userPicture"></core-user-avatar>
<a *ngIf="action == 'user' && entry" core-user-link [courseId]="database.course" [userId]="entry.userid" [title]="entry.fullname">
<a *ngIf="action === 'user' && entry" core-user-link [courseId]="database.course" [userId]="entry.userid" [title]="entry.fullname">
{{entry.fullname}}
</a>
<core-tag-list *ngIf="tagsEnabled && action == 'tags' && entry" [tags]="entry.tags"></core-tag-list>
<core-tag-list *ngIf="tagsEnabled && action === 'tags' && entry" [tags]="entry.tags"></core-tag-list>

View File

@ -1,6 +1,6 @@
<ion-content>
<ion-list>
<ion-item button class="ion-text-wrap" (click)="onItemClick(item)" *ngFor="let item of items" detail="false"
<ion-item button class="ion-text-wrap" (click)="onItemClick(item)" *ngFor="let item of items" [detail]="false"
[attr.aria-label]="item.text | translate">
<ion-label>
<p class="item-heading">{{ item.text | translate }}</p>

View File

@ -63,14 +63,14 @@
<!-- Reset search. -->
<ng-container *ngIf="search.searching && !isEmpty">
<ion-item (click)="searchReset($event)" button detail="false" *ngIf="!foundRecordsTranslationData">
<ion-item (click)="searchReset($event)" button [detail]="false" *ngIf="!foundRecordsTranslationData">
<ion-label color="info">
{{ 'addon.mod_data.resetsettings' | translate}}
</ion-label>
</ion-item>
<ion-card class="core-success-card" *ngIf="foundRecordsTranslationData">
<ion-item (click)="searchReset($event)" button detail="false">
<ion-item (click)="searchReset($event)" button [detail]="false">
<ion-label>
<p [innerHTML]="'addon.mod_data.foundrecords' | translate:{$a: foundRecordsTranslationData}"></p>
</ion-label>

Some files were not shown because too many files have changed in this diff Show More