Merge pull request #4070 from NoelDeMartin/MOBILE-4470
MOBILE-4470 core: Refactor mod-icon to use signals
This commit is contained in:
		
						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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user