MOBILE-3970 course: Open course summary in a lateral modal
parent
69d36803fc
commit
19b3a394d2
|
@ -19,6 +19,7 @@ import { resolveModuleRoutes } from '@/app/app-routing.module';
|
||||||
import { CoreSharedModule } from '@/core/shared.module';
|
import { CoreSharedModule } from '@/core/shared.module';
|
||||||
import { CoreCourseIndexPage } from '.';
|
import { CoreCourseIndexPage } from '.';
|
||||||
import { COURSE_INDEX_ROUTES } from './index-routing.module';
|
import { COURSE_INDEX_ROUTES } from './index-routing.module';
|
||||||
|
import { CoreCoursePreviewPageComponentModule } from '../preview/preview.module';
|
||||||
|
|
||||||
function buildRoutes(injector: Injector): Routes {
|
function buildRoutes(injector: Injector): Routes {
|
||||||
const routes = resolveModuleRoutes(injector, COURSE_INDEX_ROUTES);
|
const routes = resolveModuleRoutes(injector, COURSE_INDEX_ROUTES);
|
||||||
|
@ -42,6 +43,7 @@ function buildRoutes(injector: Injector): Routes {
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
CoreSharedModule,
|
CoreSharedModule,
|
||||||
|
CoreCoursePreviewPageComponentModule,
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
CoreCourseIndexPage,
|
CoreCourseIndexPage,
|
||||||
|
|
|
@ -28,6 +28,7 @@ import { CoreNavigator } from '@services/navigator';
|
||||||
import { CONTENTS_PAGE_NAME } from '@features/course/course.module';
|
import { CONTENTS_PAGE_NAME } from '@features/course/course.module';
|
||||||
import { CoreDomUtils } from '@services/utils/dom';
|
import { CoreDomUtils } from '@services/utils/dom';
|
||||||
import { CoreCollapsibleHeaderDirective } from '@directives/collapsible-header';
|
import { CoreCollapsibleHeaderDirective } from '@directives/collapsible-header';
|
||||||
|
import { CoreCoursePreviewPage } from '../preview/preview.page';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Page that displays the list of courses the user is enrolled in.
|
* Page that displays the list of courses the user is enrolled in.
|
||||||
|
@ -279,10 +280,13 @@ export class CoreCourseIndexPage implements OnInit, OnDestroy {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CoreNavigator.navigateToSitePath(
|
CoreDomUtils.openSideModal<void>({
|
||||||
`/course/${this.course.id}/preview`,
|
component: CoreCoursePreviewPage,
|
||||||
{ params: { course: this.course, avoidOpenCourse: true } },
|
componentProps: {
|
||||||
);
|
courseId: this.course.id,
|
||||||
|
course: this.course,
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -8,6 +8,11 @@
|
||||||
{{'core.course.coursesummary' | translate}}
|
{{'core.course.coursesummary' | translate}}
|
||||||
</h1>
|
</h1>
|
||||||
</ion-title>
|
</ion-title>
|
||||||
|
<ion-buttons slot="end" *ngIf="avoidOpenCourse">
|
||||||
|
<ion-button fill="clear" (click)="closeModal()" [attr.aria-label]="'core.close' | translate">
|
||||||
|
<ion-icon slot="icon-only" name="fas-times" aria-hidden="true"></ion-icon>
|
||||||
|
</ion-button>
|
||||||
|
</ion-buttons>
|
||||||
</ion-toolbar>
|
</ion-toolbar>
|
||||||
</ion-header>
|
</ion-header>
|
||||||
<ion-content>
|
<ion-content>
|
||||||
|
@ -15,12 +20,10 @@
|
||||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||||
</ion-refresher>
|
</ion-refresher>
|
||||||
<core-loading [hideUntil]="dataLoaded">
|
<core-loading [hideUntil]="dataLoaded">
|
||||||
<div *ngIf="courseImageUrl" class="core-course-thumb-parallax">
|
<div *ngIf="courseImageUrl" class="core-course-thumb">
|
||||||
<div class="core-course-thumb">
|
|
||||||
<img [src]="courseImageUrl" core-external-content alt="" />
|
<img [src]="courseImageUrl" core-external-content alt="" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div *ngIf="course">
|
||||||
<div class="core-course-thumb-parallax-content" *ngIf="course">
|
|
||||||
<ion-item class="ion-text-wrap">
|
<ion-item class="ion-text-wrap">
|
||||||
<ion-label>
|
<ion-label>
|
||||||
<p *ngIf="course.categoryname">
|
<p *ngIf="course.categoryname">
|
||||||
|
@ -40,6 +43,10 @@
|
||||||
</core-progress-bar>
|
</core-progress-bar>
|
||||||
</div>
|
</div>
|
||||||
</ion-label>
|
</ion-label>
|
||||||
|
<ion-button fill="clear" [href]="courseUrl" core-link [showBrowserWarning]="false" color="dark"
|
||||||
|
[attr.aria-label]="'core.openinbrowser' | translate" slot="end">
|
||||||
|
<ion-icon name="fas-external-link-alt" slot="start" aria-hidden="true"></ion-icon>
|
||||||
|
</ion-button>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
|
|
||||||
<ion-item class="ion-text-wrap" *ngIf="course.summary" detail="false">
|
<ion-item class="ion-text-wrap" *ngIf="course.summary" detail="false">
|
||||||
|
@ -87,7 +94,11 @@
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ion-label>
|
</ion-label>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
|
</div>
|
||||||
|
</core-loading>
|
||||||
|
</ion-content>
|
||||||
|
|
||||||
|
<ion-footer>
|
||||||
<!-- Enrol -->
|
<!-- Enrol -->
|
||||||
<ng-container *ngIf="!isEnrolled">
|
<ng-container *ngIf="!isEnrolled">
|
||||||
<ion-item class="ion-text-wrap" *ngFor="let instance of selfEnrolInstances">
|
<ion-item class="ion-text-wrap" *ngFor="let instance of selfEnrolInstances">
|
||||||
|
@ -115,8 +126,8 @@
|
||||||
|
|
||||||
<ion-button class="ion-margin" *ngIf="canAccessCourse && downloadCourseEnabled" (click)="prefetchCourse()" expand="block"
|
<ion-button class="ion-margin" *ngIf="canAccessCourse && downloadCourseEnabled" (click)="prefetchCourse()" expand="block"
|
||||||
[attr.aria-label]="prefetchCourseData.statusTranslatable | translate">
|
[attr.aria-label]="prefetchCourseData.statusTranslatable | translate">
|
||||||
<ion-icon *ngIf="(prefetchCourseData.status != statusDownloaded) && !prefetchCourseData.loading"
|
<ion-icon *ngIf="(prefetchCourseData.status != statusDownloaded) && !prefetchCourseData.loading" [name]="prefetchCourseData.icon"
|
||||||
[name]="prefetchCourseData.icon" slot="start" aria-hidden="true">
|
slot="start" aria-hidden="true">
|
||||||
</ion-icon>
|
</ion-icon>
|
||||||
<ion-icon *ngIf="(prefetchCourseData.status == statusDownloaded) && !prefetchCourseData.loading" slot="start"
|
<ion-icon *ngIf="(prefetchCourseData.status == statusDownloaded) && !prefetchCourseData.loading" slot="start"
|
||||||
[name]="prefetchCourseData.icon" color="success" aria-hidden="true" role="status">
|
[name]="prefetchCourseData.icon" color="success" aria-hidden="true" role="status">
|
||||||
|
@ -132,14 +143,4 @@
|
||||||
{{ 'core.course' | translate }}
|
{{ 'core.course' | translate }}
|
||||||
</ion-label>
|
</ion-label>
|
||||||
</ion-button>
|
</ion-button>
|
||||||
|
</ion-footer>
|
||||||
<ion-button class="ion-margin" [href]="courseUrl" core-link [showBrowserWarning]="false" expand="block">
|
|
||||||
<ion-icon name="fas-external-link-alt" slot="start" aria-hidden="true"></ion-icon>
|
|
||||||
<ion-label>
|
|
||||||
{{ 'core.openinbrowser' | translate }}
|
|
||||||
</ion-label>
|
|
||||||
</ion-button>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</core-loading>
|
|
||||||
</ion-content>
|
|
||||||
|
|
|
@ -24,15 +24,22 @@ const routes: Routes = [
|
||||||
component: CoreCoursePreviewPage,
|
component: CoreCoursePreviewPage,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
RouterModule.forChild(routes),
|
|
||||||
CoreSharedModule,
|
CoreSharedModule,
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
CoreCoursePreviewPage,
|
CoreCoursePreviewPage,
|
||||||
],
|
],
|
||||||
|
})
|
||||||
|
export class CoreCoursePreviewPageComponentModule { }
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
RouterModule.forChild(routes),
|
||||||
|
CoreSharedModule,
|
||||||
|
CoreCoursePreviewPageComponentModule,
|
||||||
|
],
|
||||||
exports: [RouterModule],
|
exports: [RouterModule],
|
||||||
})
|
})
|
||||||
export class CoreCoursePreviewPageModule { }
|
export class CoreCoursePreviewPageModule { }
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
import { Component, OnDestroy, OnInit, Input } from '@angular/core';
|
||||||
import { IonRefresher } from '@ionic/angular';
|
import { IonRefresher } from '@ionic/angular';
|
||||||
import { CoreEventObserver, CoreEvents } from '@singletons/events';
|
import { CoreEventObserver, CoreEvents } from '@singletons/events';
|
||||||
import { CoreSites } from '@services/sites';
|
import { CoreSites } from '@services/sites';
|
||||||
|
@ -29,7 +29,7 @@ import {
|
||||||
import { CoreCourseOptionsDelegate } from '@features/course/services/course-options-delegate';
|
import { CoreCourseOptionsDelegate } from '@features/course/services/course-options-delegate';
|
||||||
import { CoreCourse, CoreCourseProvider } from '@features/course/services/course';
|
import { CoreCourse, CoreCourseProvider } from '@features/course/services/course';
|
||||||
import { CoreCourseHelper, CorePrefetchStatusInfo } from '@features/course/services/course-helper';
|
import { CoreCourseHelper, CorePrefetchStatusInfo } from '@features/course/services/course-helper';
|
||||||
import { NgZone, Platform, Translate } from '@singletons';
|
import { ModalController, NgZone, Platform, Translate } from '@singletons';
|
||||||
import { CoreConstants } from '@/core/constants';
|
import { CoreConstants } from '@/core/constants';
|
||||||
import { CoreCoursesSelfEnrolPasswordComponent } from '../../../courses/components/self-enrol-password/self-enrol-password';
|
import { CoreCoursesSelfEnrolPasswordComponent } from '../../../courses/components/self-enrol-password/self-enrol-password';
|
||||||
import { CoreNavigator } from '@services/navigator';
|
import { CoreNavigator } from '@services/navigator';
|
||||||
|
@ -47,7 +47,9 @@ import { Subscription } from 'rxjs';
|
||||||
})
|
})
|
||||||
export class CoreCoursePreviewPage implements OnInit, OnDestroy {
|
export class CoreCoursePreviewPage implements OnInit, OnDestroy {
|
||||||
|
|
||||||
course?: CoreCourseSummaryData;
|
@Input() course?: CoreCourseSummaryData;
|
||||||
|
@Input() courseId = 0;
|
||||||
|
|
||||||
isEnrolled = false;
|
isEnrolled = false;
|
||||||
canAccessCourse = true;
|
canAccessCourse = true;
|
||||||
selfEnrolInstances: CoreCourseEnrolmentMethod[] = [];
|
selfEnrolInstances: CoreCourseEnrolmentMethod[] = [];
|
||||||
|
@ -76,7 +78,6 @@ export class CoreCoursePreviewPage implements OnInit, OnDestroy {
|
||||||
protected enrolUrl = '';
|
protected enrolUrl = '';
|
||||||
protected pageDestroyed = false;
|
protected pageDestroyed = false;
|
||||||
protected courseStatusObserver?: CoreEventObserver;
|
protected courseStatusObserver?: CoreEventObserver;
|
||||||
protected courseId!: number;
|
|
||||||
protected appResumeSubscription: Subscription;
|
protected appResumeSubscription: Subscription;
|
||||||
protected waitingForBrowserEnrol = false;
|
protected waitingForBrowserEnrol = false;
|
||||||
|
|
||||||
|
@ -111,17 +112,23 @@ export class CoreCoursePreviewPage implements OnInit, OnDestroy {
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
async ngOnInit(): Promise<void> {
|
async ngOnInit(): Promise<void> {
|
||||||
|
if (!this.courseId) {
|
||||||
|
// Opened as a page.
|
||||||
try {
|
try {
|
||||||
this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId');
|
this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
CoreDomUtils.showErrorModal(error);
|
CoreDomUtils.showErrorModal(error);
|
||||||
CoreNavigator.back();
|
CoreNavigator.back();
|
||||||
|
this.closeModal(); // Just in case.
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.avoidOpenCourse = !!CoreNavigator.getRouteBooleanParam('avoidOpenCourse');
|
|
||||||
this.course = CoreNavigator.getRouteParam('course');
|
this.course = CoreNavigator.getRouteParam('course');
|
||||||
|
} else {
|
||||||
|
// Opened as a modal.
|
||||||
|
this.avoidOpenCourse = true;
|
||||||
|
}
|
||||||
|
|
||||||
const currentSiteUrl = CoreSites.getRequiredCurrentSite().getURL();
|
const currentSiteUrl = CoreSites.getRequiredCurrentSite().getURL();
|
||||||
this.enrolUrl = CoreTextUtils.concatenatePaths(currentSiteUrl, 'enrol/index.php?id=' + this.courseId);
|
this.enrolUrl = CoreTextUtils.concatenatePaths(currentSiteUrl, 'enrol/index.php?id=' + this.courseId);
|
||||||
|
@ -452,6 +459,13 @@ export class CoreCoursePreviewPage implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the modal.
|
||||||
|
*/
|
||||||
|
closeModal(): void {
|
||||||
|
ModalController.dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prefetch the course.
|
* Prefetch the course.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,43 +1,12 @@
|
||||||
:host {
|
:host {
|
||||||
ion-content:not(.animating) {
|
|
||||||
&::part(scroll) {
|
|
||||||
perspective: 1px;
|
|
||||||
perspective-origin: center top;
|
|
||||||
transform-style: preserve-3d;
|
|
||||||
}
|
|
||||||
|
|
||||||
.core-course-thumb {
|
.core-course-thumb {
|
||||||
transform-origin: center top;
|
overflow: hidden;
|
||||||
|
text-align: center;
|
||||||
--scroll-factor: 0.5;
|
|
||||||
--translate-z: calc(-2 * var(--scroll-factor))px;
|
|
||||||
--scale: calc(1 + var(--scroll-factor) * 2);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculated with scroll-factor: 0.5;
|
|
||||||
* translate-z: -2 * $scroll-factor px;
|
|
||||||
* scale: 1 + $scroll-factor * 2;
|
|
||||||
*/
|
|
||||||
transform: translateZ(-1px) scale(2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.core-course-thumb-parallax-content {
|
|
||||||
transform: translateZ(0);
|
|
||||||
-webkit-filter: drop-shadow(0px -3px 3px rgba(var(--drop-shadow)));
|
|
||||||
filter: drop-shadow(0px -3px 3px rgba(var(--drop-shadow)));
|
|
||||||
}
|
|
||||||
|
|
||||||
.core-course-thumb-parallax {
|
|
||||||
height: 40vw;
|
height: 40vw;
|
||||||
max-height: 35vh;
|
max-height: 35vh;
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
border-bottom: 1px solid var(--stroke);
|
||||||
|
|
||||||
.core-course-thumb {
|
|
||||||
overflow: hidden;
|
|
||||||
text-align: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -58,7 +58,6 @@ export class CoreMainMenuUserButtonComponent implements OnInit {
|
||||||
|
|
||||||
CoreDomUtils.openSideModal<void>({
|
CoreDomUtils.openSideModal<void>({
|
||||||
component: CoreMainMenuUserMenuComponent,
|
component: CoreMainMenuUserMenuComponent,
|
||||||
cssClass: 'core-modal-lateral',
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue