MOBILE-3806 myoverview: Add layout selector

main
Pau Ferrer Ocaña 2021-11-08 16:19:08 +01:00
parent 29f6d6cd39
commit 69584e361c
3 changed files with 122 additions and 54 deletions

View File

@ -4,20 +4,19 @@
</ion-label>
<div slot="end" class="flex-row">
<!-- Download all courses. -->
<div *ngIf="downloadCoursesEnabled && downloadEnabled && filteredCourses.length > 1 && !showFilter"
class="core-button-spinner">
<ion-button *ngIf="!prefetchCoursesData[selectedFilter].loading" fill="clear" color="dark" (click)="prefetchCourses()"
<div *ngIf="downloadCoursesEnabled && downloadEnabled && filteredCourses.length > 1 && !showFilter" class="core-button-spinner">
<ion-button *ngIf="!prefetchCoursesData[timeSelectorFilter].loading" fill="clear" color="dark" (click)="prefetchCourses()"
[attr.aria-label]="'core.courses.downloadcourses' | translate">
<ion-icon [name]="prefetchCoursesData[selectedFilter].icon" slot="icon-only" aria-hidden="true">
<ion-icon [name]="prefetchCoursesData[timeSelectorFilter].icon" slot="icon-only" aria-hidden="true">
</ion-icon>
</ion-button>
<ion-badge class="core-course-download-courses-progress" *ngIf="prefetchCoursesData[selectedFilter].badge"
role="progressbar" [attr.aria-valuemax]="prefetchCoursesData[selectedFilter].total"
[attr.aria-valuenow]="prefetchCoursesData[selectedFilter].count"
[attr.aria-valuetext]="prefetchCoursesData[selectedFilter].badgeA11yText">
{{prefetchCoursesData[selectedFilter].badge}}
<ion-badge class="core-course-download-courses-progress" *ngIf="prefetchCoursesData[timeSelectorFilter].badge"
role="progressbar" [attr.aria-valuemax]="prefetchCoursesData[timeSelectorFilter].total"
[attr.aria-valuenow]="prefetchCoursesData[timeSelectorFilter].count"
[attr.aria-valuetext]="prefetchCoursesData[timeSelectorFilter].badgeA11yText">
{{prefetchCoursesData[timeSelectorFilter].badge}}
</ion-badge>
<ion-spinner *ngIf="prefetchCoursesData[selectedFilter].loading" [attr.aria-label]="'core.loading' | translate">
<ion-spinner *ngIf="prefetchCoursesData[timeSelectorFilter].loading" [attr.aria-label]="'core.loading' | translate">
</ion-spinner>
</div>
<core-context-menu>
@ -40,9 +39,9 @@
</div>
</ion-item-divider>
<core-loading [hideUntil]="loaded" [fullscreen]="false">
<div class="safe-area-padding-horizontal" [hidden]="showFilter || !showSelectorFilter">
<div class="safe-area-padding-horizontal" [hidden]="showFilter || !showTimeSelectorFilter">
<!-- "Time" selector. -->
<core-combobox [label]="'core.show' | translate" [selection]="selectedFilter" (onChange)="selectedChanged($event)">
<core-combobox [label]="'core.show' | translate" [selection]="timeSelectorFilter" (onChange)="timeSelectorChanged($event)">
<ion-select-option class="ion-text-wrap" value="allincludinghidden" *ngIf="showFilters.allincludinghidden != 'hidden'">
{{ 'addon.block_myoverview.allincludinghidden' | translate }}
</ion-select-option>
@ -77,6 +76,16 @@
</core-combobox>
</div>
<div class="safe-area-padding-horizontal" [hidden]="showFilter || layouts.length <= 1">
<!-- "Layouts" selector. -->
<core-combobox [label]="'core.show' | translate" [selection]="selectedLayout" (onChange)="layoutChanged($event)">
<ng-container *ngFor="let layout of layouts">
<ion-select-option class="ion-text-wrap" [value]="layout">{{ 'addon.block_myoverview.'+layout | translate }}
</ion-select-option>
</ng-container>
</core-combobox>
</div>
<!-- Filter courses. -->
<ion-searchbar #searchbar *ngIf="showFilter" [(ngModel)]="courses.filter" (ionInput)="filterChanged($event)"
(ionCancel)="filterChanged($event)" [placeholder]="'core.courses.filtermycourses' | translate">
@ -88,7 +97,7 @@
<!-- List of courses. -->
<div class="safe-area-padding">
<ion-grid class="ion-no-padding">
<ion-grid class="ion-no-padding" [class.core-no-grid]="selectedLayout != 'card'">
<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"
size-xl="3">

