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 *.tmp
*.tmp.* *.tmp.*
*.sublime-project
*.sublime-workspace
.DS_Store
Thumbs.db
UserInterfaceState.xcuserstate UserInterfaceState.xcuserstate
$RECYCLE.BIN/ $RECYCLE.BIN/
*.log *.log
log.txt log.txt
npm-debug.log*
/.idea
/.ionic
/.sass-cache
/.sourcemaps /.sourcemaps
/.versions /.versions
/coverage /coverage
/dist
/node_modules # Ionic
/.ionic
/www
/platforms /platforms
/plugins /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/lib
/src/assets/lang/*
/src/assets/env.json
/moodle.config.*.json /moodle.config.*.json
!/moodle.config.example.json !/moodle.config.example.json
/src/assets/lang/*
/src/assets/env.json

View File

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

View File

@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?> <?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> <name>Moodle</name>
<description>Moodle official app</description> <description>Moodle official app</description>
<author email="mobile@moodle.com" href="http://moodle.com">Moodle Mobile team</author> <author email="mobile@moodle.com" href="http://moodle.com">Moodle Mobile team</author>
@ -28,7 +28,7 @@
<preference name="UIWebViewBounce" value="false" /> <preference name="UIWebViewBounce" value="false" />
<preference name="DisallowOverscroll" value="true" /> <preference name="DisallowOverscroll" value="true" />
<preference name="prerendered-icon" 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="BackupWebStorage" value="none" />
<preference name="ScrollEnabled" value="false" /> <preference name="ScrollEnabled" value="false" />
<preference name="KeyboardDisplayRequiresUserAction" value="false" /> <preference name="KeyboardDisplayRequiresUserAction" value="false" />
@ -220,7 +220,7 @@
<true /> <true />
</edit-config> </edit-config>
<edit-config file="*-Info.plist" mode="merge" target="CFBundleShortVersionString"> <edit-config file="*-Info.plist" mode="merge" target="CFBundleShortVersionString">
<string>4.3.0</string> <string>4.4.0</string>
</edit-config> </edit-config>
<edit-config file="*-Info.plist" mode="overwrite" target="CFBundleLocalizations"> <edit-config file="*-Info.plist" mode="overwrite" target="CFBundleLocalizations">
<array> <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", "app_id": "com.moodle.moodlemobile",
"appname": "Moodle Mobile", "appname": "Moodle Mobile",
"versioncode": 43001, "versioncode": 44000,
"versionname": "4.3.0", "versionname": "4.4.0",
"cache_update_frequency_usually": 420000, "cache_update_frequency_usually": 420000,
"cache_update_frequency_often": 1200000, "cache_update_frequency_often": 1200000,
"cache_update_frequency_sometimes": 3600000, "cache_update_frequency_sometimes": 3600000,

2
package-lock.json generated
View File

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

View File

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

View File

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

View File

@ -19,7 +19,7 @@
<ion-list *ngIf="!badges.empty" class="ion-no-margin"> <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" <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"> <ion-avatar slot="start">
<img [src]="badge.badgeurl" [alt]="badge.name" core-external-content> <img [src]="badge.badgeurl" [alt]="badge.name" core-external-content>
</ion-avatar> </ion-avatar>

View File

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

View File

@ -1,10 +1,10 @@
<ion-item-divider sticky="true"> <ion-item-divider [sticky]="true">
<ion-label> <ion-label>
<h2>{{ 'addon.block_activitymodules.pluginname' | translate }}</h2> <h2>{{ 'addon.block_activitymodules.pluginname' | translate }}</h2>
</ion-label> </ion-label>
</ion-item-divider> </ion-item-divider>
<core-loading [hideUntil]="loaded"> <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 slot="start" [modicon]="entry.icon" [modname]="entry.iconModName" [showAlt]="false">
</core-mod-icon> </core-mod-icon>
<ion-label>{{ entry.name }}</ion-label> <ion-label>{{ entry.name }}</ion-label>

View File

@ -1,4 +1,4 @@
<ion-item-divider sticky="true"> <ion-item-divider [sticky]="true">
<ion-label> <ion-label>
<h2>{{ 'addon.block_myoverview.pluginname' | translate }}</h2> <h2>{{ 'addon.block_myoverview.pluginname' | translate }}</h2>
</ion-label> </ion-label>
@ -88,11 +88,11 @@
</core-combobox> </core-combobox>
</ion-col> </ion-col>
<ion-col size="auto" *ngIf="isLayoutSwitcherAvailable"> <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"> [attr.aria-label]="'addon.block_myoverview.aria:list' | translate">
<ion-icon slot="icon-only" name="fas-list" aria-hidden="true"></ion-icon> <ion-icon slot="icon-only" name="fas-list" aria-hidden="true"></ion-icon>
</ion-button> </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"> [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-icon slot="icon-only" name="fas-table-cells-large" aria-hidden="true"></ion-icon>
</ion-button> </ion-button>
@ -123,7 +123,7 @@
<!-- List of courses. --> <!-- List of courses. -->
<div class="safe-area-padding" *ngIf="hasCourses"> <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-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" <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"> size-xl="3">

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
<ion-item-divider sticky="true"> <ion-item-divider [sticky]="true">
<ion-label> <ion-label>
<h2>{{ 'addon.block_recentlyaccesseditems.pluginname' | translate }}</h2> <h2>{{ 'addon.block_recentlyaccesseditems.pluginname' | translate }}</h2>
</ion-label> </ion-label>
@ -14,7 +14,7 @@
<div class="safe-area-pseudo-padding-start"></div> <div class="safe-area-pseudo-padding-start"></div>
<div *ngFor="let item of items" class="core-horizontal-scroll-item"> <div *ngFor="let item of items" class="core-horizontal-scroll-item">
<ion-card> <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" <core-mod-icon slot="start" *ngIf="item.iconUrl" [modicon]="item.iconUrl" [modname]="item.modname"
[componentId]="item.cmid" [showAlt]="false" [purpose]="item.purpose"> [componentId]="item.cmid" [showAlt]="false" [purpose]="item.purpose">
</core-mod-icon> </core-mod-icon>

View File

@ -1,4 +1,4 @@
@import "~theme/globals"; @use "theme/globals" as *;
:host { :host {
.core-horizontal-scroll div.core-horizontal-scroll-item { .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> <ion-label>
<h2>{{ 'addon.block_sitemainmenu.pluginname' | translate }}</h2> <h2>{{ 'addon.block_sitemainmenu.pluginname' | translate }}</h2>
</ion-label> </ion-label>

View File

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

View File

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

View File

@ -14,7 +14,7 @@
</ion-label> </ion-label>
</ion-item> </ion-item>
<ng-container *ngFor="let event of dayEvents.events"> <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"> button lines="full">
<ion-label> <ion-label>
<ion-row class="ion-justify-content-between ion-align-items-center ion-no-padding"> <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 { h3 {
font-weight: bold; font-weight: bold;

View File

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

View File

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

View File

@ -65,7 +65,7 @@
<core-file *ngFor="let file of entry.attachmentfiles" [file]="file" [component]="this.component" <core-file *ngFor="let file of entry.attachmentfiles" [file]="file" [component]="this.component"
[componentId]="entry.id"> [componentId]="entry.id">
</core-file> </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-label>{{ 'addon.blog.linktooriginalentry' | translate }}</ion-label>
</ion-item> </ion-item>
</ion-card-content> </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 { CoreMainMenuDeepLinkManager } from '@features/mainmenu/classes/deep-link-manager';
import { CoreTag } from '@features/tag/services/tag'; import { CoreTag } from '@features/tag/services/tag';
import { CoreUser, CoreUserProfile } from '@features/user/services/user'; import { CoreUser, CoreUserProfile } from '@features/user/services/user';
import { IonRefresher } from '@ionic/angular';
import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics'; import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics';
import { CoreNavigator } from '@services/navigator'; import { CoreNavigator } from '@services/navigator';
import { CoreSites } from '@services/sites'; import { CoreSites } from '@services/sites';
@ -272,7 +271,7 @@ export class AddonBlogEntriesPage implements OnInit {
* *
* @param refresher Refresher instance. * @param refresher Refresher instance.
*/ */
refresh(refresher?: IonRefresher): void { refresh(refresher?: HTMLIonRefresherElement): void {
const promises = this.entries.map((entry) => const promises = this.entries.map((entry) =>
CoreComments.invalidateCommentsData('user', entry.userid, this.component, entry.id, 'format_blog')); 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 { :host {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -72,7 +72,7 @@
<p>{{ 'addon.calendar.type' + event.formattedType | translate }}</p> <p>{{ 'addon.calendar.type' + event.formattedType | translate }}</p>
</ion-label> </ion-label>
</ion-item> </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> <ion-label>
<p class="item-heading">{{ 'core.course' | translate}}</p> <p class="item-heading">{{ 'core.course' | translate}}</p>
<p> <p>

View File

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

View File

@ -13,7 +13,6 @@
// limitations under the License. // limitations under the License.
import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core'; import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
import { IonRefresher } from '@ionic/angular';
import { CoreNetwork } from '@services/network'; import { CoreNetwork } from '@services/network';
import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreEventObserver, CoreEvents } from '@singletons/events';
import { CoreSites } from '@services/sites'; 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. * @param showErrors Whether to show sync errors to the user.
* @returns Promise resolved when done. * @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) { if (!this.loaded) {
return; return;
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -81,7 +81,7 @@
</ion-label> </ion-label>
</ion-item> </ion-item>
<ion-item class="ion-text-wrap" *ngFor="let competency of competencies.items" (click)="competencies.select(competency)" <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> <ion-label>
<p class="item-heading"> <p class="item-heading">
<core-format-text [text]="competency.competency.shortname" contextLevel="user" <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 { AddonCompetencyDataForPlanPageCompetency, AddonCompetencyDataForPlanPageWSResponse } from '../../services/competency';
import { CoreNavigator } from '@services/navigator'; import { CoreNavigator } from '@services/navigator';
import { CoreUserProfile } from '@features/user/services/user'; import { CoreUserProfile } from '@features/user/services/user';
import { IonRefresher } from '@ionic/angular';
import { CoreSwipeNavigationItemsManager } from '@classes/items-management/swipe-navigation-items-manager'; import { CoreSwipeNavigationItemsManager } from '@classes/items-management/swipe-navigation-items-manager';
import { CoreRoutedItemsManagerSourcesTracker } from '@classes/items-management/routed-items-manager-sources-tracker'; import { CoreRoutedItemsManagerSourcesTracker } from '@classes/items-management/routed-items-manager-sources-tracker';
import { AddonCompetencyPlansSource } from '@addons/competency/classes/competency-plans-source'; import { AddonCompetencyPlansSource } from '@addons/competency/classes/competency-plans-source';
@ -111,7 +110,7 @@ export class AddonCompetencyPlanPage implements OnInit, OnDestroy {
* *
* @param refresher Refresher. * @param refresher Refresher.
*/ */
async refreshLearningPlan(refresher: IonRefresher): Promise<void> { async refreshLearningPlan(refresher: HTMLIonRefresherElement): Promise<void> {
await this.competencies.getSource().invalidateCache(); await this.competencies.getSource().invalidateCache();
this.fetchLearningPlan().finally(() => { this.fetchLearningPlan().finally(() => {

View File

@ -19,7 +19,7 @@
</core-empty-box> </core-empty-box>
<ion-list *ngIf="!plans.empty" class="ion-no-margin"> <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)" <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> <ion-label>
<p class="item-heading"> <p class="item-heading">
<core-format-text [text]="plan.name" contextLevel="user" [contextInstanceId]="plan.userid"> <core-format-text [text]="plan.name" contextLevel="user" [contextInstanceId]="plan.userid">

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -22,10 +22,10 @@
autocorrect="off" spellcheck="false" lengthCheck="2" [disabled]="!loaded" searchArea="AddonMessagesContacts"></core-search-box> autocorrect="off" spellcheck="false" lengthCheck="2" [disabled]="!loaded" searchArea="AddonMessagesContacts"></core-search-box>
<core-loading [hideUntil]="loaded" [message]="loadingMessage"> <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> [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> [message]="'addon.messages.nousersfound' | translate"></core-empty-box>
<ion-list *ngFor="let contactType of contactTypes" class="ion-no-margin"> <ion-list *ngFor="let contactType of contactTypes" class="ion-no-margin">
@ -42,7 +42,7 @@
<!-- Don't show deleted users --> <!-- Don't show deleted users -->
<ion-item class="ion-text-wrap addon-messages-conversation-item" <ion-item class="ion-text-wrap addon-messages-conversation-item"
*ngIf="contact.profileimageurl || contact.profileimageurlsmall" [attr.aria-label]="contact.fullname" *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'"> [attr.aria-current]="contact.id == discussionUserId ? 'page' : 'false'">
<core-user-avatar [user]="contact" slot="start" [checkOnline]="contact.showonlinestatus"></core-user-avatar> <core-user-avatar [user]="contact" slot="start" [checkOnline]="contact.showonlinestatus"></core-user-avatar>
<ion-label> <ion-label>

View File

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

View File

@ -28,7 +28,7 @@
<core-loading [hideUntil]="confirmedLoaded"> <core-loading [hideUntil]="confirmedLoaded">
<ion-list class="ion-no-margin" *ngIf="confirmedContacts.length"> <ion-list class="ion-no-margin" *ngIf="confirmedContacts.length">
<ion-item class="ion-text-wrap addon-messages-conversation-item" (click)="selectUser(contact.id)" button <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'"> [attr.aria-current]="contact.id == selectedUserId ? 'page' : 'false'">
<core-user-avatar slot="start" [user]="contact" [checkOnline]="contact.showonlinestatus" <core-user-avatar slot="start" [user]="contact" [checkOnline]="contact.showonlinestatus"
[linkProfile]="false"> [linkProfile]="false">
@ -67,7 +67,7 @@
<ion-list class="ion-no-margin" *ngIf="requests.length"> <ion-list class="ion-no-margin" *ngIf="requests.length">
<ion-item class="ion-text-wrap addon-messages-conversation-item" *ngFor="let request of requests" <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-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> <core-user-avatar slot="start" [user]="request" [linkProfile]="false"></core-user-avatar>
<ion-label> <ion-label>
<core-format-text [text]="request.fullname" contextLevel="system" [contextInstanceId]="0"> <core-format-text [text]="request.fullname" contextLevel="system" [contextInstanceId]="0">

View File

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

View File

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

View File

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

View File

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

View File

@ -28,7 +28,7 @@
<ion-list class="ion-no-margin"> <ion-list class="ion-no-margin">
<ion-item class="ion-text-wrap addon-message-discussion" (click)="gotoContacts()" <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-icon name="fas-address-book" slot="start" aria-hidden="true"></ion-icon>
<ion-label> <ion-label>
<p class="item-heading">{{ 'addon.messages.contacts' | translate }}</p> <p class="item-heading">{{ 'addon.messages.contacts' | translate }}</p>
@ -46,7 +46,7 @@
</ion-item-divider> </ion-item-divider>
<ion-item class="ion-text-wrap addon-message-discussion" *ngFor="let result of search.results" button <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-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> <core-user-avatar [user]="result" slot="start" [checkOnline]="result.showonlinestatus"></core-user-avatar>
<ion-label> <ion-label>
<p class="item-heading">{{ result.fullname }}</p> <p class="item-heading">{{ result.fullname }}</p>
@ -60,7 +60,7 @@
<ng-container *ngIf="!search.showResults"> <ng-container *ngIf="!search.showResults">
<ion-item class="ion-text-wrap addon-message-discussion" *ngFor="let discussion of discussions" button <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-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> <core-user-avatar [user]="discussion" slot="start" checkOnline="false"></core-user-avatar>
<ion-label> <ion-label>
<div class="flex-row ion-justify-content-between"> <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 { CorePushNotificationsDelegate } from '@features/pushnotifications/services/push-delegate';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { Translate } from '@singletons'; import { Translate } from '@singletons';
import { IonRefresher } from '@ionic/angular';
import { CoreNavigator } from '@services/navigator'; import { CoreNavigator } from '@services/navigator';
import { CoreScreen } from '@services/screen'; import { CoreScreen } from '@services/screen';
import { CoreMainMenuDeepLinkManager } from '@features/mainmenu/classes/deep-link-manager'; 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. * @param refreshUnreadCounts Whteher to refresh unread counts.
* @returns Promise resolved when done. * @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>[] = []; const promises: Promise<void>[] = [];
promises.push(AddonMessages.invalidateDiscussionsCache(this.siteId)); promises.push(AddonMessages.invalidateDiscussionsCache(this.siteId));

View File

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

View File

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

View File

@ -45,7 +45,7 @@
<!-- List of results --> <!-- List of results -->
<ion-item class="addon-message-discussion ion-text-wrap" *ngFor="let result of item.results" [attr.aria-label]="result.fullname" <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> <core-user-avatar slot="start" [user]="result" [checkOnline]="true" [linkProfile]="false"></core-user-avatar>
<ion-label> <ion-label>
<p class="item-heading"> <p class="item-heading">
@ -68,7 +68,7 @@
</ion-item> </ion-item>
<!-- Load more button for contacts and non-contacts --> <!-- 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"> <div class="ion-padding-horizontal" *ngIf="item.canLoadMore && !item.loadingMore">
<ion-button expand="block" fill="outline" (click)="search(query, item.type)"> <ion-button expand="block" fill="outline" (click)="search(query, item.type)">
{{ 'core.loadmore' | translate }} {{ 'core.loadmore' | translate }}

View File

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

View File

@ -46,7 +46,8 @@
</ion-item> </ion-item>
<!-- Summary of all submissions. --> <!-- 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> <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.numberofteams' | translate }}</p>
<p class="item-heading" *ngIf="!assign.teamsubmission">{{ 'addon.mod_assign.numberofparticipants' | 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 { export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityComponent implements OnInit, OnDestroy {
@ViewChild(AddonModAssignSubmissionComponent) submissionComponent?: AddonModAssignSubmissionComponent; @ViewChild(AddonModAssignSubmissionComponent) submissionComponent?: AddonModAssignSubmissionComponent;
component = AddonModAssignProvider.COMPONENT; component = AddonModAssignProvider.COMPONENT;
pluginName = 'assign'; pluginName = 'assign';

View File

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

View File

@ -5,7 +5,7 @@
</ion-buttons> </ion-buttons>
<ion-title> <ion-title>
<h1> <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> </core-format-text>
</h1> </h1>
</ion-title> </ion-title>

View File

@ -28,7 +28,7 @@
<!-- List of submissions. --> <!-- List of submissions. -->
<ng-container *ngFor="let submission of submissions.items"> <ng-container *ngFor="let submission of submissions.items">
<ion-item class="ion-text-wrap" (click)="submissions.select(submission)" button <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> <core-user-avatar [user]="submission" [linkProfile]="false" slot="start"></core-user-avatar>
<ion-label> <ion-label>
<p class="item-heading" *ngIf="submission.userfullname">{{submission.userfullname}}</p> <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 { CoreListItemsManager } from '@classes/items-management/list-items-manager';
import { CoreRoutedItemsManagerSourcesTracker } from '@classes/items-management/routed-items-manager-sources-tracker'; import { CoreRoutedItemsManagerSourcesTracker } from '@classes/items-management/routed-items-manager-sources-tracker';
import { CoreSplitViewComponent } from '@components/split-view/split-view'; import { CoreSplitViewComponent } from '@components/split-view/split-view';
import { IonRefresher } from '@ionic/angular';
import { CoreGroupInfo } from '@services/groups'; import { CoreGroupInfo } from '@services/groups';
import { CoreNavigator } from '@services/navigator'; import { CoreNavigator } from '@services/navigator';
import { CoreSites } from '@services/sites'; import { CoreSites } from '@services/sites';
@ -208,7 +207,7 @@ export class AddonModAssignSubmissionListPage implements AfterViewInit, OnDestro
* *
* @param refresher Refresher. * @param refresher Refresher.
*/ */
refreshList(refresher?: IonRefresher): void { refreshList(refresher?: HTMLIonRefresherElement): void {
this.refreshAllData(true).finally(() => { this.refreshAllData(true).finally(() => {
refresher?.complete(); 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 { CoreSwipeNavigationItemsManager } from '@classes/items-management/swipe-navigation-items-manager';
import { CoreCourse } from '@features/course/services/course'; import { CoreCourse } from '@features/course/services/course';
import { CanLeave } from '@guards/can-leave'; import { CanLeave } from '@guards/can-leave';
import { IonRefresher } from '@ionic/angular';
import { CoreNavigator } from '@services/navigator'; import { CoreNavigator } from '@services/navigator';
import { CoreScreen } from '@services/screen'; import { CoreScreen } from '@services/screen';
import { CoreDomUtils } from '@services/utils/dom'; import { CoreDomUtils } from '@services/utils/dom';
@ -210,7 +209,7 @@ export class AddonModAssignSubmissionReviewPage implements OnInit, OnDestroy, Ca
* *
* @param refresher Refresher. * @param refresher Refresher.
*/ */
refreshSubmission(refresher?: IonRefresher): void { refreshSubmission(refresher?: HTMLIonRefresherElement): void {
this.refreshAllData().finally(() => { this.refreshAllData().finally(() => {
refresher?.complete(); refresher?.complete();
}); });

View File

@ -28,6 +28,7 @@ import {
SUBMISSIONS_GRADES_TABLE, SUBMISSIONS_GRADES_TABLE,
SUBMISSIONS_TABLE, SUBMISSIONS_TABLE,
} from './database/assign'; } from './database/assign';
import { CoreArray } from '@singletons/array';
/** /**
* Service to handle offline assign. * Service to handle offline assign.
@ -86,8 +87,8 @@ export class AddonModAssignOfflineProvider {
const results = await Promise.all(promises); const results = await Promise.all(promises);
// Flatten array. // Flatten array.
const flatten: (AddonModAssignSubmissionsDBRecord | AddonModAssignSubmissionsGradingDBRecord)[] = const flatten = CoreArray
[].concat.apply([], results); .flatten<AddonModAssignSubmissionsDBRecordFormatted | AddonModAssignSubmissionsGradingDBRecordFormatted>(results);
// Get assign id. // Get assign id.
let assignIds: number[] = flatten.map((assign) => assign.assignid); 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. // 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. // 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 || []) (submissionStatus.assignmentdata?.attachments?.intro || [])
.concat(submissionStatus.assignmentdata?.attachments?.activity || []); .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> <ion-label>
<h2>{{plugin.name}}</h2> <h2>{{plugin.name}}</h2>
<core-comments contextLevel="module" [instanceId]="assign.cmid" component="assignsubmission_comments" [itemId]="submission.id" <core-comments contextLevel="module" [instanceId]="assign.cmid" component="assignsubmission_comments" [itemId]="submission.id"

View File

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

View File

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

View File

@ -92,7 +92,7 @@
</ion-item> </ion-item>
<ng-container *ngFor="let recording of recordings"> <ng-container *ngFor="let recording of recordings">
<ion-item button class="addon-mod_bbb-recording-title" [attr.aria-expanded]="recording.expanded" (click)="toggle(recording)" <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" <ion-icon name="fas-chevron-right" flip-rtl slot="start" aria-hidden="true" class="expandable-status-icon"
[class.expandable-status-icon-expanded]="recording.expanded"> [class.expandable-status-icon-expanded]="recording.expanded">
</ion-icon> </ion-icon>
@ -108,7 +108,7 @@
<p class="item-heading">{{ recording.playbackLabel }}</p> <p class="item-heading">{{ recording.playbackLabel }}</p>
</ion-label> </ion-label>
</ion-item> </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"> class="ion-text-wrap addon-mod_bbb-recording-playback-item">
<ion-label> <ion-label>
<p>{{ playback.name }}</p> <p>{{ playback.name }}</p>

View File

@ -5,7 +5,7 @@
</ion-buttons> </ion-buttons>
<ion-title> <ion-title>
<h1> <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> </core-format-text>
</h1> </h1>
</ion-title> </ion-title>

View File

@ -20,7 +20,7 @@
</ion-label> </ion-label>
</ion-item> </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)"> (click)="openBook(chapter.id)">
<ion-label> <ion-label>
<p [class.ion-padding-start]="addPadding && chapter.level == 1 ? true : null"> <p [class.ion-padding-start]="addPadding && chapter.level == 1 ? true : null">

View File

@ -14,7 +14,7 @@
<nav> <nav>
<ion-list> <ion-list>
<ion-item class="ion-text-wrap" *ngFor="let chapter of chapters" (click)="loadChapter(chapter.id)" <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> <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="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 { CoreCourseModuleData } from '@features/course/services/course-helper';
import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate'; import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate';
import { CoreTag, CoreTagItem } from '@features/tag/services/tag'; import { CoreTag, CoreTagItem } from '@features/tag/services/tag';
import { IonRefresher } from '@ionic/angular';
import { CoreNetwork } from '@services/network'; import { CoreNetwork } from '@services/network';
import { CoreNavigator } from '@services/navigator'; import { CoreNavigator } from '@services/navigator';
import { CoreDomUtils } from '@services/utils/dom'; import { CoreDomUtils } from '@services/utils/dom';
@ -232,7 +231,7 @@ export class AddonModBookContentsPage implements OnInit, OnDestroy {
* @param refresher Refresher. * @param refresher Refresher.
* @returns Promise resolved when done. * @returns Promise resolved when done.
*/ */
async doRefresh(refresher?: IonRefresher): Promise<void> { async doRefresh(refresher?: HTMLIonRefresherElement): Promise<void> {
if (this.manager) { if (this.manager) {
await CoreUtils.ignoreErrors(Promise.all([ await CoreUtils.ignoreErrors(Promise.all([
this.manager.getSource().invalidateContent(), this.manager.getSource().invalidateContent(),

View File

@ -5,7 +5,7 @@
</ion-buttons> </ion-buttons>
<ion-title> <ion-title>
<h1> <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> </core-format-text>
</h1> </h1>
</ion-title> </ion-title>

View File

@ -26,7 +26,7 @@
</p> </p>
<div class="ion-text-center addon-mod_chat-notice" *ngIf="message.special"> <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> <span>
<ion-icon name="fas-right-to-bracket" aria-hidden="true"></ion-icon> <ion-icon name="fas-right-to-bracket" aria-hidden="true"></ion-icon>
{{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }}
@ -34,7 +34,7 @@
</span> </span>
</ion-badge> </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> <span>
<ion-icon name="fas-right-from-bracket" aria-hidden="true"></ion-icon> <ion-icon name="fas-right-from-bracket" aria-hidden="true"></ion-icon>
{{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }}
@ -42,7 +42,7 @@
</span> </span>
</ion-badge> </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> <span>
<ion-icon name="fas-bell" aria-hidden="true"></ion-icon> <ion-icon name="fas-bell" aria-hidden="true"></ion-icon>
{{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }}
@ -60,7 +60,7 @@
</ion-badge> </ion-badge>
<ion-badge class="ion-text-wrap" color="light" <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> <span>
<ion-icon name="fas-bell" aria-hidden="true"></ion-icon> <ion-icon name="fas-bell" aria-hidden="true"></ion-icon>
{{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }}

View File

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

View File

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

View File

@ -5,7 +5,7 @@
</ion-buttons> </ion-buttons>
<ion-title> <ion-title>
<h1> <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> </core-format-text>
</h1> </h1>
</ion-title> </ion-title>

View File

@ -21,7 +21,7 @@
</div> </div>
<div class="ion-text-center addon-mod_chat-notice" *ngIf="message.special"> <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> <span>
<ion-icon name="fas-right-to-bracket" aria-hidden="true"></ion-icon> <ion-icon name="fas-right-to-bracket" aria-hidden="true"></ion-icon>
{{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }}
@ -29,7 +29,7 @@
</span> </span>
</ion-badge> </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> <span>
<ion-icon name="fas-right-from-bracket" aria-hidden="true"></ion-icon> <ion-icon name="fas-right-from-bracket" aria-hidden="true"></ion-icon>
{{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }}
@ -37,7 +37,7 @@
</span> </span>
</ion-badge> </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> <span>
<ion-icon name="fas-bell" aria-hidden="true"></ion-icon> <ion-icon name="fas-bell" aria-hidden="true"></ion-icon>
{{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }}
@ -55,7 +55,7 @@
</ion-badge> </ion-badge>
<ion-badge class="ion-text-wrap" color="light" <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> <span>
<ion-icon name="fas-bell" aria-hidden="true"></ion-icon> <ion-icon name="fas-bell" aria-hidden="true"></ion-icon>
{{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }}

View File

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

View File

@ -14,7 +14,6 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { CoreUser } from '@features/user/services/user'; import { CoreUser } from '@features/user/services/user';
import { IonRefresher } from '@ionic/angular';
import { CoreNavigator } from '@services/navigator'; import { CoreNavigator } from '@services/navigator';
import { CoreSites } from '@services/sites'; import { CoreSites } from '@services/sites';
import { CoreDomUtils } from '@services/utils/dom'; import { CoreDomUtils } from '@services/utils/dom';
@ -31,7 +30,7 @@ import { CoreTime } from '@singletons/time';
@Component({ @Component({
selector: 'page-addon-mod-chat-session-messages', selector: 'page-addon-mod-chat-session-messages',
templateUrl: 'session-messages.html', templateUrl: 'session-messages.html',
styleUrls: ['session-messages.scss'], styleUrls: ['../../../../../theme/components/discussion.scss', 'session-messages.scss'],
}) })
export class AddonModChatSessionMessagesPage implements OnInit { export class AddonModChatSessionMessagesPage implements OnInit {
@ -158,7 +157,7 @@ export class AddonModChatSessionMessagesPage implements OnInit {
* *
* @param refresher Refresher. * @param refresher Refresher.
*/ */
async refreshMessages(refresher: IonRefresher): Promise<void> { async refreshMessages(refresher: HTMLIonRefresherElement): Promise<void> {
try { try {
await CoreUtils.ignoreErrors(AddonModChat.invalidateSessionMessages(this.chatId, this.sessionStart, this.groupId)); 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 { CoreListItemsManager } from '@classes/items-management/list-items-manager';
import { CoreRoutedItemsManagerSourcesTracker } from '@classes/items-management/routed-items-manager-sources-tracker'; import { CoreRoutedItemsManagerSourcesTracker } from '@classes/items-management/routed-items-manager-sources-tracker';
import { CoreSplitViewComponent } from '@components/split-view/split-view'; import { CoreSplitViewComponent } from '@components/split-view/split-view';
import { IonRefresher } from '@ionic/angular';
import { CoreGroupInfo } from '@services/groups'; import { CoreGroupInfo } from '@services/groups';
import { CoreNavigator } from '@services/navigator'; import { CoreNavigator } from '@services/navigator';
import { CoreDomUtils } from '@services/utils/dom'; import { CoreDomUtils } from '@services/utils/dom';
@ -143,7 +142,7 @@ export class AddonModChatSessionsPage implements OnInit, AfterViewInit, OnDestro
* *
* @param refresher Refresher. * @param refresher Refresher.
*/ */
async refreshSessions(refresher: IonRefresher): Promise<void> { async refreshSessions(refresher: HTMLIonRefresherElement): Promise<void> {
try { try {
this.sessions.getSource().setDirty(true); this.sessions.getSource().setDirty(true);

View File

@ -89,7 +89,7 @@
<ion-col *ngIf="choice.publish && results" size="12" size-lg="7"> <ion-col *ngIf="choice.publish && results" size="12" size-lg="7">
<ion-item-group *ngFor="let result of results"> <ion-item-group *ngFor="let result of results">
<ion-item [button]="result.numberofuser > 0" class="divider ion-text-wrap" (click)="toggle(result)" <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" <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"> class="expandable-status-icon" [class.expandable-status-icon-expanded]="result.expanded">
</ion-icon> </ion-icon>

View File

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

View File

@ -5,7 +5,7 @@
</ion-buttons> </ion-buttons>
<ion-title> <ion-title>
<h1> <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> </core-format-text>
</h1> </h1>
</ion-title> </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"> [attr.aria-label]="'addon.mod_data.actionsmenu' | translate">
<ion-icon name="fas-ellipsis-vertical" slot="icon-only" aria-hidden="true"></ion-icon> <ion-icon name="fas-ellipsis-vertical" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button> </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"> [attr.aria-label]="'addon.mod_data.showmore' | translate">
<ion-icon name="fas-magnifying-glass-plus" slot="icon-only" aria-hidden="true"></ion-icon> <ion-icon name="fas-magnifying-glass-plus" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button> </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-icon name="fas-pen" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button> </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"> [attr.aria-label]="'core.delete' | translate">
<ion-icon name="fas-trash" slot="icon-only" aria-hidden="true"></ion-icon> <ion-icon name="fas-trash" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button> </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"> [attr.aria-label]="'core.restore' | translate">
<ion-icon name="fas-rotate-left" slot="icon-only" aria-hidden="true"></ion-icon> <ion-icon name="fas-rotate-left" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button> </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"> [attr.aria-label]="'addon.mod_data.approve' | translate">
<ion-icon name="fas-thumbs-up" slot="icon-only" aria-hidden="true"></ion-icon> <ion-icon name="fas-thumbs-up" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button> </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"> [attr.aria-label]="'addon.mod_data.disapprove' | translate">
<ion-icon name="far-thumbs-down" slot="icon-only" aria-hidden="true"></ion-icon> <ion-icon name="far-thumbs-down" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button> </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"> component="mod_data" [itemId]="entry.id" area="database_entry" [courseId]="database.course">
</core-comments> </core-comments>
<span *ngIf="action == 'timeadded'">{{ entry.timecreated * 1000 | coreFormatDate }}</span> <span *ngIf="action === 'timeadded'">{{ entry.timecreated * 1000 | coreFormatDate }}</span>
<span *ngIf="action == 'timemodified'">{{ entry.timemodified * 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> [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}} {{entry.fullname}}
</a> </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-content>
<ion-list> <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"> [attr.aria-label]="item.text | translate">
<ion-label> <ion-label>
<p class="item-heading">{{ item.text | translate }}</p> <p class="item-heading">{{ item.text | translate }}</p>

View File

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

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