2
0
Fork 0

MOBILE-4193 grades: Avoid filtering rich images

main
Noel De Martin 2023-03-14 18:20:11 +01:00
parent 0c998b8f8b
commit f82d3504f6
8 changed files with 68 additions and 10 deletions

View File

@ -22,6 +22,7 @@ import { AddonModLtiHelper } from '../lti-helper';
import { AddonModLtiIndexComponent } from '../../components/index'; import { AddonModLtiIndexComponent } from '../../components/index';
import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler'; import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler';
import { CoreCourse } from '@features/course/services/course'; import { CoreCourse } from '@features/course/services/course';
import { CoreSites } from '@services/sites';
/** /**
* Handler to support LTI modules. * Handler to support LTI modules.
@ -86,6 +87,19 @@ export class AddonModLtiModuleHandlerService extends CoreModuleHandlerBase imple
return module?.modicon ?? modicon ?? CoreCourse.getModuleIconSrc(this.modName); return module?.modicon ?? modicon ?? CoreCourse.getModuleIconSrc(this.modName);
} }
/**
* @inheritdoc
*/
iconIsShape(module?: CoreCourseModuleData | undefined, modicon?: string | undefined): boolean | undefined {
const iconUrl = module?.modicon ?? modicon;
if (!iconUrl) {
return true;
}
return iconUrl.startsWith(CoreSites.getRequiredCurrentSite().siteUrl);
}
} }
export const AddonModLtiModuleHandler = makeSingleton(AddonModLtiModuleHandlerService); export const AddonModLtiModuleHandler = makeSingleton(AddonModLtiModuleHandlerService);

View File

@ -1,5 +1,5 @@
<img *ngIf="!isLocalUrl" [src]="icon" [alt]="showAlt ? modNameTranslated : ''" [attr.role]="!showAlt ? 'presentation' : null" <img *ngIf="!isLocalUrl" [src]="icon" [alt]="showAlt ? modNameTranslated : ''" [attr.role]="!showAlt ? 'presentation' : null"
class="core-module-icon" core-external-content [component]="linkIconWithComponent ? modname : null" class="core-module-icon" [class.no-filter]="noFilter" core-external-content [component]="linkIconWithComponent ? modname : null"
[componentId]="linkIconWithComponent ? componentId : null" (error)="loadFallbackIcon()"> [componentId]="linkIconWithComponent ? componentId : null" (error)="loadFallbackIcon()">
<img *ngIf="isLocalUrl" [src]="icon" [alt]="showAlt ? modNameTranslated : ''" [attr.role]="!showAlt ? 'presentation' : null" <img *ngIf="isLocalUrl" [src]="icon" [alt]="showAlt ? modNameTranslated : ''" [attr.role]="!showAlt ? 'presentation' : null"
class="core-module-icon" (error)="loadFallbackIcon()"> class="core-module-icon" [class.no-filter]="noFilter" (error)="loadFallbackIcon()">

View File

@ -42,6 +42,11 @@ img {
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
} }
&.no-filter {
--filter: none;
}
} }
:host-context(ion-item) { :host-context(ion-item) {

View File

@ -13,7 +13,7 @@
// limitations under the License. // limitations under the License.
import { CoreConstants, ModPurpose } from '@/core/constants'; import { CoreConstants, ModPurpose } from '@/core/constants';
import { Component, ElementRef, Input, OnChanges, OnInit, SimpleChange } from '@angular/core'; import { Component, ElementRef, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChange } from '@angular/core';
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';
import { CoreSites } from '@services/sites'; import { CoreSites } from '@services/sites';
@ -34,9 +34,12 @@ export class CoreModIconComponent implements OnInit, OnChanges {
@Input() modname?: string; // The module name. Used also as component if set. @Input() modname?: string; // The module name. Used also as component if set.
@Input() componentId?: number; // Component Id for external icons. @Input() componentId?: number; // Component Id for external icons.
@Input() modicon?: string; // Module icon url or local url. @Input() modicon?: string; // Module icon url or local url.
@Input() noFilter?: boolean; // Whether to disable filters.
@Input() showAlt = true; // Show alt otherwise it's only presentation icon. @Input() showAlt = true; // Show alt otherwise it's only presentation icon.
@Input() purpose: ModPurpose = ModPurpose.MOD_PURPOSE_OTHER; // Purpose of the module. @Input() purpose: ModPurpose = ModPurpose.MOD_PURPOSE_OTHER; // Purpose of the module.
@Output() failedLoading = new EventEmitter<void>();
icon = ''; icon = '';
modNameTranslated = ''; modNameTranslated = '';
isLocalUrl = true; isLocalUrl = true;
@ -122,6 +125,8 @@ export class CoreModIconComponent implements OnInit, OnChanges {
} }
this.icon = path + moduleName + '.svg'; this.icon = path + moduleName + '.svg';
this.failedLoading.emit();
} }
} }

View File

