MOBILE-2406 courses: Add course image

main
Pau Ferrer Ocaña 2018-06-05 12:30:33 +02:00
parent 0ec3f9fcab
commit c6956250fa
14 changed files with 136 additions and 10 deletions

View File

@ -29,7 +29,7 @@
<ion-card *ngIf="user"> <ion-card *ngIf="user">
<ion-item text-wrap> <ion-item text-wrap>
<ion-avatar *ngIf="user.profileimageurl && user.profileimageurl !== true" item-start> <ion-avatar *ngIf="user.profileimageurl && user.profileimageurl !== true" item-start>
<img [src]="user.profileimageurl" [alt]="'core.pictureof' | translate:{$a: user.fullname}" core-external-content> <img [src]="user.profileimageurl" [alt]="'core.pictureof' | translate:{$a: user.fullname}" core-external-content>
</ion-avatar> </ion-avatar>
<span *ngIf="user.profileimageurl === true" item-start> <span *ngIf="user.profileimageurl === true" item-start>
<ion-icon name="person"></ion-icon> <ion-icon name="person"></ion-icon>

View File

@ -11,6 +11,7 @@ core-empty-box {
margin: 0; margin: 0;
padding: 0; padding: 0;
clear: both; clear: both;
pointer-events: none;
.core-empty-box-content { .core-empty-box-content {
color: $black; color: $black;

View File

@ -3,6 +3,9 @@
<!-- Course summary. By default we only display the course progress. --> <!-- Course summary. By default we only display the course progress. -->
<core-dynamic-component [component]="courseSummaryComponent" [data]="data"> <core-dynamic-component [component]="courseSummaryComponent" [data]="data">
<ion-list no-lines> <ion-list no-lines>
<div *ngIf="course.imageThumb" class="core-course-thumb">
<img [src]="course.imageThumb" core-external-content alt=""/>
</div>
<ion-item *ngIf="course.progress != null && course.progress >= 0"> <ion-item *ngIf="course.progress != null && course.progress >= 0">
<core-progress-bar [progress]="course.progress"></core-progress-bar> <core-progress-bar [progress]="course.progress"></core-progress-bar>
</ion-item> </ion-item>

View File

@ -3,4 +3,23 @@ ion-badge.core-course-download-section-progress {
float: left; float: left;
margin-top: 12px; margin-top: 12px;
margin-right: 12px; margin-right: 12px;
}
core-course-format {
.core-course-thumb {
height: 150px;
width: 100%;
overflow: hidden;
cursor: pointer;
pointer-events: auto;
position: relative;
img {
position: absolute;
top: 0;
bottom: 0;
margin: auto;
width: 100%;
}
}
} }

View File

@ -252,7 +252,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
/** /**
* Calculate the status of sections. * Calculate the status of sections.
* *
* @param {boolean} refresh [description] * @param {boolean} refresh If refresh or not.
*/ */
protected calculateSectionsStatus(refresh?: boolean): void { protected calculateSectionsStatus(refresh?: boolean): void {
this.courseHelper.calculateSectionsStatus(this.sections, this.course.id, refresh).catch(() => { this.courseHelper.calculateSectionsStatus(this.sections, this.course.id, refresh).catch(() => {

View File

@ -204,6 +204,19 @@ export class CoreCourseSectionPage implements OnDestroy {
}); });
})); }));
// Get the overview files.
if (this.course.overviewfiles) {
this.course.imageThumb = this.course.overviewfiles[0] && this.course.overviewfiles[0].fileurl;
} else {
promises.push(this.coursesProvider.getCoursesByField('id', this.course.id).then((coursesInfo) => {
if (coursesInfo[0] && coursesInfo[0].overviewfiles && coursesInfo[0].overviewfiles[0]) {
this.course.imageThumb = coursesInfo[0].overviewfiles[0].fileurl;
} else {
this.course.imageThumb = false;
}
}));
}
// Load the course handlers. // Load the course handlers.
promises.push(this.courseOptionsDelegate.getHandlersToDisplay(this.injector, this.course, refresh, false) promises.push(this.courseOptionsDelegate.getHandlersToDisplay(this.injector, this.course, refresh, false)
.then((handlers) => { .then((handlers) => {
@ -265,6 +278,7 @@ export class CoreCourseSectionPage implements OnDestroy {
promises.push(this.courseProvider.invalidateSections(this.course.id)); promises.push(this.courseProvider.invalidateSections(this.course.id));
promises.push(this.coursesProvider.invalidateUserCourses()); promises.push(this.coursesProvider.invalidateUserCourses());
promises.push(this.courseFormatDelegate.invalidateData(this.course, this.sections)); promises.push(this.courseFormatDelegate.invalidateData(this.course, this.sections));
promises.push(this.coursesProvider.invalidateCoursesByField('id', this.course.id));
if (this.sections) { if (this.sections) {
promises.push(this.prefetchDelegate.invalidateCourseUpdates(this.course.id)); promises.push(this.prefetchDelegate.invalidateCourseUpdates(this.course.id));

View File

@ -1,4 +1,7 @@
<ion-card> <ion-card>
<div (click)="openCourse(course)" class="core-course-thumb core-course-color-{{course.id % 10}}">
<img *ngIf="course.imageThumb" [src]="course.imageThumb" core-external-content alt=""/>
</div>
<ion-item tappable text-wrap detail-none (click)="openCourse(course)" [title]="course.fullname" class="core-course-link"> <ion-item tappable text-wrap detail-none (click)="openCourse(course)" [title]="course.fullname" class="core-course-link">
<h2><core-format-text [text]="course.fullname"></core-format-text></h2> <h2><core-format-text [text]="course.fullname"></core-format-text></h2>

View File

@ -2,9 +2,32 @@ core-courses-course-progress {
ion-card.card { ion-card.card {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: space-between;
.core-course-thumb {
height: 150px;
width: 100%;
overflow: hidden;
cursor: pointer;
pointer-events: auto;
position: relative;
@for $i from 0 to length($core-course-image-background) {
&.core-course-color-#{$i} {
background: nth($core-course-image-background, $i + 1);
}
}
img {
position: absolute;
top: 0;
bottom: 0;
margin: auto;
}
}
.core-course-link { .core-course-link {
padding-top: 8px;
padding-bottom: 8px;
.item-inner { .item-inner {
padding-right: 0; padding-right: 0;
} }

View File

@ -10,6 +10,9 @@
<core-loading [hideUntil]="dataLoaded"> <core-loading [hideUntil]="dataLoaded">
<ion-list *ngIf="course"> <ion-list *ngIf="course">
<div *ngIf="course.imageThumb" (click)="openCourse()" class="core-course-thumb">
<img [src]="course.imageThumb" core-external-content alt=""/>
</div>
<a ion-item text-wrap (click)="openCourse()" [title]="course.fullname" [attr.detail-none]="!canAccessCourse"> <a ion-item text-wrap (click)="openCourse()" [title]="course.fullname" [attr.detail-none]="!canAccessCourse">
<ion-icon name="ionic" item-start></ion-icon> <ion-icon name="ionic" item-start></ion-icon>
<h2><core-format-text [text]="course.fullname"></core-format-text></h2> <h2><core-format-text [text]="course.fullname"></core-format-text></h2>

View File

@ -1,3 +1,18 @@
page-core-courses-course-preview { page-core-courses-course-preview {
.core-course-thumb {
height: 150px;
width: 100%;
overflow: hidden;
cursor: pointer;
pointer-events: auto;
position: relative;
img {
position: absolute;
top: 0;
bottom: 0;
margin: auto;
width: 100%;
}
}
} }

View File

@ -96,6 +96,9 @@ export class CoreCoursesCoursePreviewPage implements OnDestroy {
this.enrolUrl = this.textUtils.concatenatePaths(currentSiteUrl, 'enrol/index.php?id=' + this.course.id); this.enrolUrl = this.textUtils.concatenatePaths(currentSiteUrl, 'enrol/index.php?id=' + this.course.id);
this.courseUrl = this.textUtils.concatenatePaths(currentSiteUrl, 'course/view.php?id=' + this.course.id); this.courseUrl = this.textUtils.concatenatePaths(currentSiteUrl, 'course/view.php?id=' + this.course.id);
this.paypalReturnUrl = this.textUtils.concatenatePaths(currentSiteUrl, 'enrol/paypal/return.php'); this.paypalReturnUrl = this.textUtils.concatenatePaths(currentSiteUrl, 'enrol/paypal/return.php');
if (this.course.overviewfiles && this.course.overviewfiles.length > 0) {
this.course.imageThumb = this.course.overviewfiles[0].fileurl;
}
// Initialize the self enrol modal. // Initialize the self enrol modal.
this.selfEnrolModal = this.modalCtrl.create('CoreCoursesSelfEnrolPasswordPage'); this.selfEnrolModal = this.modalCtrl.create('CoreCoursesSelfEnrolPasswordPage');

View File

@ -17,6 +17,7 @@ import { IonicPage, NavController } from 'ionic-angular';
import { CoreEventsProvider } from '@providers/events'; import { CoreEventsProvider } from '@providers/events';
import { CoreSitesProvider } from '@providers/sites'; import { CoreSitesProvider } from '@providers/sites';
import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreDomUtilsProvider } from '@providers/utils/dom';
import { CoreUtilsProvider } from '@providers/utils/utils';
import { CoreCoursesProvider } from '../../providers/courses'; import { CoreCoursesProvider } from '../../providers/courses';
import { CoreCourseHelperProvider } from '@core/course/providers/helper'; import { CoreCourseHelperProvider } from '@core/course/providers/helper';
import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate'; import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate';
@ -43,11 +44,12 @@ export class CoreCoursesMyCoursesPage implements OnDestroy {
protected myCoursesObserver; protected myCoursesObserver;
protected siteUpdatedObserver; protected siteUpdatedObserver;
protected isDestroyed = false; protected isDestroyed = false;
protected courseIds = '';
constructor(private navCtrl: NavController, private coursesProvider: CoreCoursesProvider, constructor(private navCtrl: NavController, private coursesProvider: CoreCoursesProvider,
private domUtils: CoreDomUtilsProvider, private eventsProvider: CoreEventsProvider, private domUtils: CoreDomUtilsProvider, private eventsProvider: CoreEventsProvider,
private sitesProvider: CoreSitesProvider, private courseHelper: CoreCourseHelperProvider, private sitesProvider: CoreSitesProvider, private courseHelper: CoreCourseHelperProvider,
private courseOptionsDelegate: CoreCourseOptionsDelegate) { } private courseOptionsDelegate: CoreCourseOptionsDelegate, private utils: CoreUtilsProvider) { }
/** /**
* View loaded. * View loaded.
@ -85,16 +87,33 @@ export class CoreCoursesMyCoursesPage implements OnDestroy {
*/ */
protected fetchCourses(): Promise<any> { protected fetchCourses(): Promise<any> {
return this.coursesProvider.getUserCourses().then((courses) => { return this.coursesProvider.getUserCourses().then((courses) => {
const promises = [],
const courseIds = courses.map((course) => { courseIds = courses.map((course) => {
return course.id; return course.id;
}); });
return this.coursesProvider.getCoursesAdminAndNavOptions(courseIds).then((options) => { this.courseIds = courseIds.join(',');
// Load course image of all the courses.
promises.push(this.coursesProvider.getCoursesByField('ids', this.courseIds).then((coursesInfo) => {
coursesInfo = this.utils.arrayToObject(coursesInfo, 'id');
courses.forEach((course) => {
if (coursesInfo[course.id] && coursesInfo[course.id].overviewfiles && coursesInfo[course.id].overviewfiles[0]) {
course.imageThumb = coursesInfo[course.id].overviewfiles[0].fileurl;
} else {
course.imageThumb = false;
}
});
}));
promises.push(this.coursesProvider.getCoursesAdminAndNavOptions(courseIds).then((options) => {
courses.forEach((course) => { courses.forEach((course) => {
course.navOptions = options.navOptions[course.id]; course.navOptions = options.navOptions[course.id];
course.admOptions = options.admOptions[course.id]; course.admOptions = options.admOptions[course.id];
}); });
}));
return Promise.all(promises).then(() => {
this.courses = courses; this.courses = courses;
this.filteredCourses = this.courses; this.filteredCourses = this.courses;
this.filter = ''; this.filter = '';
@ -116,6 +135,7 @@ export class CoreCoursesMyCoursesPage implements OnDestroy {
promises.push(this.coursesProvider.invalidateUserCourses()); promises.push(this.coursesProvider.invalidateUserCourses());
promises.push(this.courseOptionsDelegate.clearAndInvalidateCoursesOptions()); promises.push(this.courseOptionsDelegate.clearAndInvalidateCoursesOptions());
promises.push(this.coursesProvider.invalidateCoursesByField('ids', this.courseIds));
Promise.all(promises).finally(() => { Promise.all(promises).finally(() => {

View File

@ -17,6 +17,7 @@ import { IonicPage, NavController } from 'ionic-angular';
import { CoreEventsProvider } from '@providers/events'; import { CoreEventsProvider } from '@providers/events';
import { CoreSitesProvider } from '@providers/sites'; import { CoreSitesProvider } from '@providers/sites';
import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreDomUtilsProvider } from '@providers/utils/dom';
import { CoreUtilsProvider } from '@providers/utils/utils';
import { CoreCoursesProvider } from '../../providers/courses'; import { CoreCoursesProvider } from '../../providers/courses';
import { CoreCoursesMyOverviewProvider } from '../../providers/my-overview'; import { CoreCoursesMyOverviewProvider } from '../../providers/my-overview';
import { CoreCourseHelperProvider } from '@core/course/providers/helper'; import { CoreCourseHelperProvider } from '@core/course/providers/helper';
@ -70,12 +71,13 @@ export class CoreCoursesMyOverviewPage implements OnDestroy {
protected prefetchIconsInitialized = false; protected prefetchIconsInitialized = false;
protected isDestroyed; protected isDestroyed;
protected updateSiteObserver; protected updateSiteObserver;
protected courseIds = '';
constructor(private navCtrl: NavController, private coursesProvider: CoreCoursesProvider, constructor(private navCtrl: NavController, private coursesProvider: CoreCoursesProvider,
private domUtils: CoreDomUtilsProvider, private myOverviewProvider: CoreCoursesMyOverviewProvider, private domUtils: CoreDomUtilsProvider, private myOverviewProvider: CoreCoursesMyOverviewProvider,
private courseHelper: CoreCourseHelperProvider, private sitesProvider: CoreSitesProvider, private courseHelper: CoreCourseHelperProvider, private sitesProvider: CoreSitesProvider,
private siteHomeProvider: CoreSiteHomeProvider, private courseOptionsDelegate: CoreCourseOptionsDelegate, private siteHomeProvider: CoreSiteHomeProvider, private courseOptionsDelegate: CoreCourseOptionsDelegate,
private eventsProvider: CoreEventsProvider) { private eventsProvider: CoreEventsProvider, private utils: CoreUtilsProvider) {
} }
/** /**
@ -198,17 +200,34 @@ export class CoreCoursesMyOverviewPage implements OnDestroy {
*/ */
protected fetchUserCourses(): Promise<any> { protected fetchUserCourses(): Promise<any> {
return this.coursesProvider.getUserCourses().then((courses) => { return this.coursesProvider.getUserCourses().then((courses) => {
const courseIds = courses.map((course) => { const promises = [],
courseIds = courses.map((course) => {
return course.id; return course.id;
}); });
// Load course options of the course. // Load course options of the course.
return this.coursesProvider.getCoursesAdminAndNavOptions(courseIds).then((options) => { promises.push(this.coursesProvider.getCoursesAdminAndNavOptions(courseIds).then((options) => {
courses.forEach((course) => { courses.forEach((course) => {
course.navOptions = options.navOptions[course.id]; course.navOptions = options.navOptions[course.id];
course.admOptions = options.admOptions[course.id]; course.admOptions = options.admOptions[course.id];
}); });
}));
this.courseIds = courseIds.join(',');
// Load course image of all the courses.
promises.push(this.coursesProvider.getCoursesByField('ids', this.courseIds).then((coursesInfo) => {
coursesInfo = this.utils.arrayToObject(coursesInfo, 'id');
courses.forEach((course) => {
if (coursesInfo[course.id] && coursesInfo[course.id].overviewfiles && coursesInfo[course.id].overviewfiles[0]) {
course.imageThumb = coursesInfo[course.id].overviewfiles[0].fileurl;
} else {
course.imageThumb = false;
}
});
}));
return Promise.all(promises).then(() => {
return courses.sort((a, b) => { return courses.sort((a, b) => {
const compareA = a.fullname.toLowerCase(), const compareA = a.fullname.toLowerCase(),
compareB = b.fullname.toLowerCase(); compareB = b.fullname.toLowerCase();
@ -260,6 +279,7 @@ export class CoreCoursesMyOverviewPage implements OnDestroy {
promises.push(this.coursesProvider.invalidateUserCourses()); promises.push(this.coursesProvider.invalidateUserCourses());
promises.push(this.courseOptionsDelegate.clearAndInvalidateCoursesOptions()); promises.push(this.courseOptionsDelegate.clearAndInvalidateCoursesOptions());
promises.push(this.coursesProvider.invalidateCoursesByField('ids', this.courseIds));
return Promise.all(promises).finally(() => { return Promise.all(promises).finally(() => {
switch (this.tabShown) { switch (this.tabShown) {

View File

@ -50,6 +50,8 @@ $core-color: $orange;
$core-color-light: lighten($core-color, 10%); $core-color-light: lighten($core-color, 10%);
$core-color-dark: darken($core-color, 10%); $core-color-dark: darken($core-color, 10%);
$core-course-image-background: #81ecec, #74b9ff, #a29bfe, #dfe6e9, #00b894, #0984e3, #b2bec3, #fdcb6e, #fd79a8, #6c5ce7;
// Shared Variables // Shared Variables
// -------------------------------------------------- // --------------------------------------------------
// To customize the look and feel of this app, you can override // To customize the look and feel of this app, you can override