Merge pull request #4070 from NoelDeMartin/MOBILE-4470
MOBILE-4470 core: Refactor mod-icon to use signalsmain
commit
1d135e62d2
|
@ -1,6 +1,6 @@
|
||||||
<ng-container *ngIf="loaded && !svgIcon">
|
<ng-container *ngIf="loaded() && !svgIcon()">
|
||||||
<img *ngIf="!isLocalUrl" [url]="iconUrl" core-external-content alt="" [component]="linkIconWithComponent ? modname : null"
|
<img *ngIf="!isLocalUrl()" [url]="iconUrl()" core-external-content alt="" [component]="linkIconWithComponent() ? modname : null"
|
||||||
[componentId]="linkIconWithComponent ? componentId : null" (error)="loadFallbackIcon()">
|
[componentId]="linkIconWithComponent() ? componentId : null" (error)="loadFallbackIcon()">
|
||||||
<img *ngIf="isLocalUrl" [src]="iconUrl" (error)="loadFallbackIcon()" alt="">
|
<img *ngIf="isLocalUrl()" [src]="iconUrl()" (error)="loadFallbackIcon()" alt="">
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<div *ngIf="svgIcon" [innerHTML]="svgIcon"></div>
|
<div *ngIf="svgIcon()" [innerHTML]="svgIcon()"></div>
|
||||||
|
|
|
@ -13,7 +13,17 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { CoreConstants, ModPurpose } from '@/core/constants';
|
import { CoreConstants, ModPurpose } from '@/core/constants';
|
||||||
import { Component, ElementRef, HostBinding, Input, OnChanges, OnInit, SimpleChange } from '@angular/core';
|
import {
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
Component,
|
||||||
|
ElementRef,
|
||||||
|
HostBinding,
|
||||||
|
Input,
|
||||||
|
OnChanges,
|
||||||
|
OnInit,
|
||||||
|
SimpleChange,
|
||||||
|
signal,
|
||||||
|
} from '@angular/core';
|
||||||
import { SafeHtml } from '@angular/platform-browser';
|
import { SafeHtml } from '@angular/platform-browser';
|
||||||
import { CoreCourse } from '@features/course/services/course';
|
import { CoreCourse } from '@features/course/services/course';
|
||||||
import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate';
|
import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate';
|
||||||
|
@ -42,6 +52,7 @@ const enum IconVersion {
|
||||||
selector: 'core-mod-icon',
|
selector: 'core-mod-icon',
|
||||||
templateUrl: 'mod-icon.html',
|
templateUrl: 'mod-icon.html',
|
||||||
styleUrls: ['mod-icon.scss'],
|
styleUrls: ['mod-icon.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class CoreModIconComponent implements OnInit, OnChanges {
|
export class CoreModIconComponent implements OnInit, OnChanges {
|
||||||
|
|
||||||
|
@ -63,16 +74,15 @@ export class CoreModIconComponent implements OnInit, OnChanges {
|
||||||
|
|
||||||
@HostBinding('attr.aria-label')
|
@HostBinding('attr.aria-label')
|
||||||
get getAriaLabel(): string {
|
get getAriaLabel(): string {
|
||||||
return this.showAlt ? this.modNameTranslated : '';
|
return this.showAlt ? this.modNameTranslated() : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
iconUrl = '';
|
iconUrl = signal('');
|
||||||
|
modNameTranslated = signal('');
|
||||||
modNameTranslated = '';
|
isLocalUrl = signal(false);
|
||||||
isLocalUrl = false;
|
svgIcon = signal<SafeHtml>('');
|
||||||
svgIcon: SafeHtml = '';
|
linkIconWithComponent = signal(false);
|
||||||
linkIconWithComponent = false;
|
loaded = signal(false);
|
||||||
loaded = false;
|
|
||||||
|
|
||||||
protected iconVersion: IconVersion = IconVersion.LEGACY_VERSION;
|
protected iconVersion: IconVersion = IconVersion.LEGACY_VERSION;
|
||||||
protected purposeClass = '';
|
protected purposeClass = '';
|
||||||
|
@ -94,7 +104,7 @@ export class CoreModIconComponent implements OnInit, OnChanges {
|
||||||
this.modname = this.getComponentNameFromIconUrl(this.modicon);
|
this.modname = this.getComponentNameFromIconUrl(this.modicon);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.modNameTranslated = CoreCourse.translateModuleName(this.modname, this.fallbackTranslation);
|
this.modNameTranslated.set(CoreCourse.translateModuleName(this.modname, this.fallbackTranslation));
|
||||||
|
|
||||||
this.setPurposeClass();
|
this.setPurposeClass();
|
||||||
|
|
||||||
|
@ -133,7 +143,7 @@ export class CoreModIconComponent implements OnInit, OnChanges {
|
||||||
this.brandedClass = this.isBranded;
|
this.brandedClass = this.isBranded;
|
||||||
|
|
||||||
// No icon or local icon (not legacy), colorize it.
|
// No icon or local icon (not legacy), colorize it.
|
||||||
if (!this.iconUrl || this.isLocalUrl) {
|
if (!this.iconUrl() || this.isLocalUrl()) {
|
||||||
// Exception for bigbluebuttonbn, it's the only one that has a branded icon.
|
// Exception for bigbluebuttonbn, it's the only one that has a branded icon.
|
||||||
if (this.iconVersion === IconVersion.VERSION_4_0 && this.modname === 'bigbluebuttonbn') {
|
if (this.iconVersion === IconVersion.VERSION_4_0 && this.modname === 'bigbluebuttonbn') {
|
||||||
this.brandedClass = true;
|
this.brandedClass = true;
|
||||||
|
@ -146,11 +156,11 @@ export class CoreModIconComponent implements OnInit, OnChanges {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.iconUrl = CoreTextUtils.decodeHTMLEntities(this.iconUrl);
|
this.iconUrl.update(value => CoreTextUtils.decodeHTMLEntities(value));
|
||||||
|
|
||||||
// If it's an Moodle Theme icon, check if filtericon is set and use it.
|
// If it's an Moodle Theme icon, check if filtericon is set and use it.
|
||||||
if (this.iconUrl && CoreUrlUtils.isThemeImageUrl(this.iconUrl)) {
|
if (CoreUrlUtils.isThemeImageUrl(this.iconUrl())) {
|
||||||
const filter = CoreUrlUtils.getThemeImageUrlParam(this.iconUrl, 'filtericon');
|
const filter = CoreUrlUtils.getThemeImageUrlParam(this.iconUrl(), 'filtericon');
|
||||||
if (filter === '1') {
|
if (filter === '1') {
|
||||||
this.brandedClass = false;
|
this.brandedClass = false;
|
||||||
|
|
||||||
|
@ -160,7 +170,7 @@ export class CoreModIconComponent implements OnInit, OnChanges {
|
||||||
// filtericon was introduced in 4.2 and backported to 4.1.3 and 4.0.8.
|
// filtericon was introduced in 4.2 and backported to 4.1.3 and 4.0.8.
|
||||||
if (this.modname && !CoreSites.getCurrentSite()?.isVersionGreaterEqualThan(['4.0.8', '4.1.3', '4.2'])) {
|
if (this.modname && !CoreSites.getCurrentSite()?.isVersionGreaterEqualThan(['4.0.8', '4.1.3', '4.2'])) {
|
||||||
// If version is prior to that, check if the url is a module icon and filter it.
|
// If version is prior to that, check if the url is a module icon and filter it.
|
||||||
if (this.getComponentNameFromIconUrl(this.iconUrl) === this.modname) {
|
if (this.getComponentNameFromIconUrl(this.iconUrl()) === this.modname) {
|
||||||
this.brandedClass = false;
|
this.brandedClass = false;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -169,32 +179,33 @@ export class CoreModIconComponent implements OnInit, OnChanges {
|
||||||
}
|
}
|
||||||
|
|
||||||
// External icons, or non monologo, do not filter.
|
// External icons, or non monologo, do not filter.
|
||||||
this.brandedClass = true;
|
this.brandedClass = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set icon.
|
* Set icon.
|
||||||
*/
|
*/
|
||||||
async setIcon(): Promise<void> {
|
async setIcon(): Promise<void> {
|
||||||
this.iconUrl = this.modicon || this.iconUrl;
|
this.iconUrl.update(value => this.modicon || value);
|
||||||
|
|
||||||
if (!this.iconUrl) {
|
if (!this.iconUrl()) {
|
||||||
this.loadFallbackIcon();
|
this.loadFallbackIcon();
|
||||||
this.setBrandedClass();
|
this.setBrandedClass();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isLocalUrl = this.iconUrl.startsWith(assetsPath);
|
this.isLocalUrl.set(this.iconUrl().startsWith(assetsPath));
|
||||||
|
|
||||||
// Cache icon if the url is not the theme generic one.
|
// Cache icon if the url is not the theme generic one.
|
||||||
// If modname is not set icon won't be cached.
|
// If modname is not set icon won't be cached.
|
||||||
// Also if the url matches the regexp (the theme will manage the image so it's not cached).
|
// Also if the url matches the regexp (the theme will manage the image so it's not cached).
|
||||||
this.linkIconWithComponent =
|
this.linkIconWithComponent.set(
|
||||||
!!this.modname &&
|
!!this.modname &&
|
||||||
!!this.componentId &&
|
!!this.componentId &&
|
||||||
!this.isLocalUrl &&
|
!this.isLocalUrl() &&
|
||||||
this.getComponentNameFromIconUrl(this.iconUrl) != this.modname;
|
this.getComponentNameFromIconUrl(this.iconUrl()) != this.modname,
|
||||||
|
);
|
||||||
|
|
||||||
this.setBrandedClass();
|
this.setBrandedClass();
|
||||||
|
|
||||||
|
@ -205,12 +216,12 @@ export class CoreModIconComponent implements OnInit, OnChanges {
|
||||||
* Icon to load on error.
|
* Icon to load on error.
|
||||||
*/
|
*/
|
||||||
async loadFallbackIcon(): Promise<void> {
|
async loadFallbackIcon(): Promise<void> {
|
||||||
if (this.isLocalUrl) {
|
if (this.isLocalUrl()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isLocalUrl = true;
|
this.isLocalUrl.set(true);
|
||||||
this.linkIconWithComponent = false;
|
this.linkIconWithComponent.set(false);
|
||||||
|
|
||||||
const moduleName = !this.modname || !CoreCourse.isCoreModule(this.modname)
|
const moduleName = !this.modname || !CoreCourse.isCoreModule(this.modname)
|
||||||
? fallbackModName
|
? fallbackModName
|
||||||
|
@ -218,7 +229,7 @@ export class CoreModIconComponent implements OnInit, OnChanges {
|
||||||
|
|
||||||
const path = CoreCourse.getModuleIconsPath();
|
const path = CoreCourse.getModuleIconsPath();
|
||||||
|
|
||||||
this.iconUrl = path + moduleName + '.svg';
|
this.iconUrl.set(path + moduleName + '.svg');
|
||||||
|
|
||||||
await this.setSVGIcon();
|
await this.setSVGIcon();
|
||||||
}
|
}
|
||||||
|
@ -302,24 +313,24 @@ export class CoreModIconComponent implements OnInit, OnChanges {
|
||||||
*/
|
*/
|
||||||
protected async setSVGIcon(): Promise<void> {
|
protected async setSVGIcon(): Promise<void> {
|
||||||
if (this.iconVersion === IconVersion.LEGACY_VERSION) {
|
if (this.iconVersion === IconVersion.LEGACY_VERSION) {
|
||||||
this.loaded = true;
|
this.loaded.set(true);
|
||||||
this.svgIcon = '';
|
this.svgIcon.set('');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.loaded = false;
|
this.loaded.set(false);
|
||||||
|
|
||||||
let mimetype = '';
|
let mimetype = '';
|
||||||
let fileContents = '';
|
let fileContents = '';
|
||||||
|
|
||||||
// Download the icon if it's not local to cache it.
|
// Download the icon if it's not local to cache it.
|
||||||
if (!this.isLocalUrl) {
|
if (!this.isLocalUrl()) {
|
||||||
try {
|
try {
|
||||||
const iconUrl = await CoreFileHelper.downloadFile(
|
const iconUrl = await CoreFileHelper.downloadFile(
|
||||||
this.iconUrl,
|
this.iconUrl(),
|
||||||
this.linkIconWithComponent ? this.modname : undefined,
|
this.linkIconWithComponent() ? this.modname : undefined,
|
||||||
this.linkIconWithComponent ? this.componentId : undefined,
|
this.linkIconWithComponent() ? this.componentId : undefined,
|
||||||
);
|
);
|
||||||
if (iconUrl) {
|
if (iconUrl) {
|
||||||
mimetype = await CoreUtils.getMimeTypeFromUrl(iconUrl);
|
mimetype = await CoreUtils.getMimeTypeFromUrl(iconUrl);
|
||||||
|
@ -335,7 +346,7 @@ export class CoreModIconComponent implements OnInit, OnChanges {
|
||||||
if (!fileContents) {
|
if (!fileContents) {
|
||||||
// Try to download the icon directly (also for local files).
|
// Try to download the icon directly (also for local files).
|
||||||
const response = await firstValueFrom(Http.get(
|
const response = await firstValueFrom(Http.get(
|
||||||
this.iconUrl,
|
this.iconUrl(),
|
||||||
{
|
{
|
||||||
observe: 'response',
|
observe: 'response',
|
||||||
responseType: 'text',
|
responseType: 'text',
|
||||||
|
@ -346,7 +357,7 @@ export class CoreModIconComponent implements OnInit, OnChanges {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mimetype !== 'image/svg+xml' || !fileContents) {
|
if (mimetype !== 'image/svg+xml' || !fileContents) {
|
||||||
this.svgIcon = '';
|
this.svgIcon.set('');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -357,7 +368,7 @@ export class CoreModIconComponent implements OnInit, OnChanges {
|
||||||
|
|
||||||
// Safety check.
|
// Safety check.
|
||||||
if (doc.documentElement.nodeName !== 'svg') {
|
if (doc.documentElement.nodeName !== 'svg') {
|
||||||
this.svgIcon = '';
|
this.svgIcon.set('');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -424,11 +435,11 @@ export class CoreModIconComponent implements OnInit, OnChanges {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
this.svgIcon = DomSanitizer.bypassSecurityTrustHtml(doc.documentElement.outerHTML);
|
this.svgIcon.set(DomSanitizer.bypassSecurityTrustHtml(doc.documentElement.outerHTML));
|
||||||
} catch {
|
} catch {
|
||||||
this.svgIcon = '';
|
this.svgIcon.set('');
|
||||||
} finally {
|
} finally {
|
||||||
this.loaded = true;
|
this.loaded.set(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue