Merge pull request #3579 from NoelDeMartin/MOBILE-4193
MOBILE-4193 core: Consolidate module icons sourcesmain
commit
8f6320e012
|
@ -18,6 +18,7 @@ import { CoreDomUtils } from '@services/utils/dom';
|
||||||
import { CoreCourse } from '@features/course/services/course';
|
import { CoreCourse } from '@features/course/services/course';
|
||||||
import { CoreSiteWSPreSets } from '@classes/site';
|
import { CoreSiteWSPreSets } from '@classes/site';
|
||||||
import { makeSingleton } from '@singletons';
|
import { makeSingleton } from '@singletons';
|
||||||
|
import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate';
|
||||||
|
|
||||||
const ROOT_CACHE_KEY = 'AddonBlockRecentlyAccessedItems:';
|
const ROOT_CACHE_KEY = 'AddonBlockRecentlyAccessedItems:';
|
||||||
|
|
||||||
|
@ -54,15 +55,15 @@ export class AddonBlockRecentlyAccessedItemsProvider {
|
||||||
|
|
||||||
const cmIds: number[] = [];
|
const cmIds: number[] = [];
|
||||||
|
|
||||||
items = items.map((item) => {
|
items = await Promise.all(items.map(async (item) => {
|
||||||
const modicon = item.icon && CoreDomUtils.getHTMLElementAttribute(item.icon, 'src');
|
const modicon = item.icon && CoreDomUtils.getHTMLElementAttribute(item.icon, 'src');
|
||||||
|
|
||||||
item.iconUrl = CoreCourse.getModuleIconSrc(item.modname, modicon || undefined);
|
item.iconUrl = await CoreCourseModuleDelegate.getModuleIconSrc(item.modname, modicon || undefined);
|
||||||
item.iconTitle = item.icon && CoreDomUtils.getHTMLElementAttribute(item.icon, 'title');
|
item.iconTitle = item.icon && CoreDomUtils.getHTMLElementAttribute(item.icon, 'title');
|
||||||
cmIds.push(item.cmid);
|
cmIds.push(item.cmid);
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
});
|
}));
|
||||||
|
|
||||||
// Check if the viewed module should be updated for each activity.
|
// Check if the viewed module should be updated for each activity.
|
||||||
const lastViewedMap = await CoreCourse.getCertainModulesViewed(cmIds, site.getId());
|
const lastViewedMap = await CoreCourse.getCertainModulesViewed(cmIds, site.getId());
|
||||||
|
|
|
@ -15,9 +15,10 @@
|
||||||
import { AddonBlockTimeline } from '@addons/block/timeline/services/timeline';
|
import { AddonBlockTimeline } from '@addons/block/timeline/services/timeline';
|
||||||
import { AddonCalendarEvent } from '@addons/calendar/services/calendar';
|
import { AddonCalendarEvent } from '@addons/calendar/services/calendar';
|
||||||
import { CoreCourse } from '@features/course/services/course';
|
import { CoreCourse } from '@features/course/services/course';
|
||||||
|
import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate';
|
||||||
import { CoreEnrolledCourseDataWithOptions } from '@features/courses/services/courses-helper';
|
import { CoreEnrolledCourseDataWithOptions } from '@features/courses/services/courses-helper';
|
||||||
import { CoreTimeUtils } from '@services/utils/time';
|
import { CoreTimeUtils } from '@services/utils/time';
|
||||||
import { BehaviorSubject } from 'rxjs';
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A collection of events displayed in the timeline block.
|
* A collection of events displayed in the timeline block.
|
||||||
|
@ -28,12 +29,8 @@ export class AddonBlockTimelineSection {
|
||||||
overdue: boolean;
|
overdue: boolean;
|
||||||
dateRange: AddonBlockTimelineDateRange;
|
dateRange: AddonBlockTimelineDateRange;
|
||||||
course?: CoreEnrolledCourseDataWithOptions;
|
course?: CoreEnrolledCourseDataWithOptions;
|
||||||
data$: BehaviorSubject<{
|
|
||||||
events: AddonBlockTimelineDayEvents[];
|
private dataSubject$: BehaviorSubject<AddonBlockTimelineSectionData>;
|
||||||
lastEventId?: number;
|
|
||||||
canLoadMore: boolean;
|
|
||||||
loadingMore: boolean;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
search: string | null,
|
search: string | null,
|
||||||
|
@ -47,30 +44,42 @@ export class AddonBlockTimelineSection {
|
||||||
this.overdue = overdue;
|
this.overdue = overdue;
|
||||||
this.dateRange = dateRange;
|
this.dateRange = dateRange;
|
||||||
this.course = course;
|
this.course = course;
|
||||||
this.data$ = new BehaviorSubject({
|
this.dataSubject$ = new BehaviorSubject({
|
||||||
events: courseEvents ? this.reduceEvents(courseEvents, overdue, dateRange) : [],
|
events: [],
|
||||||
lastEventId: canLoadMore,
|
lastEventId: canLoadMore,
|
||||||
canLoadMore: typeof canLoadMore !== 'undefined',
|
canLoadMore: typeof canLoadMore !== 'undefined',
|
||||||
loadingMore: false,
|
loadingMore: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (courseEvents) {
|
||||||
|
// eslint-disable-next-line promise/catch-or-return
|
||||||
|
this.reduceEvents(courseEvents, overdue, dateRange).then(events => this.dataSubject$.next({
|
||||||
|
...this.dataSubject$.value,
|
||||||
|
events,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get data$(): Observable<AddonBlockTimelineSectionData> {
|
||||||
|
return this.dataSubject$;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load more events.
|
* Load more events.
|
||||||
*/
|
*/
|
||||||
async loadMore(): Promise<void> {
|
async loadMore(): Promise<void> {
|
||||||
this.data$.next({
|
this.dataSubject$.next({
|
||||||
...this.data$.value,
|
...this.dataSubject$.value,
|
||||||
loadingMore: true,
|
loadingMore: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const lastEventId = this.data$.value.lastEventId;
|
const lastEventId = this.dataSubject$.value.lastEventId;
|
||||||
const { events, canLoadMore } = this.course
|
const { events, canLoadMore } = this.course
|
||||||
? await AddonBlockTimeline.getActionEventsByCourse(this.course.id, lastEventId, this.search ?? '')
|
? await AddonBlockTimeline.getActionEventsByCourse(this.course.id, lastEventId, this.search ?? '')
|
||||||
: await AddonBlockTimeline.getActionEventsByTimesort(lastEventId, this.search ?? '');
|
: await AddonBlockTimeline.getActionEventsByTimesort(lastEventId, this.search ?? '');
|
||||||
|
|
||||||
this.data$.next({
|
this.dataSubject$.next({
|
||||||
events: this.data$.value.events.concat(this.reduceEvents(events, this.overdue, this.dateRange)),
|
events: this.dataSubject$.value.events.concat(await this.reduceEvents(events, this.overdue, this.dateRange)),
|
||||||
lastEventId: canLoadMore,
|
lastEventId: canLoadMore,
|
||||||
canLoadMore: canLoadMore !== undefined,
|
canLoadMore: canLoadMore !== undefined,
|
||||||
loadingMore: false,
|
loadingMore: false,
|
||||||
|
@ -85,32 +94,35 @@ export class AddonBlockTimelineSection {
|
||||||
* @param dateRange Date range to filter events.
|
* @param dateRange Date range to filter events.
|
||||||
* @returns Day events list.
|
* @returns Day events list.
|
||||||
*/
|
*/
|
||||||
private reduceEvents(
|
private async reduceEvents(
|
||||||
events: AddonCalendarEvent[],
|
events: AddonCalendarEvent[],
|
||||||
overdue: boolean,
|
overdue: boolean,
|
||||||
{ from, to }: AddonBlockTimelineDateRange,
|
{ from, to }: AddonBlockTimelineDateRange,
|
||||||
): AddonBlockTimelineDayEvents[] {
|
): Promise<AddonBlockTimelineDayEvents[]> {
|
||||||
const filterDates: AddonBlockTimelineFilterDates = {
|
const filterDates: AddonBlockTimelineFilterDates = {
|
||||||
now: CoreTimeUtils.timestamp(),
|
now: CoreTimeUtils.timestamp(),
|
||||||
midnight: AddonBlockTimeline.getDayStart(),
|
midnight: AddonBlockTimeline.getDayStart(),
|
||||||
start: AddonBlockTimeline.getDayStart(from),
|
start: AddonBlockTimeline.getDayStart(from),
|
||||||
end: typeof to === 'number' ? AddonBlockTimeline.getDayStart(to) : undefined,
|
end: typeof to === 'number' ? AddonBlockTimeline.getDayStart(to) : undefined,
|
||||||
};
|
};
|
||||||
const eventsByDates = events
|
const timelineEvents = await Promise.all(
|
||||||
.filter((event) => this.filterEvent(event, overdue, filterDates))
|
events
|
||||||
.map((event) => this.mapToTimelineEvent(event, filterDates.now))
|
.filter((event) => this.filterEvent(event, overdue, filterDates))
|
||||||
.reduce((filteredEvents, event) => {
|
.map((event) => this.mapToTimelineEvent(event, filterDates.now)),
|
||||||
const dayTimestamp = CoreTimeUtils.getMidnightForTimestamp(event.timesort);
|
);
|
||||||
|
|
||||||
filteredEvents[dayTimestamp] = filteredEvents[dayTimestamp] ?? {
|
const eventsByDates = timelineEvents.reduce((filteredEvents, event) => {
|
||||||
dayTimestamp,
|
const dayTimestamp = CoreTimeUtils.getMidnightForTimestamp(event.timesort);
|
||||||
events: [],
|
|
||||||
} as AddonBlockTimelineDayEvents;
|
|
||||||
|
|
||||||
filteredEvents[dayTimestamp].events.push(event);
|
filteredEvents[dayTimestamp] = filteredEvents[dayTimestamp] ?? {
|
||||||
|
dayTimestamp,
|
||||||
|
events: [],
|
||||||
|
} as AddonBlockTimelineDayEvents;
|
||||||
|
|
||||||
return filteredEvents;
|
filteredEvents[dayTimestamp].events.push(event);
|
||||||
}, {} as Record<string, AddonBlockTimelineDayEvents>);
|
|
||||||
|
return filteredEvents;
|
||||||
|
}, {} as Record<string, AddonBlockTimelineDayEvents>);
|
||||||
|
|
||||||
return Object.values(eventsByDates);
|
return Object.values(eventsByDates);
|
||||||
}
|
}
|
||||||
|
@ -151,20 +163,30 @@ export class AddonBlockTimelineSection {
|
||||||
* @param now Current time.
|
* @param now Current time.
|
||||||
* @returns Timeline event.
|
* @returns Timeline event.
|
||||||
*/
|
*/
|
||||||
private mapToTimelineEvent(event: AddonCalendarEvent, now: number): AddonBlockTimelineEvent {
|
private async mapToTimelineEvent(event: AddonCalendarEvent, now: number): Promise<AddonBlockTimelineEvent> {
|
||||||
const modulename = event.modulename || event.icon.component;
|
const modulename = event.modulename || event.icon.component;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...event,
|
...event,
|
||||||
modulename,
|
modulename,
|
||||||
overdue: event.timesort < now,
|
overdue: event.timesort < now,
|
||||||
iconUrl: CoreCourse.getModuleIconSrc(event.icon.component),
|
iconUrl: await CoreCourseModuleDelegate.getModuleIconSrc(event.icon.component, event.icon.iconurl),
|
||||||
iconTitle: CoreCourse.translateModuleName(modulename),
|
iconTitle: CoreCourse.translateModuleName(modulename),
|
||||||
} as AddonBlockTimelineEvent;
|
} as AddonBlockTimelineEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Section data.
|
||||||
|
*/
|
||||||
|
export type AddonBlockTimelineSectionData = {
|
||||||
|
events: AddonBlockTimelineDayEvents[];
|
||||||
|
lastEventId?: number;
|
||||||
|
canLoadMore: boolean;
|
||||||
|
loadingMore: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Timestamps to use during event filtering.
|
* Timestamps to use during event filtering.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -22,7 +22,7 @@ import { CoreCoursesHelper, CoreEnrolledCourseDataWithOptions } from '@features/
|
||||||
import { CoreCourses } from '@features/courses/services/courses';
|
import { CoreCourses } from '@features/courses/services/courses';
|
||||||
import { CoreCourseOptionsDelegate } from '@features/course/services/course-options-delegate';
|
import { CoreCourseOptionsDelegate } from '@features/course/services/course-options-delegate';
|
||||||
import { BehaviorSubject, combineLatest, Observable, of, Subject } from 'rxjs';
|
import { BehaviorSubject, combineLatest, Observable, of, Subject } from 'rxjs';
|
||||||
import { catchError, distinctUntilChanged, map, share, tap } from 'rxjs/operators';
|
import { catchError, distinctUntilChanged, map, share, tap, mergeAll } from 'rxjs/operators';
|
||||||
import { AddonBlockTimelineDateRange, AddonBlockTimelineSection } from '@addons/block/timeline/classes/section';
|
import { AddonBlockTimelineDateRange, AddonBlockTimelineSection } from '@addons/block/timeline/classes/section';
|
||||||
import { FormControl } from '@angular/forms';
|
import { FormControl } from '@angular/forms';
|
||||||
import { formControlValue, resolved } from '@/core/utils/rxjs';
|
import { formControlValue, resolved } from '@/core/utils/rxjs';
|
||||||
|
@ -198,6 +198,7 @@ export class AddonBlockTimelineComponent implements OnInit, ICoreBlockComponent
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
resolved(),
|
resolved(),
|
||||||
|
mergeAll(),
|
||||||
catchError(error => {
|
catchError(error => {
|
||||||
// An error ocurred in the function, log the error and just resolve the observable so the workflow continues.
|
// An error ocurred in the function, log the error and just resolve the observable so the workflow continues.
|
||||||
this.logger.error(error);
|
this.logger.error(error);
|
||||||
|
@ -205,7 +206,7 @@ export class AddonBlockTimelineComponent implements OnInit, ICoreBlockComponent
|
||||||
// Error getting data, fail.
|
// Error getting data, fail.
|
||||||
CoreDomUtils.showErrorModalDefault(error, this.fetchContentDefaultError, true);
|
CoreDomUtils.showErrorModalDefault(error, this.fetchContentDefaultError, true);
|
||||||
|
|
||||||
return of([]);
|
return of([] as AddonBlockTimelineSection[]);
|
||||||
}),
|
}),
|
||||||
share(),
|
share(),
|
||||||
tap(() => (this.loaded = true)),
|
tap(() => (this.loaded = true)),
|
||||||
|
@ -224,12 +225,12 @@ export class AddonBlockTimelineComponent implements OnInit, ICoreBlockComponent
|
||||||
search: string | null,
|
search: string | null,
|
||||||
overdue: boolean,
|
overdue: boolean,
|
||||||
dateRange: AddonBlockTimelineDateRange,
|
dateRange: AddonBlockTimelineDateRange,
|
||||||
): Promise<AddonBlockTimelineSection[]> {
|
): Promise<Observable<AddonBlockTimelineSection[]>> {
|
||||||
const section = new AddonBlockTimelineSection(search, overdue, dateRange);
|
const section = new AddonBlockTimelineSection(search, overdue, dateRange);
|
||||||
|
|
||||||
await section.loadMore();
|
await section.loadMore();
|
||||||
|
|
||||||
return section.data$.value.events.length > 0 ? [section] : [];
|
return section.data$.pipe(map(({ events }) => events.length > 0 ? [section] : []));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -246,29 +247,38 @@ export class AddonBlockTimelineComponent implements OnInit, ICoreBlockComponent
|
||||||
overdue: boolean,
|
overdue: boolean,
|
||||||
dateRange: AddonBlockTimelineDateRange,
|
dateRange: AddonBlockTimelineDateRange,
|
||||||
courses: CoreEnrolledCourseDataWithOptions[],
|
courses: CoreEnrolledCourseDataWithOptions[],
|
||||||
): Promise<AddonBlockTimelineSection[]> {
|
): Promise<Observable<AddonBlockTimelineSection[]>> {
|
||||||
// Do not filter courses by date because they can contain activities due.
|
// Do not filter courses by date because they can contain activities due.
|
||||||
const courseIds = courses.map(course => course.id);
|
const courseIds = courses.map(course => course.id);
|
||||||
const gracePeriod = await this.getCoursesGracePeriod();
|
const gracePeriod = await this.getCoursesGracePeriod();
|
||||||
const courseEvents = await AddonBlockTimeline.getActionEventsByCourses(courseIds, search ?? '');
|
const courseEvents = await AddonBlockTimeline.getActionEventsByCourses(courseIds, search ?? '');
|
||||||
|
|
||||||
return courses
|
return combineLatest(
|
||||||
.filter(
|
courses
|
||||||
course =>
|
.filter(
|
||||||
!course.hidden &&
|
course =>
|
||||||
!CoreCoursesHelper.isPastCourse(course, gracePeriod.after) &&
|
!course.hidden &&
|
||||||
!CoreCoursesHelper.isFutureCourse(course, gracePeriod.after, gracePeriod.before) &&
|
!CoreCoursesHelper.isPastCourse(course, gracePeriod.after) &&
|
||||||
courseEvents[course.id].events.length > 0,
|
!CoreCoursesHelper.isFutureCourse(course, gracePeriod.after, gracePeriod.before) &&
|
||||||
)
|
courseEvents[course.id].events.length > 0,
|
||||||
.map(course => new AddonBlockTimelineSection(
|
)
|
||||||
search,
|
.map(course => {
|
||||||
overdue,
|
const section = new AddonBlockTimelineSection(
|
||||||
dateRange,
|
search,
|
||||||
course,
|
overdue,
|
||||||
courseEvents[course.id].events,
|
dateRange,
|
||||||
courseEvents[course.id].canLoadMore,
|
course,
|
||||||
))
|
courseEvents[course.id].events,
|
||||||
.filter(section => section.data$.value.events.length > 0);
|
courseEvents[course.id].canLoadMore,
|
||||||
|
);
|
||||||
|
|
||||||
|
return section.data$.pipe(map(({ events }) => events.length > 0 ? section : null));
|
||||||
|
}),
|
||||||
|
).pipe(
|
||||||
|
map(sections => sections.filter(
|
||||||
|
(section: AddonBlockTimelineSection | null): section is AddonBlockTimelineSection => !!section,
|
||||||
|
)),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -551,7 +551,9 @@ class AddonCalendarMonthSlidesItemsManagerSource extends CoreSwipeSlidesDynamicI
|
||||||
day.eventsFormated = day.eventsFormated || [];
|
day.eventsFormated = day.eventsFormated || [];
|
||||||
day.filteredEvents = day.filteredEvents || [];
|
day.filteredEvents = day.filteredEvents || [];
|
||||||
// Format online events.
|
// Format online events.
|
||||||
const onlineEventsFormatted = day.events.map((event) => AddonCalendarHelper.formatEventData(event));
|
const onlineEventsFormatted = await Promise.all(
|
||||||
|
day.events.map((event) => AddonCalendarHelper.formatEventData(event)),
|
||||||
|
);
|
||||||
|
|
||||||
day.eventsFormated = day.eventsFormated.concat(onlineEventsFormatted);
|
day.eventsFormated = day.eventsFormated.concat(onlineEventsFormatted);
|
||||||
|
|
||||||
|
|
|
@ -165,7 +165,7 @@ export class AddonCalendarUpcomingEventsComponent implements OnInit, DoCheck, On
|
||||||
async fetchEvents(): Promise<void> {
|
async fetchEvents(): Promise<void> {
|
||||||
// Don't pass courseId and categoryId, we'll filter them locally.
|
// Don't pass courseId and categoryId, we'll filter them locally.
|
||||||
const result = await AddonCalendar.getUpcomingEvents();
|
const result = await AddonCalendar.getUpcomingEvents();
|
||||||
this.onlineEvents = result.events.map((event) => AddonCalendarHelper.formatEventData(event));
|
this.onlineEvents = await Promise.all(result.events.map((event) => AddonCalendarHelper.formatEventData(event)));
|
||||||
// Merge the online events with offline data.
|
// Merge the online events with offline data.
|
||||||
this.events = this.mergeEvents();
|
this.events = this.mergeEvents();
|
||||||
// Filter events by course.
|
// Filter events by course.
|
||||||
|
|
|
@ -672,7 +672,7 @@ class AddonCalendarDaySlidesItemsManagerSource extends CoreSwipeSlidesDynamicIte
|
||||||
try {
|
try {
|
||||||
// Don't pass courseId and categoryId, we'll filter them locally.
|
// Don't pass courseId and categoryId, we'll filter them locally.
|
||||||
result = await AddonCalendar.getDayEvents(day.moment.year(), day.moment.month() + 1, day.moment.date());
|
result = await AddonCalendar.getDayEvents(day.moment.year(), day.moment.month() + 1, day.moment.date());
|
||||||
preloadedDay.onlineEvents = result.events.map((event) => AddonCalendarHelper.formatEventData(event));
|
preloadedDay.onlineEvents = await Promise.all(result.events.map((event) => AddonCalendarHelper.formatEventData(event)));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Allow navigating to non-cached days in offline (behave as if using emergency cache).
|
// Allow navigating to non-cached days in offline (behave as if using emergency cache).
|
||||||
if (CoreNetwork.isOnline()) {
|
if (CoreNetwork.isOnline()) {
|
||||||
|
|
|
@ -197,7 +197,7 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
|
||||||
// Get the event data.
|
// Get the event data.
|
||||||
if (this.eventId >= 0) {
|
if (this.eventId >= 0) {
|
||||||
const event = await AddonCalendar.getEventById(this.eventId);
|
const event = await AddonCalendar.getEventById(this.eventId);
|
||||||
this.event = AddonCalendarHelper.formatEventData(event);
|
this.event = await AddonCalendarHelper.formatEventData(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -37,6 +37,7 @@ import { AddonCalendarOfflineEventDBRecord } from './database/calendar-offline';
|
||||||
import { CoreCategoryData } from '@features/courses/services/courses';
|
import { CoreCategoryData } from '@features/courses/services/courses';
|
||||||
import { CoreTimeUtils } from '@services/utils/time';
|
import { CoreTimeUtils } from '@services/utils/time';
|
||||||
import { CoreReminders, CoreRemindersService } from '@features/reminders/services/reminders';
|
import { CoreReminders, CoreRemindersService } from '@features/reminders/services/reminders';
|
||||||
|
import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Context levels enumeration.
|
* Context levels enumeration.
|
||||||
|
@ -164,9 +165,9 @@ export class AddonCalendarHelperProvider {
|
||||||
* @param event Event to format.
|
* @param event Event to format.
|
||||||
* @returns The formatted event to display.
|
* @returns The formatted event to display.
|
||||||
*/
|
*/
|
||||||
formatEventData(
|
async formatEventData(
|
||||||
event: AddonCalendarEvent | AddonCalendarEventBase | AddonCalendarGetEventsEvent,
|
event: AddonCalendarEvent | AddonCalendarEventBase | AddonCalendarGetEventsEvent,
|
||||||
): AddonCalendarEventToDisplay {
|
): Promise<AddonCalendarEventToDisplay> {
|
||||||
|
|
||||||
const eventFormatted: AddonCalendarEventToDisplay = {
|
const eventFormatted: AddonCalendarEventToDisplay = {
|
||||||
...event,
|
...event,
|
||||||
|
@ -182,7 +183,10 @@ export class AddonCalendarHelperProvider {
|
||||||
};
|
};
|
||||||
|
|
||||||
if (event.modulename) {
|
if (event.modulename) {
|
||||||
eventFormatted.eventIcon = CoreCourse.getModuleIconSrc(event.modulename);
|
eventFormatted.eventIcon = await CoreCourseModuleDelegate.getModuleIconSrc(
|
||||||
|
event.modulename,
|
||||||
|
'icon' in event ? event.icon.iconurl : undefined,
|
||||||
|
);
|
||||||
eventFormatted.moduleIcon = eventFormatted.eventIcon;
|
eventFormatted.moduleIcon = eventFormatted.eventIcon;
|
||||||
eventFormatted.iconTitle = CoreCourse.translateModuleName(event.modulename);
|
eventFormatted.iconTitle = CoreCourse.translateModuleName(event.modulename);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1809,6 +1809,7 @@ export type AddonCalendarEventBase = {
|
||||||
key: string; // Key.
|
key: string; // Key.
|
||||||
component: string; // Component.
|
component: string; // Component.
|
||||||
alttext: string; // Alttext.
|
alttext: string; // Alttext.
|
||||||
|
iconurl?: string; // @since 4.2. Icon image url.
|
||||||
};
|
};
|
||||||
category?: {
|
category?: {
|
||||||
id: number; // Id.
|
id: number; // Id.
|
||||||
|
|
|
@ -52,7 +52,7 @@ export class AddonModLabelModuleHandlerService extends CoreModuleHandlerBase imp
|
||||||
module.description = '';
|
module.description = '';
|
||||||
|
|
||||||
return {
|
return {
|
||||||
icon: '',
|
icon: this.getIconSrc(),
|
||||||
title,
|
title,
|
||||||
a11yTitle: '',
|
a11yTitle: '',
|
||||||
class: 'addon-mod-label-handler',
|
class: 'addon-mod-label-handler',
|
||||||
|
@ -74,5 +74,12 @@ export class AddonModLabelModuleHandlerService extends CoreModuleHandlerBase imp
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
getIconSrc(): string {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
export const AddonModLabelModuleHandler = makeSingleton(AddonModLabelModuleHandlerService);
|
export const AddonModLabelModuleHandler = makeSingleton(AddonModLabelModuleHandlerService);
|
||||||
|
|
|
@ -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.
|
||||||
|
@ -58,10 +59,6 @@ export class AddonModLtiModuleHandlerService extends CoreModuleHandlerBase imple
|
||||||
): Promise<CoreCourseModuleHandlerData> {
|
): Promise<CoreCourseModuleHandlerData> {
|
||||||
const data = await super.getData(module, courseId, sectionId, forCoursePage);
|
const data = await super.getData(module, courseId, sectionId, forCoursePage);
|
||||||
data.showDownloadButton = false;
|
data.showDownloadButton = false;
|
||||||
|
|
||||||
// Handle custom icons.
|
|
||||||
data.icon = module.modicon;
|
|
||||||
|
|
||||||
data.buttons = [{
|
data.buttons = [{
|
||||||
icon: 'fas-external-link-alt',
|
icon: 'fas-external-link-alt',
|
||||||
label: 'addon.mod_lti.launchactivity',
|
label: 'addon.mod_lti.launchactivity',
|
||||||
|
@ -83,6 +80,26 @@ export class AddonModLtiModuleHandlerService extends CoreModuleHandlerBase imple
|
||||||
return AddonModLtiIndexComponent;
|
return AddonModLtiIndexComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
getIconSrc(module?: CoreCourseModuleData | undefined, modicon?: string | undefined): string | undefined {
|
||||||
|
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);
|
||||||
|
|
|
@ -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()">
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ export class CoreModuleHandlerBase implements Partial<CoreCourseModuleHandler> {
|
||||||
forCoursePage?: boolean, // eslint-disable-line @typescript-eslint/no-unused-vars
|
forCoursePage?: boolean, // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||||
): Promise<CoreCourseModuleHandlerData> | CoreCourseModuleHandlerData {
|
): Promise<CoreCourseModuleHandlerData> | CoreCourseModuleHandlerData {
|
||||||
return {
|
return {
|
||||||
icon: CoreCourse.getModuleIconSrc(module.modname, module.modicon),
|
icon: this.getIconSrc(module, module.modicon),
|
||||||
title: module.name,
|
title: module.name,
|
||||||
class: 'addon-mod_' + module.modname + '-handler',
|
class: 'addon-mod_' + module.modname + '-handler',
|
||||||
showDownloadButton: true,
|
showDownloadButton: true,
|
||||||
|
@ -78,4 +78,15 @@ export class CoreModuleHandlerBase implements Partial<CoreCourseModuleHandler> {
|
||||||
await CoreNavigator.navigateToSitePath(this.pageName + routeParams, options);
|
await CoreNavigator.navigateToSitePath(this.pageName + routeParams, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
getIconSrc(module?: CoreCourseModuleData, modicon?: string): Promise<string | undefined> | string | undefined {
|
||||||
|
if (!module) {
|
||||||
|
return modicon;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CoreCourse.getModuleIconSrc(module.name, modicon);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,9 +82,19 @@ export interface CoreCourseModuleHandler extends CoreDelegateHandler {
|
||||||
* Get the icon src for the module.
|
* Get the icon src for the module.
|
||||||
*
|
*
|
||||||
* @param module Module to get the icon from.
|
* @param module Module to get the icon from.
|
||||||
|
* @param modicon The mod icon string.
|
||||||
* @returns The icon src.
|
* @returns The icon src.
|
||||||
*/
|
*/
|
||||||
getIconSrc?(module?: CoreCourseModuleData): 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.
|
||||||
|
@ -390,9 +400,21 @@ export class CoreCourseModuleDelegateService extends CoreDelegate<CoreCourseModu
|
||||||
* @returns Promise resolved with the icon src.
|
* @returns Promise resolved with the icon src.
|
||||||
*/
|
*/
|
||||||
async getModuleIconSrc(modname: string, modicon?: string, module?: CoreCourseModuleData): Promise<string> {
|
async getModuleIconSrc(modname: string, modicon?: string, module?: CoreCourseModuleData): Promise<string> {
|
||||||
const icon = await this.executeFunctionOnEnabled<Promise<string>>(modname, 'getIconSrc', [module]);
|
const icon = await this.executeFunctionOnEnabled<Promise<string>>(modname, 'getIconSrc', [module, modicon]);
|
||||||
|
|
||||||
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]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -206,7 +206,7 @@ export class CoreGradesCoursePage implements AfterViewInit, OnDestroy {
|
||||||
*/
|
*/
|
||||||
private async fetchGrades(): Promise<void> {
|
private async fetchGrades(): Promise<void> {
|
||||||
const table = await CoreGrades.getCourseGradesTable(this.courseId, this.userId);
|
const table = await CoreGrades.getCourseGradesTable(this.courseId, this.userId);
|
||||||
const formattedTable = CoreGradesHelper.formatGradesTable(table);
|
const formattedTable = await CoreGradesHelper.formatGradesTable(table);
|
||||||
|
|
||||||
this.title = formattedTable.rows[0]?.gradeitem ?? Translate.instant('core.grades.grades');
|
this.title = formattedTable.rows[0]?.gradeitem ?? Translate.instant('core.grades.grades');
|
||||||
this.columns = formattedTable.columns;
|
this.columns = formattedTable.columns;
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@ import { makeSingleton, Translate } from '@singletons';
|
||||||
import { CoreError } from '@classes/errors/error';
|
import { CoreError } from '@classes/errors/error';
|
||||||
import { CoreCourseHelper } from '@features/course/services/course-helper';
|
import { CoreCourseHelper } from '@features/course/services/course-helper';
|
||||||
import { CoreAppProvider } from '@services/app';
|
import { CoreAppProvider } from '@services/app';
|
||||||
|
import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate';
|
||||||
|
|
||||||
export const GRADES_PAGE_NAME = 'grades';
|
export const GRADES_PAGE_NAME = 'grades';
|
||||||
|
|
||||||
|
@ -73,7 +74,7 @@ export class CoreGradesHelperProvider {
|
||||||
let content = String(column.content);
|
let content = String(column.content);
|
||||||
|
|
||||||
if (name == 'itemname') {
|
if (name == 'itemname') {
|
||||||
this.setRowIconAndType(row, content);
|
await this.setRowIconAndType(row, content);
|
||||||
|
|
||||||
row.link = this.getModuleLink(content);
|
row.link = this.getModuleLink(content);
|
||||||
row.rowclass += column.class.indexOf('hidden') >= 0 ? ' hidden' : '';
|
row.rowclass += column.class.indexOf('hidden') >= 0 ? ' hidden' : '';
|
||||||
|
@ -102,7 +103,10 @@ export class CoreGradesHelperProvider {
|
||||||
* @param useLegacyLayout Whether to use the layout before 4.1.
|
* @param useLegacyLayout Whether to use the layout before 4.1.
|
||||||
* @returns Formatted row object.
|
* @returns Formatted row object.
|
||||||
*/
|
*/
|
||||||
protected formatGradeRowForTable(tableRow: CoreGradesTableRow, useLegacyLayout: boolean): CoreGradesFormattedTableRow {
|
protected async formatGradeRowForTable(
|
||||||
|
tableRow: CoreGradesTableRow,
|
||||||
|
useLegacyLayout: boolean,
|
||||||
|
): Promise<CoreGradesFormattedTableRow> {
|
||||||
const row: CoreGradesFormattedTableRow = {};
|
const row: CoreGradesFormattedTableRow = {};
|
||||||
|
|
||||||
if (!useLegacyLayout && 'leader' in tableRow) {
|
if (!useLegacyLayout && 'leader' in tableRow) {
|
||||||
|
@ -132,7 +136,7 @@ export class CoreGradesHelperProvider {
|
||||||
row.colspan = itemNameColumn.colspan;
|
row.colspan = itemNameColumn.colspan;
|
||||||
row.rowspan = tableRow.leader?.rowspan || 1;
|
row.rowspan = tableRow.leader?.rowspan || 1;
|
||||||
|
|
||||||
this.setRowIconAndType(row, content);
|
await this.setRowIconAndType(row, content);
|
||||||
this.setRowStyleClasses(row, itemNameColumn.class);
|
this.setRowStyleClasses(row, itemNameColumn.class);
|
||||||
row.rowclass += itemNameColumn.class.indexOf('hidden') >= 0 ? ' hidden' : '';
|
row.rowclass += itemNameColumn.class.indexOf('hidden') >= 0 ? ' hidden' : '';
|
||||||
row.rowclass += itemNameColumn.class.indexOf('dimmed_text') >= 0 ? ' dimmed_text' : '';
|
row.rowclass += itemNameColumn.class.indexOf('dimmed_text') >= 0 ? ' dimmed_text' : '';
|
||||||
|
@ -203,7 +207,7 @@ export class CoreGradesHelperProvider {
|
||||||
* @param table JSON object representing a table with data.
|
* @param table JSON object representing a table with data.
|
||||||
* @returns Formatted HTML table.
|
* @returns Formatted HTML table.
|
||||||
*/
|
*/
|
||||||
formatGradesTable(table: CoreGradesTable): CoreGradesFormattedTable {
|
async formatGradesTable(table: CoreGradesTable): Promise<CoreGradesFormattedTable> {
|
||||||
const maxDepth = table.maxdepth;
|
const maxDepth = table.maxdepth;
|
||||||
const formatted: CoreGradesFormattedTable = {
|
const formatted: CoreGradesFormattedTable = {
|
||||||
columns: [],
|
columns: [],
|
||||||
|
@ -223,7 +227,7 @@ export class CoreGradesHelperProvider {
|
||||||
feedback: false,
|
feedback: false,
|
||||||
contributiontocoursetotal: false,
|
contributiontocoursetotal: false,
|
||||||
};
|
};
|
||||||
formatted.rows = this.formatGradesTableRows(table.tabledata);
|
formatted.rows = await this.formatGradesTableRows(table.tabledata);
|
||||||
|
|
||||||
// Get a row with some info.
|
// Get a row with some info.
|
||||||
let normalRow = formatted.rows.find(
|
let normalRow = formatted.rows.find(
|
||||||
|
@ -261,9 +265,9 @@ export class CoreGradesHelperProvider {
|
||||||
* @param rows Unformatted rows.
|
* @param rows Unformatted rows.
|
||||||
* @returns Formatted rows.
|
* @returns Formatted rows.
|
||||||
*/
|
*/
|
||||||
protected formatGradesTableRows(rows: CoreGradesTableRow[]): CoreGradesFormattedTableRow[] {
|
protected async formatGradesTableRows(rows: CoreGradesTableRow[]): Promise<CoreGradesFormattedTableRow[]> {
|
||||||
const useLegacyLayout = !CoreSites.getRequiredCurrentSite().isVersionGreaterEqualThan('4.1');
|
const useLegacyLayout = !CoreSites.getRequiredCurrentSite().isVersionGreaterEqualThan('4.1');
|
||||||
const formattedRows = rows.map(row => this.formatGradeRowForTable(row, useLegacyLayout));
|
const formattedRows = await Promise.all(rows.map(row => this.formatGradeRowForTable(row, useLegacyLayout)));
|
||||||
|
|
||||||
if (!useLegacyLayout) {
|
if (!useLegacyLayout) {
|
||||||
for (let index = 0; index < formattedRows.length - 1; index++) {
|
for (let index = 0; index < formattedRows.length - 1; index++) {
|
||||||
|
@ -652,7 +656,7 @@ export class CoreGradesHelperProvider {
|
||||||
* @param row Row.
|
* @param row Row.
|
||||||
* @param text Row content.
|
* @param text Row content.
|
||||||
*/
|
*/
|
||||||
protected setRowIconAndType(row: CoreGradesFormattedRowCommonData, text: string): void {
|
protected async setRowIconAndType(row: CoreGradesFormattedRowCommonData, text: string): Promise<void> {
|
||||||
text = text.replace('%2F', '/').replace('%2f', '/');
|
text = text.replace('%2F', '/').replace('%2f', '/');
|
||||||
if (text.indexOf('/agg_mean') > -1) {
|
if (text.indexOf('/agg_mean') > -1) {
|
||||||
row.itemtype = 'agg_mean';
|
row.itemtype = 'agg_mean';
|
||||||
|
@ -680,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 = CoreCourse.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) {
|
||||||
|
@ -800,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;
|
||||||
|
|
Loading…
Reference in New Issue