View File

@ -59,54 +59,54 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
value: string;
}[] = [];
selectedFilter = 'inprogress';
timeSelectorFilter = 'inprogress';
sort = 'fullname';
currentSite?: CoreSite;
filteredCourses: CoreEnrolledCourseDataWithOptions[] = [];
prefetchCoursesData = {
all: <CorePrefetchStatusInfo> {
prefetchCoursesData: Record<string, CorePrefetchStatusInfo> = {
all: {
icon: '',
statusTranslatable: 'core.loading',
status: '',
loading: true,
},
allincludinghidden: <CorePrefetchStatusInfo> {
allincludinghidden: {
icon: '',
statusTranslatable: 'core.loading',
status: '',
loading: true,
},
inprogress: <CorePrefetchStatusInfo> {
inprogress: {
icon: '',
statusTranslatable: 'core.loading',
status: '',
loading: true,
},
past: <CorePrefetchStatusInfo> {
past: {
icon: '',
statusTranslatable: 'core.loading',
status: '',
loading: true,
},
future: <CorePrefetchStatusInfo> {
future: {
icon: '',
statusTranslatable: 'core.loading',
status: '',
loading: true,
},
favourite: <CorePrefetchStatusInfo> {
favourite: {
icon: '',
statusTranslatable: 'core.loading',
status: '',
loading: true,
},
hidden: <CorePrefetchStatusInfo> {
hidden: {
icon: '',
statusTranslatable: 'core.loading',
status: '',
loading: true,
},
custom: <CorePrefetchStatusInfo> {
custom: {
icon: '',
statusTranslatable: '',
status: '',
@ -126,12 +126,15 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
};
showFilter = false;
showSelectorFilter = false;
showTimeSelectorFilter = false;
showSortFilter = false;
downloadCourseEnabled = false;
downloadCoursesEnabled = false;
showSortByShortName = false;
layouts: AddonBlockMyOverviewLayouts[] = [];
selectedLayout: AddonBlockMyOverviewLayouts = 'card';
protected prefetchIconsInitialized = false;
protected isDestroyed = false;
protected coursesObserver?: CoreEventObserver;
@ -169,21 +172,31 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
CoreSites.getCurrentSiteId(),
);
this.currentSite = CoreSites.getCurrentSite();
this.currentSite = CoreSites.getRequiredCurrentSite();
const promises: Promise<void>[] = [];
if (this.currentSite) {
promises.push(this.currentSite.getLocalSiteConfig('AddonBlockMyOverviewSort', this.sort).then((value) => {
this.sort = value;
promises.push(this.currentSite.getLocalSiteConfig('AddonBlockMyOverviewSort', this.sort).then((value) => {
this.sort = value;
return;
}));
promises.push(this.currentSite.getLocalSiteConfig('AddonBlockMyOverviewFilter', this.selectedFilter).then((value) => {
this.selectedFilter = value;
return;
}));
promises.push(this.currentSite.getLocalSiteConfig(
'AddonBlockMyOverviewFilter',
this.timeSelectorFilter,
).then((value) => {
this.timeSelectorFilter = value;
return;
}));
}
return;
}));
promises.push(this.currentSite.getLocalSiteConfig(
'AddonBlockMyOverviewLayout',
this.selectedLayout,
).then((value) => {
this.selectedLayout = value;
return;
}));
Promise.all(promises).finally(() => {
super.ngOnInit();
@ -229,6 +242,8 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
protected async fetchContent(refresh?: boolean): Promise<void> {
const config = this.block.configsRecord;
this.loadLayouts(config?.layouts?.value.split(','));
const showCategories = config?.displaycategories?.value == '1';
const courses = await CoreCoursesHelper.getUserCoursesWithOptions(this.sort, undefined, undefined, showCategories, {
@ -280,23 +295,23 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
this.courses.future.length === 0,
);
this.showSelectorFilter = courses.length > 0 && (this.courses.past.length > 0 || this.courses.future.length > 0 ||
this.showTimeSelectorFilter = courses.length > 0 && (this.courses.past.length > 0 || this.courses.future.length > 0 ||
typeof courses[0].enddate != 'undefined');
this.showFilters.hidden = this.getShowFilterValue(
this.showSelectorFilter && typeof courses[0].hidden != 'undefined' &&
this.showTimeSelectorFilter && typeof courses[0].hidden != 'undefined' &&
(!config || config.displaygroupinghidden?.value == '1'),
this.courses.hidden.length === 0,
);
this.showFilters.favourite = this.getShowFilterValue(
this.showSelectorFilter && typeof courses[0].isfavourite != 'undefined' &&
this.showTimeSelectorFilter && typeof courses[0].isfavourite != 'undefined' &&
(!config || config.displaygroupingstarred?.value == '1' || config.displaygroupingfavourites?.value == '1'),
this.courses.favourite.length === 0,
);
this.showFilters.custom = this.getShowFilterValue(
this.showSelectorFilter && config?.displaygroupingcustomfield?.value == '1' && !!config?.customfieldsexport?.value,
this.showTimeSelectorFilter && config?.displaygroupingcustomfield?.value == '1' && !!config?.customfieldsexport?.value,
false,
);
if (this.showFilters.custom == 'show') {
@ -305,25 +320,54 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
this.customFilter = [];
}
if (this.showSelectorFilter) {
if (this.showTimeSelectorFilter) {
// Check if any selector is shown and not disabled.
this.showSelectorFilter = Object.keys(this.showFilters).some((key) => this.showFilters[key] == 'show');
this.showTimeSelectorFilter = Object.keys(this.showFilters).some((key) => this.showFilters[key] == 'show');
if (!this.showSelectorFilter) {
if (!this.showTimeSelectorFilter) {
// All filters disabled, display all the courses.
this.showFilters.all = 'show';
}
}
if (!this.showSelectorFilter) {
if (!this.showTimeSelectorFilter) {
// No selector, display all the courses.
this.selectedFilter = 'all';
this.timeSelectorFilter = 'all';
}
this.setCourseFilter(this.selectedFilter);
this.setCourseFilter(this.timeSelectorFilter);
this.initPrefetchCoursesIcons();
}
/**
* Load block layouts.
*
* @param layouts Config available layouts.
*/
protected loadLayouts(layouts: string[] = []): void {
this.layouts = [];
layouts.forEach((layout) => {
if (layout == '') {
return;
}
const validLayout: AddonBlockMyOverviewLayouts = layout == 'summary' ? 'list' : layout as AddonBlockMyOverviewLayouts;
if (!this.layouts.includes(validLayout)) {
this.layouts.push(validLayout);
}
});
// If no layout is available use card.
if (this.layouts.length == 0) {
this.layouts = ['card'];
}
if (!this.layouts.includes(this.selectedLayout)) {
this.selectedLayout = this.layouts[0];
}
}
/**
* Helper function to help with filter values.
*
@ -417,7 +461,7 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
* @return Promise resolved when done.
*/
async prefetchCourses(): Promise<void> {
const selected = this.selectedFilter;
const selected = this.timeSelectorFilter;
const initialIcon = this.prefetchCoursesData[selected].icon;
try {
@ -442,13 +486,14 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
}
/**
* The selected courses filter have changed.
* The layout have changed.
*
* @param filter New filter
* @param layout New layout.
*/
selectedChanged(filter: string): void {
this.selectedFilter = filter;
this.setCourseFilter(this.selectedFilter);
layoutChanged(layout: AddonBlockMyOverviewLayouts): void {
this.selectedLayout = layout;
this.currentSite?.setLocalSiteConfig('AddonBlockMyOverviewLayout', layout);
}
/**
@ -457,7 +502,7 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
* @param filter Filter name to set.
*/
protected async setCourseFilter(filter: string): Promise<void> {
this.selectedFilter = filter;
this.timeSelectorFilter = filter;
if (this.showFilters.custom == 'show' && filter.startsWith('custom-') &&
typeof this.customFilter[filter.substr(7)] != 'undefined') {
@ -497,6 +542,16 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
}
}
/**
* The time selector courses filter have changed.
*
* @param filter New filter
*/
timeSelectorChanged(filter: string): void {
this.timeSelectorFilter = filter;
this.setCourseFilter(this.timeSelectorFilter);
}
/**
* Init courses filters.
*
@ -556,7 +611,7 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
}
});
this.setCourseFilter(this.selectedFilter);
this.setCourseFilter(this.timeSelectorFilter);
}
/**
@ -580,7 +635,7 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
if (this.showFilter) {
this.filteredCourses = this.courses.allincludinghidden;
} else {
this.setCourseFilter(this.selectedFilter);
this.setCourseFilter(this.timeSelectorFilter);
}
}
@ -614,3 +669,5 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
}
}
type AddonBlockMyOverviewLayouts = 'card'|'list';

View File

@ -1,11 +1,13 @@
{
"all": "All (except removed from view)",
"allincludinghidden": "All",
"card": "Card",
"favourites": "Starred",
"future": "Future",
"hiddencourses": "Removed from view",
"inprogress": "In progress",
"lastaccessed": "Last accessed",
"list": "List",
"nocourses": "No courses",
"past": "Past",
"pluginname": "Course overview",