@ -87,6 +87,15 @@ export interface CoreCourseModuleHandler extends CoreDelegateHandler {
*/ */
getIconSrc?(module?: CoreCourseModuleData, modicon?: string): Promise<string | undefined> | string | undefined; getIconSrc?(module?: CoreCourseModuleData, modicon?: string): Promise<string | undefined> | string | undefined;
/**
* Check whether the icon should be treated as a shape or a rich image.
*
* @param module Module to get the icon from.
* @param modicon The mod icon string.
* @returns Whether the icon should be treated as a shape.
*/
iconIsShape?(module?: CoreCourseModuleData, modicon?: string): Promise<boolean | undefined> | boolean | undefined;
/** /**
* Check if this type of module supports a certain feature. * Check if this type of module supports a certain feature.
* If this function is implemented, the supportedFeatures object will be ignored. * If this function is implemented, the supportedFeatures object will be ignored.
@ -396,6 +405,18 @@ export class CoreCourseModuleDelegateService extends CoreDelegate<CoreCourseModu
return icon ?? CoreCourse.getModuleIconSrc(modname, modicon) ?? ''; return icon ?? CoreCourse.getModuleIconSrc(modname, modicon) ?? '';
} }
/**
* Get whether the icon for the given module should be treated as a shape or a rich image.
*
* @param modname The name of the module type.
* @param modicon The mod icon string.
* @param module The module to use.
* @returns Whether the icon should be treated as a shape.
*/
async moduleIconIsShape(modname: string, modicon?: string, module?: CoreCourseModuleData): Promise<boolean | undefined> {
return await this.executeFunctionOnEnabled<Promise<boolean>>(modname, 'iconIsShape', [module, modicon]);
}
/** /**
* Check if a certain type of module supports a certain feature. * Check if a certain type of module supports a certain feature.
* *

View File

@ -48,7 +48,8 @@
<img *ngIf="row.image && !row.itemmodule" [src]="row.image" slot="start" class="core-module-icon" <img *ngIf="row.image && !row.itemmodule" [src]="row.image" slot="start" class="core-module-icon"
[alt]="row.iconAlt" /> [alt]="row.iconAlt" />
<core-mod-icon *ngIf="row.image && row.itemmodule" [modicon]="row.image" slot="start" <core-mod-icon *ngIf="row.image && row.itemmodule" [modicon]="row.image" slot="start"
[modname]="row.itemmodule"> [modname]="row.itemmodule" [noFilter]="row.imageIsShape === false"
(failedLoading)="failedLoadingRowImage(row)">
</core-mod-icon> </core-mod-icon>
<span [innerHTML]="row.gradeitem"></span> <span [innerHTML]="row.gradeitem"></span>
</th> </th>

View File

@ -239,4 +239,13 @@ export class CoreGradesCoursePage implements AfterViewInit, OnDestroy {
infiniteComplete && infiniteComplete(); infiniteComplete && infiniteComplete();
} }
/**
* Handle row image failed loading.
*
* @param row Row data.
*/
failedLoadingRowImage(row: CoreGradesFormattedTableRow): void {
delete row.imageIsShape;
}
} }

View File

@ -684,14 +684,16 @@ export class CoreGradesHelperProvider {
row.iconAlt = Translate.instant('core.grades.calculatedgrade'); row.iconAlt = Translate.instant('core.grades.calculatedgrade');
} else if (text.indexOf('/mod/') > -1) { } else if (text.indexOf('/mod/') > -1) {
const module = text.match(/mod\/([^/]*)\//); const module = text.match(/mod\/([^/]*)\//);
if (module?.[1] !== undefined) { const modname = module?.[1];
if (modname !== undefined) {
const modicon = CoreDomUtils.convertToElement(text).querySelector('img')?.getAttribute('src') ?? undefined;
row.itemtype = 'mod'; row.itemtype = 'mod';
row.itemmodule = module[1]; row.itemmodule = modname;
row.iconAlt = CoreCourse.translateModuleName(row.itemmodule) || ''; row.iconAlt = CoreCourse.translateModuleName(row.itemmodule) || '';
row.image = await CoreCourseModuleDelegate.getModuleIconSrc( row.image = await CoreCourseModuleDelegate.getModuleIconSrc(modname, modicon);
module[1], row.imageIsShape = await CoreCourseModuleDelegate.moduleIconIsShape(modname, modicon);
CoreDomUtils.convertToElement(text).querySelector('img')?.getAttribute('src') ?? undefined,
);
} }
} else { } else {
if (row.rowspan && row.rowspan > 1) { if (row.rowspan && row.rowspan > 1) {
@ -804,6 +806,7 @@ export type CoreGradesFormattedRowCommonData = {
rowclass?: string; rowclass?: string;
itemtype?: string; itemtype?: string;
image?: string; image?: string;
imageIsShape?: boolean;
itemmodule?: string; itemmodule?: string;
iconAlt?: string; iconAlt?: string;
rowspan?: number; rowspan?: number;