MOBILE-3025 blocks: Show blocks on sidebar
parent
83d2f455ef
commit
cec6844968
|
@ -23,6 +23,12 @@ ion-app.app-root core-ion-tabs {
|
|||
background-color: $ion-tabs-badge-color;
|
||||
}
|
||||
|
||||
&[tabsplacement="bottom"] {
|
||||
.ion-page > ion-content > .scroll-content {
|
||||
margin-bottom: $navbar-md-height !important;
|
||||
}
|
||||
}
|
||||
|
||||
&[tabsplacement="side"] {
|
||||
.tabbar {
|
||||
@include float(start);
|
||||
|
@ -53,6 +59,10 @@ ion-app.app-root core-ion-tabs {
|
|||
position: relative;
|
||||
}
|
||||
}
|
||||
|
||||
.scroll-content, .fixed-content {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,9 +14,8 @@
|
|||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CoreBlockDelegate } from './providers/delegate';
|
||||
import { CoreBlockHelperProvider } from './providers/helper';
|
||||
import { CoreBlockDefaultHandler } from './providers/default-block-handler';
|
||||
import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate';
|
||||
import { CoreBlockCourseBlocksCourseOptionHandler } from './providers/course-option-handler';
|
||||
import { CoreBlockComponentsModule } from './components/components.module';
|
||||
|
||||
// List of providers (without handlers).
|
||||
|
@ -31,14 +30,10 @@ export const CORE_BLOCK_PROVIDERS: any[] = [
|
|||
],
|
||||
providers: [
|
||||
CoreBlockDelegate,
|
||||
CoreBlockDefaultHandler,
|
||||
CoreBlockCourseBlocksCourseOptionHandler
|
||||
CoreBlockHelperProvider,
|
||||
CoreBlockDefaultHandler
|
||||
],
|
||||
exports: []
|
||||
})
|
||||
export class CoreBlockModule {
|
||||
constructor(courseOptionHandler: CoreBlockCourseBlocksCourseOptionHandler,
|
||||
courseOptionsDelegate: CoreCourseOptionsDelegate) {
|
||||
courseOptionsDelegate.registerHandler(courseOptionHandler);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
<ion-content>
|
||||
<ion-refresher [enabled]="dataLoaded" (ionRefresh)="doRefresh($event)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
<div class="core-course-blocks-content">
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
|
||||
<ion-content *ngIf="blocks && blocks.length > 0" [class.core-hide-blocks]="hideBlocks" class="core-course-blocks-side">
|
||||
<core-loading [hideUntil]="dataLoaded" class="core-loading-center">
|
||||
<ion-list *ngIf="hasSupportedBlock">
|
||||
<ion-list>
|
||||
<!-- Course blocks. -->
|
||||
<ng-container *ngFor="let block of blocks">
|
||||
<core-block [block]="block" contextLevel="course" [instanceId]="courseId"></core-block>
|
||||
<core-block [block]="block" contextLevel="course" [instanceId]="courseId" [extraData]="{'downloadEnabled': downloadEnabled}"></core-block>
|
||||
</ng-container>
|
||||
</ion-list>
|
||||
|
||||
<core-empty-box *ngIf="!hasSupportedBlock" icon="qr-scanner" [message]="'core.course.nocontentavailable' | translate"></core-empty-box>
|
||||
</core-loading>
|
||||
</ion-content>
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
$core-side-blocks-max-width: 320px;
|
||||
$core-side-blocks-min-width: 30%;
|
||||
|
||||
.core-course-block-with-blocks > .scroll-content {
|
||||
overflow-y: visible;
|
||||
}
|
||||
|
||||
ion-app.app-root core-block-course-blocks {
|
||||
|
||||
&.core-no-blocks {
|
||||
.core-course-blocks-content > ion-content {
|
||||
height: auto;
|
||||
contain: content;
|
||||
|
||||
> .scroll-content {
|
||||
overflow-y: visible;
|
||||
position: relative;
|
||||
contain: content;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.core-has-blocks {
|
||||
@include media-breakpoint-up(md) {
|
||||
@include position(0, 0, 0, 0);
|
||||
|
||||
position: absolute;
|
||||
|
||||
display: flex;
|
||||
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
|
||||
contain: strict;
|
||||
|
||||
.core-course-blocks-content {
|
||||
min-width: calc(100% - #{($core-side-blocks-max-width)});
|
||||
max-width: calc(100% - #{($core-side-blocks-min-width)});
|
||||
z-index: 0;
|
||||
flex: 1;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
ion-content.core-course-blocks-side {
|
||||
transform: none !important;
|
||||
position: sticky;
|
||||
@include position(0, 0, 0, auto);
|
||||
z-index: 30;
|
||||
max-width: $core-side-blocks-max-width;
|
||||
min-width: $core-side-blocks-min-width;
|
||||
@include border-start(1px, solid, $list-md-border-color);
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(sm) {
|
||||
// Disable scroll on individual columns.
|
||||
.core-course-blocks-content > ion-content,
|
||||
ion-content.core-course-blocks-side {
|
||||
height: auto;
|
||||
contain: content;
|
||||
|
||||
&.core-hide-blocks {
|
||||
display: none;
|
||||
}
|
||||
|
||||
> .scroll-content {
|
||||
overflow-y: visible;
|
||||
position: relative;
|
||||
contain: content;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,11 +12,12 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component, ViewChildren, Input, OnInit, QueryList } from '@angular/core';
|
||||
import { Component, ViewChildren, Input, OnInit, QueryList, ElementRef, Optional } from '@angular/core';
|
||||
import { Content } from 'ionic-angular';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { CoreBlockComponent } from '../block/block';
|
||||
import { CoreBlockDelegate } from '../../providers/delegate';
|
||||
import { CoreCourseProvider } from '@core/course/providers/course';
|
||||
import { CoreBlockComponent } from '../block/block';
|
||||
import { CoreBlockHelperProvider } from '../../providers/helper';
|
||||
|
||||
/**
|
||||
* Component that displays the list of course blocks.
|
||||
|
@ -28,16 +29,22 @@ import { CoreCourseProvider } from '@core/course/providers/course';
|
|||
export class CoreBlockCourseBlocksComponent implements OnInit {
|
||||
|
||||
@Input() courseId: number;
|
||||
@Input() hideBlocks = false;
|
||||
@Input() downloadEnabled: boolean;
|
||||
|
||||
@ViewChildren(CoreBlockComponent) blocksComponents: QueryList<CoreBlockComponent>;
|
||||
|
||||
dataLoaded = false;
|
||||
hasContent: boolean;
|
||||
hasSupportedBlock: boolean;
|
||||
blocks = [];
|
||||
|
||||
protected element: HTMLElement;
|
||||
protected parentContent: HTMLElement;
|
||||
|
||||
constructor(private domUtils: CoreDomUtilsProvider, private courseProvider: CoreCourseProvider,
|
||||
private blockDelegate: CoreBlockDelegate) {
|
||||
protected blockHelper: CoreBlockHelperProvider, element: ElementRef,
|
||||
@Optional() content: Content) {
|
||||
this.element = element.nativeElement;
|
||||
this.parentContent = content.getElementRef().nativeElement;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -50,14 +57,14 @@ export class CoreBlockCourseBlocksComponent implements OnInit {
|
|||
}
|
||||
|
||||
/**
|
||||
* Refresh the data.
|
||||
* Invalidate blocks data.
|
||||
*
|
||||
* @param {any} refresher Refresher.
|
||||
* @return {Promise<any>} Promise resolved when done.
|
||||
*/
|
||||
doRefresh(refresher: any): void {
|
||||
invalidateBlocks(): Promise<any> {
|
||||
const promises = [];
|
||||
|
||||
if (this.courseProvider.canGetCourseBlocks()) {
|
||||
if (this.blockHelper.canGetCourseBlocks()) {
|
||||
promises.push(this.courseProvider.invalidateCourseBlocks(this.courseId));
|
||||
}
|
||||
|
||||
|
@ -68,11 +75,7 @@ export class CoreBlockCourseBlocksComponent implements OnInit {
|
|||
}));
|
||||
});
|
||||
|
||||
Promise.all(promises).finally(() => {
|
||||
this.loadContent().finally(() => {
|
||||
refresher.complete();
|
||||
});
|
||||
});
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -80,21 +83,24 @@ export class CoreBlockCourseBlocksComponent implements OnInit {
|
|||
*
|
||||
* @return {Promise<any>} Promise resolved when done.
|
||||
*/
|
||||
protected loadContent(): Promise<any> {
|
||||
// Get site home blocks.
|
||||
const canGetBlocks = this.courseProvider.canGetCourseBlocks(),
|
||||
promise = canGetBlocks ? this.courseProvider.getCourseBlocks(this.courseId) : Promise.reject(null);
|
||||
|
||||
return promise.then((blocks) => {
|
||||
loadContent(): Promise<any> {
|
||||
return this.blockHelper.getCourseBlocks(this.courseId).then((blocks) => {
|
||||
this.blocks = blocks;
|
||||
this.hasSupportedBlock = this.blockDelegate.hasSupportedBlock(blocks);
|
||||
|
||||
}).catch((error) => {
|
||||
if (canGetBlocks) {
|
||||
this.domUtils.showErrorModal(error);
|
||||
}
|
||||
this.blocks = [];
|
||||
});
|
||||
this.domUtils.showErrorModal(error);
|
||||
|
||||
this.blocks = [];
|
||||
}).finally(() => {
|
||||
if (this.blocks.length > 0) {
|
||||
this.element.classList.add('core-has-blocks');
|
||||
this.element.classList.remove('core-no-blocks');
|
||||
|
||||
this.parentContent.classList.add('core-course-block-with-blocks');
|
||||
} else {
|
||||
this.element.classList.remove('core-has-blocks');
|
||||
this.element.classList.add('core-no-blocks');
|
||||
this.parentContent.classList.remove('core-course-block-with-blocks');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
<ion-item-divider text-wrap detail-push (click)="gotoBlock($event)">
|
||||
<h2>{{ title | translate }}</h2>
|
||||
<h2><core-format-text [text]="title | translate"></core-format-text></h2>
|
||||
</ion-item-divider>
|
|
@ -1,91 +0,0 @@
|
|||
// (C) Copyright 2015 Martin Dougiamas
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Injectable, Injector } from '@angular/core';
|
||||
import { CoreCourseOptionsHandler, CoreCourseOptionsHandlerData } from '@core/course/providers/options-delegate';
|
||||
import { CoreCourseProvider } from '@core/course/providers/course';
|
||||
import { CoreBlockCourseBlocksComponent } from '../components/course-blocks/course-blocks';
|
||||
import { CoreBlockDelegate } from './delegate';
|
||||
|
||||
/**
|
||||
* Course nav handler.
|
||||
*/
|
||||
@Injectable()
|
||||
export class CoreBlockCourseBlocksCourseOptionHandler implements CoreCourseOptionsHandler {
|
||||
name = 'CoreCourseBlocks';
|
||||
priority = 700;
|
||||
|
||||
constructor(private courseProvider: CoreCourseProvider, private blockDelegate: CoreBlockDelegate) {}
|
||||
|
||||
/**
|
||||
* Should invalidate the data to determine if the handler is enabled for a certain course.
|
||||
*
|
||||
* @param {number} courseId The course ID.
|
||||
* @param {any} [navOptions] Course navigation options for current user. See CoreCoursesProvider.getUserNavigationOptions.
|
||||
* @param {any} [admOptions] Course admin options for current user. See CoreCoursesProvider.getUserAdministrationOptions.
|
||||
* @return {Promise<any>} Promise resolved when done.
|
||||
*/
|
||||
invalidateEnabledForCourse(courseId: number, navOptions?: any, admOptions?: any): Promise<any> {
|
||||
return this.courseProvider.invalidateCourseBlocks(courseId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the handler is enabled on a site level.
|
||||
*
|
||||
* @return {boolean} Whether or not the handler is enabled on a site level.
|
||||
*/
|
||||
isEnabled(): boolean | Promise<boolean> {
|
||||
return this.courseProvider.canGetCourseBlocks() && !this.blockDelegate.areBlocksDisabledInCourses();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the handler is enabled for a certain course.
|
||||
*
|
||||
* @param {number} courseId The course ID.
|
||||
* @param {any} accessData Access type and data. Default, guest, ...
|
||||
* @param {any} [navOptions] Course navigation options for current user. See CoreCoursesProvider.getUserNavigationOptions.
|
||||
* @param {any} [admOptions] Course admin options for current user. See CoreCoursesProvider.getUserAdministrationOptions.
|
||||
* @return {boolean|Promise<boolean>} True or promise resolved with true if enabled.
|
||||
*/
|
||||
isEnabledForCourse(courseId: number, accessData: any, navOptions?: any, admOptions?: any): boolean | Promise<boolean> {
|
||||
return this.courseProvider.getCourseBlocks(courseId).then((blocks) => {
|
||||
return blocks && blocks.length > 0;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data needed to render the handler.
|
||||
*
|
||||
* @param {Injector} injector Injector.
|
||||
* @param {number} courseId The course ID.
|
||||
* @return {CoreCourseOptionsHandlerData|Promise<CoreCourseOptionsHandlerData>} Data or promise resolved with the data.
|
||||
*/
|
||||
getDisplayData(injector: Injector, courseId: number): CoreCourseOptionsHandlerData | Promise<CoreCourseOptionsHandlerData> {
|
||||
return {
|
||||
title: 'core.block.blocks',
|
||||
class: 'core-course-blocks-handler',
|
||||
component: CoreBlockCourseBlocksComponent
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a course is downloaded. It should prefetch all the data to be able to see the addon in offline.
|
||||
*
|
||||
* @param {any} course The course.
|
||||
* @return {Promise<any>} Promise resolved when done.
|
||||
*/
|
||||
prefetch(course: any): Promise<any> {
|
||||
return this.courseProvider.getCourseBlocks(course.id);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
// (C) Copyright 2015 Martin Dougiamas
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { CoreCourseProvider } from '@core/course/providers/course';
|
||||
import { CoreBlockDelegate } from '@core/block/providers/delegate';
|
||||
|
||||
/**
|
||||
* Service that provides helper functions for blocks.
|
||||
*/
|
||||
@Injectable()
|
||||
export class CoreBlockHelperProvider {
|
||||
|
||||
constructor(protected courseProvider: CoreCourseProvider, protected blockDelegate: CoreBlockDelegate) {}
|
||||
|
||||
/**
|
||||
* Return if it get course blocks options is enabled for the current site.
|
||||
*
|
||||
* @return {boolean} true if enabled, false otherwise.
|
||||
*/
|
||||
canGetCourseBlocks(): boolean {
|
||||
return this.courseProvider.canGetCourseBlocks() && !this.blockDelegate.areBlocksDisabledInCourses();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of blocks for the selected course.
|
||||
*
|
||||
* @param {number} courseId Course ID.
|
||||
* @return {Promise<any>} List of supported blocks.
|
||||
*/
|
||||
getCourseBlocks(courseId: number): Promise<any> {
|
||||
const canGetBlocks = this.canGetCourseBlocks();
|
||||
|
||||
if (!canGetBlocks) {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
return this.courseProvider.getCourseBlocks(courseId).then((blocks) => {
|
||||
const hasSupportedBlock = this.blockDelegate.hasSupportedBlock(blocks);
|
||||
|
||||
if (!hasSupportedBlock) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return blocks;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -18,6 +18,7 @@ import { IonicModule } from 'ionic-angular';
|
|||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { CoreComponentsModule } from '@components/components.module';
|
||||
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||
import { CoreBlockComponentsModule } from '@core/block/components/components.module';
|
||||
import { CoreCourseFormatComponent } from './format/format';
|
||||
import { CoreCourseModuleComponent } from './module/module';
|
||||
import { CoreCourseModuleCompletionComponent } from './module-completion/module-completion';
|
||||
|
@ -33,6 +34,7 @@ import { CoreCourseUnsupportedModuleComponent } from './unsupported-module/unsup
|
|||
CoreCourseUnsupportedModuleComponent
|
||||
],
|
||||
imports: [
|
||||
CoreBlockComponentsModule,
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
TranslateModule.forChild(),
|
||||
|
|
|
@ -5,67 +5,71 @@
|
|||
</core-context-menu>
|
||||
</core-navbar-buttons>
|
||||
|
||||
<!-- Default course format. -->
|
||||
<core-dynamic-component [component]="courseFormatComponent" [data]="data">
|
||||
<core-loading [hideUntil]="loaded">
|
||||
<!-- Section selector. -->
|
||||
<core-dynamic-component [component]="sectionSelectorComponent" [data]="data">
|
||||
<div text-wrap *ngIf="displaySectionSelector && sections && sections.length" padding class="clearfix" ion-row justify-content-between class="safe-padding-horizontal" [class.core-section-download]="downloadEnabled">
|
||||
<button float-start ion-button icon-start icon-end (click)="showSectionSelector($event)" color="light" class="core-button-select button-no-uppercase" ion-col [attr.aria-label]="('core.course.sections' | translate) + ': ' + (selectedSection && (selectedSection.formattedName || selectedSection.name))" aria-haspopup="true" [attr.aria-expanded]="sectionSelectorExpanded" aria-controls="core-course-section-selector" id="core-course-section-button">
|
||||
<core-icon name="fa-folder"></core-icon>
|
||||
<span class="core-section-selector-text">{{selectedSection && (selectedSection.formattedName || selectedSection.name) || 'core.course.sections' | translate }}</span>
|
||||
<ion-icon name="arrow-dropdown" ios="md-arrow-dropdown"></ion-icon>
|
||||
</button>
|
||||
<!-- Section download. -->
|
||||
<ng-container *ngTemplateOutlet="sectionDownloadTemplate; context: {section: selectedSection}"></ng-container>
|
||||
</div>
|
||||
</core-dynamic-component>
|
||||
<core-block-course-blocks [courseId]="course.id" [hideBlocks]="selectedSection && selectedSection.id == allSectionsId && canLoadMore" [downloadEnabled]="downloadEnabled">
|
||||
<ion-content>
|
||||
<!-- Default course format. -->
|
||||
<core-dynamic-component [component]="courseFormatComponent" [data]="data">
|
||||
<core-loading [hideUntil]="loaded">
|
||||
<!-- Section selector. -->
|
||||
<core-dynamic-component [component]="sectionSelectorComponent" [data]="data">
|
||||
<div text-wrap *ngIf="displaySectionSelector && sections && sections.length" padding class="clearfix" ion-row justify-content-between class="safe-padding-horizontal" [class.core-section-download]="downloadEnabled">
|
||||
<button float-start ion-button icon-start icon-end (click)="showSectionSelector($event)" color="light" class="core-button-select button-no-uppercase" ion-col [attr.aria-label]="('core.course.sections' | translate) + ': ' + (selectedSection && (selectedSection.formattedName || selectedSection.name))" aria-haspopup="true" [attr.aria-expanded]="sectionSelectorExpanded" aria-controls="core-course-section-selector" id="core-course-section-button">
|
||||
<core-icon name="fa-folder"></core-icon>
|
||||
<span class="core-section-selector-text">{{selectedSection && (selectedSection.formattedName || selectedSection.name) || 'core.course.sections' | translate }}</span>
|
||||
<ion-icon name="arrow-dropdown" ios="md-arrow-dropdown"></ion-icon>
|
||||
</button>
|
||||
<!-- Section download. -->
|
||||
<ng-container *ngTemplateOutlet="sectionDownloadTemplate; context: {section: selectedSection}"></ng-container>
|
||||
</div>
|
||||
</core-dynamic-component>
|
||||
|
||||
<!-- Course summary. By default we only display the course progress. -->
|
||||
<core-dynamic-component [component]="courseSummaryComponent" [data]="data">
|
||||
<ion-list no-lines *ngIf="course.imageThumb || (selectedSection && selectedSection.id == allSectionsId && course.progress != null && course.progress >= 0)" class="core-format-progress-list">
|
||||
<div *ngIf="course.imageThumb" class="core-course-thumb">
|
||||
<img [src]="course.imageThumb" core-external-content alt=""/>
|
||||
<!-- Course summary. By default we only display the course progress. -->
|
||||
<core-dynamic-component [component]="courseSummaryComponent" [data]="data">
|
||||
<ion-list no-lines *ngIf="course.imageThumb || (selectedSection && selectedSection.id == allSectionsId && course.progress != null && course.progress >= 0)" class="core-format-progress-list">
|
||||
<div *ngIf="course.imageThumb" class="core-course-thumb">
|
||||
<img [src]="course.imageThumb" core-external-content alt=""/>
|
||||
</div>
|
||||
<ion-item class="core-course-progress" *ngIf="selectedSection && selectedSection.id == allSectionsId && course.progress != null && course.progress >= 0 && course.completionusertracked !== false">
|
||||
<core-progress-bar [progress]="course.progress"></core-progress-bar>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</core-dynamic-component>
|
||||
|
||||
<!-- Single section. -->
|
||||
<div *ngIf="selectedSection && selectedSection.id != allSectionsId">
|
||||
<core-dynamic-component [component]="singleSectionComponent" [data]="data">
|
||||
<ng-container *ngTemplateOutlet="sectionTemplate; context: {section: selectedSection}"></ng-container>
|
||||
<core-empty-box *ngIf="!selectedSection.hasContent" icon="qr-scanner" [message]="'core.course.nocontentavailable' | translate"></core-empty-box>
|
||||
</core-dynamic-component>
|
||||
</div>
|
||||
<ion-item class="core-course-progress" *ngIf="selectedSection && selectedSection.id == allSectionsId && course.progress != null && course.progress >= 0 && course.completionusertracked !== false">
|
||||
<core-progress-bar [progress]="course.progress"></core-progress-bar>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
|
||||
<!-- Multiple sections. -->
|
||||
<div *ngIf="selectedSection && selectedSection.id == allSectionsId">
|
||||
<core-dynamic-component [component]="allSectionsComponent" [data]="data">
|
||||
<ng-container *ngFor="let section of sections; index as i">
|
||||
<ng-container *ngIf="i <= showSectionId">
|
||||
<ng-container *ngTemplateOutlet="sectionTemplate; context: {section: section}"></ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</core-dynamic-component>
|
||||
|
||||
<core-infinite-loading [enabled]="canLoadMore" (action)="showMoreActivities($event)"></core-infinite-loading>
|
||||
</div>
|
||||
</core-loading>
|
||||
<ion-buttons padding end class="core-course-section-nav-buttons" *ngIf="displaySectionSelector && sections && sections.length">
|
||||
<button *ngIf="previousSection" ion-button color="light" icon-only (click)="sectionChanged(previousSection)" title="{{ 'core.previous' | translate }}">
|
||||
<ion-icon name="arrow-back" md="ios-arrow-back"></ion-icon>
|
||||
<core-format-text class="accesshide" [text]="previousSection.formattedName || previousSection.name"></core-format-text>
|
||||
</button>
|
||||
<button *ngIf="nextSection" ion-button icon-only (click)="sectionChanged(nextSection)" title="{{ 'core.next' | translate }}">
|
||||
<core-format-text class="accesshide" [text]="nextSection.formattedName || nextSection.name"></core-format-text>
|
||||
<ion-icon name="arrow-forward" md="ios-arrow-forward"></ion-icon>
|
||||
</button>
|
||||
</ion-buttons>
|
||||
|
||||
</core-dynamic-component>
|
||||
|
||||
<!-- Single section. -->
|
||||
<div *ngIf="selectedSection && selectedSection.id != allSectionsId">
|
||||
<core-dynamic-component [component]="singleSectionComponent" [data]="data">
|
||||
<ng-container *ngTemplateOutlet="sectionTemplate; context: {section: selectedSection}"></ng-container>
|
||||
<core-empty-box *ngIf="!selectedSection.hasContent" icon="qr-scanner" [message]="'core.course.nocontentavailable' | translate"></core-empty-box>
|
||||
</core-dynamic-component>
|
||||
</div>
|
||||
|
||||
<!-- Multiple sections. -->
|
||||
<div *ngIf="selectedSection && selectedSection.id == allSectionsId">
|
||||
<core-dynamic-component [component]="allSectionsComponent" [data]="data">
|
||||
<ng-container *ngFor="let section of sections; index as i">
|
||||
<ng-container *ngIf="i <= showSectionId">
|
||||
<ng-container *ngTemplateOutlet="sectionTemplate; context: {section: section}"></ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</core-dynamic-component>
|
||||
|
||||
<core-infinite-loading [enabled]="canLoadMore" (action)="showMoreActivities($event)"></core-infinite-loading>
|
||||
</div>
|
||||
</core-loading>
|
||||
<ion-buttons padding end class="core-course-section-nav-buttons" *ngIf="displaySectionSelector && sections && sections.length">
|
||||
<button *ngIf="previousSection" ion-button color="light" icon-only (click)="sectionChanged(previousSection)" title="{{ 'core.previous' | translate }}">
|
||||
<ion-icon name="arrow-back" md="ios-arrow-back"></ion-icon>
|
||||
<core-format-text class="accesshide" [text]="previousSection.formattedName || previousSection.name"></core-format-text>
|
||||
</button>
|
||||
<button *ngIf="nextSection" ion-button icon-only (click)="sectionChanged(nextSection)" title="{{ 'core.next' | translate }}">
|
||||
<core-format-text class="accesshide" [text]="nextSection.formattedName || nextSection.name"></core-format-text>
|
||||
<ion-icon name="arrow-forward" md="ios-arrow-forward"></ion-icon>
|
||||
</button>
|
||||
</ion-buttons>
|
||||
|
||||
</core-dynamic-component>
|
||||
</ion-content>
|
||||
</core-block-course-blocks>
|
||||
|
||||
<!-- Template to render a section. -->
|
||||
<ng-template #sectionTemplate let-section="section">
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
// limitations under the License.
|
||||
|
||||
import {
|
||||
Component, Input, OnInit, OnChanges, OnDestroy, SimpleChange, Output, EventEmitter, ViewChildren, QueryList, Injector
|
||||
Component, Input, OnInit, OnChanges, OnDestroy, SimpleChange, Output, EventEmitter, ViewChildren, QueryList, Injector, ViewChild
|
||||
} from '@angular/core';
|
||||
import { Content, ModalController } from 'ionic-angular';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
@ -24,6 +24,7 @@ import { CoreCourseProvider } from '@core/course/providers/course';
|
|||
import { CoreCourseHelperProvider } from '@core/course/providers/helper';
|
||||
import { CoreCourseFormatDelegate } from '@core/course/providers/format-delegate';
|
||||
import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate';
|
||||
import { CoreBlockCourseBlocksComponent } from '@core/block/components/course-blocks/course-blocks';
|
||||
import { CoreDynamicComponent } from '@components/dynamic-component/dynamic-component';
|
||||
|
||||
/**
|
||||
|
@ -52,6 +53,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
|
|||
@Output() completionChanged?: EventEmitter<any>; // Will emit an event when any module completion changes.
|
||||
|
||||
@ViewChildren(CoreDynamicComponent) dynamicComponents: QueryList<CoreDynamicComponent>;
|
||||
@ViewChild(CoreBlockCourseBlocksComponent) courseBlocksComponent: CoreBlockCourseBlocksComponent;
|
||||
|
||||
// All the possible component classes.
|
||||
courseFormatComponent: any;
|
||||
|
@ -420,6 +422,10 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
|
|||
promises.push(Promise.resolve(component.callComponentFunction('doRefresh', [refresher, done, afterCompletionChange])));
|
||||
});
|
||||
|
||||
promises.push(this.courseBlocksComponent.invalidateBlocks().finally(() => {
|
||||
return this.courseBlocksComponent.loadContent();
|
||||
}));
|
||||
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@ import { CoreSitesProvider } from '@providers/sites';
|
|||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||
import { CoreTabsComponent } from '@components/tabs/tabs';
|
||||
import { CoreCoursesProvider } from '@core/courses/providers/courses';
|
||||
import { CoreCourseProvider } from '../../providers/course';
|
||||
import { CoreCourseHelperProvider } from '../../providers/helper';
|
||||
import { CoreCourseFormatDelegate } from '../../providers/format-delegate';
|
||||
|
@ -28,8 +30,6 @@ import { CoreCourseOptionsDelegate, CoreCourseOptionsHandlerToDisplay,
|
|||
CoreCourseOptionsMenuHandlerToDisplay } from '../../providers/options-delegate';
|
||||
import { CoreCourseSyncProvider } from '../../providers/sync';
|
||||
import { CoreCourseFormatComponent } from '../../components/format/format';
|
||||
import { CoreCoursesProvider } from '@core/courses/providers/courses';
|
||||
import { CoreTabsComponent } from '@components/tabs/tabs';
|
||||
|
||||
/**
|
||||
* Page that displays the list of courses the user is enrolled in.
|
||||
|
|
|
@ -1,32 +1,30 @@
|
|||
<core-loading [hideUntil]="dataLoaded">
|
||||
<core-block-course-blocks [courseId]="siteHomeId" [downloadEnabled]="downloadEnabled">
|
||||
<ion-content>
|
||||
<core-loading [hideUntil]="dataLoaded">
|
||||
<ion-list>
|
||||
<!-- Site home main contents. -->
|
||||
<ng-container *ngIf="section && section.hasContent">
|
||||
<ion-item text-wrap *ngIf="section.summary">
|
||||
<core-format-text [text]="section.summary"></core-format-text>
|
||||
</ion-item>
|
||||
|
||||
<ion-list>
|
||||
<!-- Site home main contents. -->
|
||||
<ng-container *ngIf="section && section.hasContent">
|
||||
<ion-item text-wrap *ngIf="section.summary">
|
||||
<core-format-text [text]="section.summary"></core-format-text>
|
||||
</ion-item>
|
||||
<core-course-module *ngFor="let module of section.modules" [module]="module" [courseId]="siteHomeId" [downloadEnabled]="downloadEnabled" [section]="section"></core-course-module>
|
||||
</ng-container>
|
||||
|
||||
<core-course-module *ngFor="let module of section.modules" [module]="module" [courseId]="siteHomeId" [downloadEnabled]="downloadEnabled" [section]="section"></core-course-module>
|
||||
</ng-container>
|
||||
<!-- Site home items: news, categories, courses, etc. -->
|
||||
<ng-container *ngIf="items.length > 0">
|
||||
<ion-item-divider *ngIf="section && section.hasContent"></ion-item-divider>
|
||||
<ng-container *ngFor="let item of items">
|
||||
<core-sitehome-all-course-list class="item" *ngIf="item == 'all-course-list'"></core-sitehome-all-course-list>
|
||||
<core-sitehome-categories *ngIf="item == 'categories'"></core-sitehome-categories>
|
||||
<core-sitehome-course-search *ngIf="item == 'course-search'"></core-sitehome-course-search>
|
||||
<core-sitehome-enrolled-course-list *ngIf="item == 'enrolled-course-list'"></core-sitehome-enrolled-course-list>
|
||||
<core-sitehome-news *ngIf="item == 'news'"></core-sitehome-news>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ion-list>
|
||||
|
||||
<!-- Site home items: news, categories, courses, etc. -->
|
||||
<ng-container *ngIf="items.length > 0">
|
||||
<ion-item-divider *ngIf="section && section.hasContent"></ion-item-divider>
|
||||
<ng-container *ngFor="let item of items">
|
||||
<core-sitehome-all-course-list class="item" *ngIf="item == 'all-course-list'"></core-sitehome-all-course-list>
|
||||
<core-sitehome-categories *ngIf="item == 'categories'"></core-sitehome-categories>
|
||||
<core-sitehome-course-search *ngIf="item == 'course-search'"></core-sitehome-course-search>
|
||||
<core-sitehome-enrolled-course-list *ngIf="item == 'enrolled-course-list'"></core-sitehome-enrolled-course-list>
|
||||
<core-sitehome-news *ngIf="item == 'news'"></core-sitehome-news>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
<!-- Site home blocks. -->
|
||||
<ng-container *ngFor="let block of blocks">
|
||||
<core-block [block]="block" contextLevel="course" [instanceId]="siteHomeId" [extraData]="{'downloadEnabled': downloadEnabled}"></core-block>
|
||||
</ng-container>
|
||||
</ion-list>
|
||||
|
||||
<core-empty-box *ngIf="!hasContent && !hasSupportedBlock" icon="qr-scanner" [message]="'core.course.nocontentavailable' | translate"></core-empty-box>
|
||||
</core-loading>
|
||||
<core-empty-box *ngIf="!hasContent" icon="qr-scanner" [message]="'core.course.nocontentavailable' | translate"></core-empty-box>
|
||||
</core-loading>
|
||||
</ion-content>
|
||||
</core-block-course-blocks>
|
||||
|
|
|
@ -12,14 +12,13 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component, OnInit, ViewChildren, QueryList, Input } from '@angular/core';
|
||||
import { Component, OnInit, Input, ViewChild } from '@angular/core';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { CoreCourseProvider } from '@core/course/providers/course';
|
||||
import { CoreCourseHelperProvider } from '@core/course/providers/helper';
|
||||
import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate';
|
||||
import { CoreBlockDelegate } from '@core/block/providers/delegate';
|
||||
import { CoreBlockComponent } from '@core/block/components/block/block';
|
||||
import { CoreBlockCourseBlocksComponent } from '@core/block/components/course-blocks/course-blocks';
|
||||
import { CoreSite } from '@classes/site';
|
||||
|
||||
/**
|
||||
|
@ -30,21 +29,19 @@ import { CoreSite } from '@classes/site';
|
|||
templateUrl: 'core-sitehome-index.html',
|
||||
})
|
||||
export class CoreSiteHomeIndexComponent implements OnInit {
|
||||
@ViewChildren(CoreBlockComponent) blocksComponents: QueryList<CoreBlockComponent>;
|
||||
@Input() downloadEnabled: boolean;
|
||||
@ViewChild(CoreBlockCourseBlocksComponent) courseBlocksComponent: CoreBlockCourseBlocksComponent;
|
||||
|
||||
dataLoaded = false;
|
||||
section: any;
|
||||
hasContent: boolean;
|
||||
hasSupportedBlock: boolean;
|
||||
items: any[] = [];
|
||||
siteHomeId: number;
|
||||
currentSite: CoreSite;
|
||||
blocks = [];
|
||||
|
||||
constructor(private domUtils: CoreDomUtilsProvider, sitesProvider: CoreSitesProvider,
|
||||
private courseProvider: CoreCourseProvider, private courseHelper: CoreCourseHelperProvider,
|
||||
private prefetchDelegate: CoreCourseModulePrefetchDelegate, private blockDelegate: CoreBlockDelegate) {
|
||||
private prefetchDelegate: CoreCourseModulePrefetchDelegate) {
|
||||
this.currentSite = sitesProvider.getCurrentSite();
|
||||
this.siteHomeId = this.currentSite.getSiteHomeId();
|
||||
}
|
||||
|
@ -79,19 +76,15 @@ export class CoreSiteHomeIndexComponent implements OnInit {
|
|||
promises.push(this.prefetchDelegate.invalidateModules(this.section.modules, this.siteHomeId));
|
||||
}
|
||||
|
||||
if (this.courseProvider.canGetCourseBlocks()) {
|
||||
promises.push(this.courseProvider.invalidateCourseBlocks(this.siteHomeId));
|
||||
}
|
||||
|
||||
// Invalidate the blocks.
|
||||
this.blocksComponents.forEach((blockComponent) => {
|
||||
promises.push(blockComponent.invalidate().catch(() => {
|
||||
// Ignore errors.
|
||||
}));
|
||||
});
|
||||
promises.push(this.courseBlocksComponent.invalidateBlocks());
|
||||
|
||||
Promise.all(promises).finally(() => {
|
||||
this.loadContent().finally(() => {
|
||||
const p2 = [];
|
||||
|
||||
p2.push(this.loadContent());
|
||||
p2.push(this.courseBlocksComponent.loadContent());
|
||||
|
||||
return Promise.all(p2).finally(() => {
|
||||
refresher.complete();
|
||||
});
|
||||
});
|
||||
|
@ -149,32 +142,6 @@ export class CoreSiteHomeIndexComponent implements OnInit {
|
|||
this.currentSite && this.currentSite.getInfo().sitename).catch(() => {
|
||||
// Ignore errors.
|
||||
});
|
||||
|
||||
// Get site home blocks.
|
||||
const canGetBlocks = this.courseProvider.canGetCourseBlocks(),
|
||||
promise = canGetBlocks ? this.courseProvider.getCourseBlocks(this.siteHomeId) : Promise.reject(null);
|
||||
|
||||
return promise.then((blocks) => {
|
||||
this.blocks = blocks;
|
||||
this.hasSupportedBlock = this.blockDelegate.hasSupportedBlock(blocks);
|
||||
|
||||
}).catch((error) => {
|
||||
if (canGetBlocks) {
|
||||
this.domUtils.showErrorModal(error);
|
||||
}
|
||||
this.blocks = [];
|
||||
|
||||
// Cannot get the blocks, just show site main menu if needed.
|
||||
const section = sections.find((section) => section.section == 0);
|
||||
if (section && this.courseHelper.sectionHasContent(section)) {
|
||||
this.blocks.push({
|
||||
name: 'site_main_menu'
|
||||
});
|
||||
this.hasSupportedBlock = true;
|
||||
} else {
|
||||
this.hasSupportedBlock = false;
|
||||
}
|
||||
});
|
||||
}).catch((error) => {
|
||||
this.domUtils.showErrorModalDefault(error, 'core.course.couldnotloadsectioncontent', true);
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue