MOBILE-3914 course: Add side blocks component and replace it
This commit is contained in:
		
							parent
							
								
									80f1b96f61
								
							
						
					
					
						commit
						8cd89ead85
					
				| @ -1399,6 +1399,8 @@ | ||||
|   "core.areyousure": "moodle", | ||||
|   "core.back": "moodle", | ||||
|   "core.block.blocks": "moodle", | ||||
|   "core.block.noblocks": "error", | ||||
|   "core.block.opendrawerblocks": "moodle", | ||||
|   "core.browser": "local_moodlemobileapp", | ||||
|   "core.cancel": "moodle", | ||||
|   "core.cannotconnect": "local_moodlemobileapp", | ||||
|  | ||||
| @ -72,7 +72,3 @@ | ||||
|         height: auto; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| :host-context(core-block-course-blocks) .core-empty-box { | ||||
|     position: relative; | ||||
| } | ||||
|  | ||||
| @ -16,15 +16,17 @@ import { NgModule } from '@angular/core'; | ||||
| import { CoreBlockComponent } from './block/block'; | ||||
| import { CoreBlockOnlyTitleComponent } from './only-title-block/only-title-block'; | ||||
| import { CoreBlockPreRenderedComponent } from './pre-rendered-block/pre-rendered-block'; | ||||
| import { CoreBlockCourseBlocksComponent } from './course-blocks/course-blocks'; | ||||
| import { CoreSharedModule } from '@/core/shared.module'; | ||||
| import { CoreBlockSideBlocksComponent } from './side-blocks/side-blocks'; | ||||
| import { CoreBlockSideBlocksButtonComponent } from './side-blocks-button/side-blocks-button'; | ||||
| 
 | ||||
| @NgModule({ | ||||
|     declarations: [ | ||||
|         CoreBlockComponent, | ||||
|         CoreBlockOnlyTitleComponent, | ||||
|         CoreBlockPreRenderedComponent, | ||||
|         CoreBlockCourseBlocksComponent, | ||||
|         CoreBlockSideBlocksComponent, | ||||
|         CoreBlockSideBlocksButtonComponent, | ||||
|     ], | ||||
|     imports: [ | ||||
|         CoreSharedModule, | ||||
| @ -33,7 +35,8 @@ import { CoreSharedModule } from '@/core/shared.module'; | ||||
|         CoreBlockComponent, | ||||
|         CoreBlockOnlyTitleComponent, | ||||
|         CoreBlockPreRenderedComponent, | ||||
|         CoreBlockCourseBlocksComponent, | ||||
|         CoreBlockSideBlocksComponent, | ||||
|         CoreBlockSideBlocksButtonComponent, | ||||
|     ], | ||||
| }) | ||||
| export class CoreBlockComponentsModule {} | ||||
|  | ||||
| @ -1,15 +0,0 @@ | ||||
| <div class="core-course-blocks-content"> | ||||
|     <ng-content></ng-content> | ||||
| </div> | ||||
| 
 | ||||
| <div *ngIf="blocks && blocks.length > 0 && !hideBlocks" [class.core-hide-blocks]="hideBottomBlocks" class="core-course-blocks-side"> | ||||
|     <core-loading [hideUntil]="dataLoaded" [fullscreen]="false"> | ||||
|         <ion-list> | ||||
|             <!-- Course expand="block"s. --> | ||||
|             <ng-container *ngFor="let block of blocks"> | ||||
|                 <core-block *ngIf="block.visible" [block]="block" contextLevel="course" [instanceId]="courseId" | ||||
|                     [extraData]="{'downloadEnabled': downloadEnabled}"></core-block> | ||||
|             </ng-container> | ||||
|         </ion-list> | ||||
|     </core-loading> | ||||
| </div> | ||||
| @ -1,61 +0,0 @@ | ||||
| :host { | ||||
|     --side-blocks-box-shadow: var(--core-menu-box-shadow-start); | ||||
| 
 | ||||
|     &.core-no-blocks .core-course-blocks-content { | ||||
|         height: auto; | ||||
|     } | ||||
| 
 | ||||
|     &.core-has-blocks { | ||||
|         @media (min-width: 768px) { | ||||
|             display: flex; | ||||
| 
 | ||||
|             flex-direction: row; | ||||
|             flex-wrap: nowrap; | ||||
| 
 | ||||
|             .core-course-blocks-content { | ||||
|                 box-shadow: none !important; | ||||
|                 flex-grow: 1; | ||||
|                 max-width: 100%; | ||||
| 
 | ||||
|                 --ion-safe-area-right: 0px; | ||||
|             } | ||||
| 
 | ||||
|             div.core-course-blocks-side { | ||||
|                 max-width: var(--side-blocks-max-width); | ||||
|                 min-width: var(--side-blocks-min-width); | ||||
|                 box-shadow: var(--side-blocks-box-shadow); | ||||
|                 z-index: 2; | ||||
|             } | ||||
| 
 | ||||
|             .core-course-blocks-content, | ||||
|             div.core-course-blocks-side { | ||||
|                 position: relative; | ||||
|                 height: 100%; | ||||
| 
 | ||||
|                 .core-loading-center, | ||||
|                 core-loading.core-loading-loaded { | ||||
|                     position: initial; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         @media (max-width: 767.98px) { | ||||
|             // Disable scroll on individual columns. | ||||
|             div.core-course-blocks-side { | ||||
|                 height: auto; | ||||
| 
 | ||||
|                 &.core-hide-blocks { | ||||
|                     display: none; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| :host-context([dir="rtl"]).core-has-blocks { | ||||
|     @media (min-width: 768px) { | ||||
|         div.core-course-blocks-side { | ||||
|             box-shadow: var(--side-blocks-box-shadow); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,3 @@ | ||||
| <ion-button (click)="openBlocks()" [attr.aria-label]="'core.block.opendrawerblocks' | translate"> | ||||
|     <ion-icon name="fas-cubes" slot="icon-only" aria-hidden="true"></ion-icon> | ||||
| </ion-button> | ||||
| @ -0,0 +1,29 @@ | ||||
| @import "~theme/globals"; | ||||
| 
 | ||||
| :host { | ||||
|     @include position(50%, 0px, null, null); | ||||
|     position: fixed; | ||||
|     z-index: 10; | ||||
| 
 | ||||
|     ion-button { | ||||
|         margin: 0; | ||||
|         --padding-start: 0.5em; | ||||
|         --padding-end: 0; | ||||
|         --border-radius: 2em 0 0 2em; | ||||
| 
 | ||||
|         &::part(native) { | ||||
|             @include core-transition(padding, 200ms); | ||||
|         } | ||||
|          | ||||
|         &:hover { | ||||
|             --padding-end: 1.2em; | ||||
|             --padding-start: 1em; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| :host-context([dir=rtl]) { | ||||
|     ion-button { | ||||
|         --border-radius: 0 2em 2em 0; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,45 @@ | ||||
| // (C) Copyright 2015 Moodle Pty Ltd.
 | ||||
| //
 | ||||
| // 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 { Component, Input } from '@angular/core'; | ||||
| import { CoreDomUtils } from '@services/utils/dom'; | ||||
| import { CoreBlockSideBlocksComponent } from '../side-blocks/side-blocks'; | ||||
| 
 | ||||
| /** | ||||
|  * Component that displays a button to open blocks. | ||||
|  */ | ||||
| @Component({ | ||||
|     selector: 'core-block-side-blocks-button', | ||||
|     templateUrl: 'side-blocks-button.html', | ||||
|     styleUrls: ['side-blocks-button.scss'], | ||||
| }) | ||||
| export class CoreBlockSideBlocksButtonComponent { | ||||
| 
 | ||||
|     @Input() courseId!: number; | ||||
|     @Input() downloadEnabled = false; | ||||
| 
 | ||||
|     /** | ||||
|      * Open side blocks. | ||||
|      */ | ||||
|     openBlocks(): void { | ||||
|         CoreDomUtils.openSideModal({ | ||||
|             component: CoreBlockSideBlocksComponent, | ||||
|             componentProps: { | ||||
|                 courseId: this.courseId, | ||||
|                 downloadEnabled: this.downloadEnabled, | ||||
|             }, | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,26 @@ | ||||
| <ion-header> | ||||
|     <ion-toolbar> | ||||
|         <h1>{{ 'core.block.blocks' | translate }}</h1> | ||||
|         <ion-buttons slot="end"> | ||||
|             <ion-button fill="clear" (click)="closeModal()" [attr.aria-label]="'core.close' | translate"> | ||||
|                 <ion-icon name="fas-times" slot="icon-only" aria-hidden=true></ion-icon> | ||||
|             </ion-button> | ||||
|         </ion-buttons> | ||||
|     </ion-toolbar> | ||||
| </ion-header> | ||||
| <ion-content> | ||||
|     <ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="doRefresh($event.target)"> | ||||
|         <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content> | ||||
|     </ion-refresher> | ||||
|     <core-loading [hideUntil]="loaded"> | ||||
|         <ion-list *ngIf="blocks.length > 0"> | ||||
|             <ng-container *ngFor="let block of blocks"> | ||||
|                 <core-block *ngIf="block.visible" [block]="block" contextLevel="course" [instanceId]="courseId" | ||||
|                     [extraData]="{'downloadEnabled': downloadEnabled}"></core-block> | ||||
|             </ng-container> | ||||
|         </ion-list> | ||||
| 
 | ||||
|         <core-empty-box *ngIf="blocks.length == 0" icon="fas-cubes" [message]="'core.block.noblocks' | translate"> | ||||
|         </core-empty-box> | ||||
|     </core-loading> | ||||
| </ion-content> | ||||
| @ -12,50 +12,38 @@ | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { Component, ViewChildren, Input, OnInit, QueryList, ElementRef } from '@angular/core'; | ||||
| import { IonContent } from '@ionic/angular'; | ||||
| import { Component, ViewChildren, Input, OnInit, QueryList } from '@angular/core'; | ||||
| import { ModalController } from '@singletons'; | ||||
| import { CoreDomUtils } from '@services/utils/dom'; | ||||
| import { CoreCourse, CoreCourseBlock } from '@features/course/services/course'; | ||||
| import { CoreBlockHelper } from '../../services/block-helper'; | ||||
| import { CoreBlockComponent } from '../block/block'; | ||||
| import { CoreUtils } from '@services/utils/utils'; | ||||
| import { IonRefresher } from '@ionic/angular'; | ||||
| 
 | ||||
| /** | ||||
|  * Component that displays the list of course blocks. | ||||
|  * Component that displays the list of side blocks. | ||||
|  */ | ||||
| @Component({ | ||||
|     selector: 'core-block-course-blocks', | ||||
|     templateUrl: 'core-block-course-blocks.html', | ||||
|     styleUrls: ['course-blocks.scss'], | ||||
|     selector: 'core-block-side-blocks', | ||||
|     templateUrl: 'side-blocks.html', | ||||
| }) | ||||
| export class CoreBlockCourseBlocksComponent implements OnInit { | ||||
| export class CoreBlockSideBlocksComponent implements OnInit { | ||||
| 
 | ||||
|     @Input() courseId!: number; | ||||
|     @Input() hideBlocks = false; | ||||
|     @Input() hideBottomBlocks = false; | ||||
|     @Input() downloadEnabled = false; | ||||
| 
 | ||||
|     @ViewChildren(CoreBlockComponent) blocksComponents?: QueryList<CoreBlockComponent>; | ||||
| 
 | ||||
|     dataLoaded = false; | ||||
|     loaded = false; | ||||
|     blocks: CoreCourseBlock[] = []; | ||||
| 
 | ||||
|     protected element: HTMLElement; | ||||
| 
 | ||||
|     constructor( | ||||
|         element: ElementRef, | ||||
|         protected content: IonContent, | ||||
|     ) { | ||||
|         this.element = element.nativeElement; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Component being initialized. | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     async ngOnInit(): Promise<void> { | ||||
|         this.element.classList.add('core-no-blocks'); | ||||
|         this.loadContent().finally(() => { | ||||
|             this.dataLoaded = true; | ||||
|             this.loaded = true; | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
| @ -87,7 +75,6 @@ export class CoreBlockCourseBlocksComponent implements OnInit { | ||||
|      * @return Promise resolved when done. | ||||
|      */ | ||||
|     async loadContent(): Promise<void> { | ||||
| 
 | ||||
|         try { | ||||
|             this.blocks = await CoreBlockHelper.getCourseBlocks(this.courseId); | ||||
|         } catch (error) { | ||||
| @ -95,29 +82,26 @@ export class CoreBlockCourseBlocksComponent implements OnInit { | ||||
| 
 | ||||
|             this.blocks = []; | ||||
|         } | ||||
| 
 | ||||
|         const scrollElement = await this.content.getScrollElement(); | ||||
|         if (!this.hideBlocks && this.blocks.length > 0) { | ||||
|             this.element.classList.add('core-has-blocks'); | ||||
|             this.element.classList.remove('core-no-blocks'); | ||||
| 
 | ||||
|             scrollElement.classList.add('core-course-block-with-blocks'); | ||||
|         } else { | ||||
|             this.element.classList.remove('core-has-blocks'); | ||||
|             this.element.classList.add('core-no-blocks'); | ||||
|             scrollElement.classList.remove('core-course-block-with-blocks'); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Refresh data. | ||||
|      * Refresh the data. | ||||
|      * | ||||
|      * @return Promise resolved when done. | ||||
|      * @param refresher Refresher. | ||||
|      */ | ||||
|     async doRefresh(): Promise<void> { | ||||
|     async doRefresh(refresher?: IonRefresher): Promise<void> { | ||||
|         await CoreUtils.ignoreErrors(this.invalidateBlocks()); | ||||
| 
 | ||||
|         await this.loadContent(); | ||||
|         await this.loadContent().finally(() => { | ||||
|             refresher?.complete(); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Close modal. | ||||
|      */ | ||||
|     closeModal(): void { | ||||
|         ModalController.dismiss(); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,3 +1,5 @@ | ||||
| { | ||||
|     "blocks": "Blocks" | ||||
| } | ||||
|     "blocks": "Blocks", | ||||
|     "noblocks": "No blocks found!", | ||||
|     "opendrawerblocks": "Open block drawer" | ||||
| } | ||||
|  | ||||
| @ -54,6 +54,22 @@ export class CoreBlockHelperProvider { | ||||
|         return blocks; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns if the course has any block. | ||||
|      * | ||||
|      * @param courseId Course ID. | ||||
|      * @return Wether course has blocks. | ||||
|      */ | ||||
|     async hasCourseBlocks(courseId: number): Promise<boolean> { | ||||
|         try { | ||||
|             const blocks = await this.getCourseBlocks(courseId); | ||||
| 
 | ||||
|             return blocks.length > 0; | ||||
|         } catch { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| export const CoreBlockHelper = makeSingleton(CoreBlockHelperProvider); | ||||
|  | ||||
| @ -6,126 +6,115 @@ | ||||
|         </core-context-menu-item> | ||||
|     </core-context-menu> | ||||
| </core-navbar-buttons> | ||||
| <core-dynamic-component [component]="courseFormatComponent" [data]="data"> | ||||
|     <!-- Default course format. --> | ||||
|     <core-loading [hideUntil]="loaded"> | ||||
|         <!-- Section selector. --> | ||||
|         <core-dynamic-component [component]="sectionSelectorComponent" [data]="data"> | ||||
| 
 | ||||
| <core-block-course-blocks *ngIf="loaded" [courseId]="course!.id" [hideBlocks]="!displayBlocks" [downloadEnabled]="downloadEnabled" | ||||
|     [hideBottomBlocks]="selectedSection && selectedSection.id == allSectionsId && canLoadMore"> | ||||
|             <div *ngIf="displaySectionSelector && sections && hasSeveralSections" | ||||
|                 class="ion-text-wrap ion-justify-content-between ion-align-items-center core-button-selector-row" | ||||
|                 [class.core-section-download]="downloadEnabled"> | ||||
|                 <core-combobox [modalOptions]="sectionSelectorModalOptions" interface="modal" listboxId="core-course-section-button" | ||||
|                     icon="fas-folder" [label]="'core.course.section' | translate" | ||||
|                     [selection]="selectedSection ? selectedSection.name : 'core.course.sections' | translate" | ||||
|                     (onChange)="sectionChanged($event)"> | ||||
|                     <span slot="text"> | ||||
|                         <core-format-text *ngIf="selectedSection" [text]="selectedSection.name" contextLevel="course" | ||||
|                             [contextInstanceId]="course?.id" [clean]="true" [singleLine]="true"> | ||||
|                         </core-format-text> | ||||
|                         <ng-container *ngIf="!selectedSection">{{ 'core.course.sections' | translate }}</ng-container> | ||||
|                     </span> | ||||
|                 </core-combobox> | ||||
|                 <!-- Section download. --> | ||||
|                 <ng-container *ngTemplateOutlet="sectionDownloadTemplate; context: {section: selectedSection}"></ng-container> | ||||
|             </div> | ||||
|         </core-dynamic-component> | ||||
| 
 | ||||
|     <core-dynamic-component [component]="courseFormatComponent" [data]="data"> | ||||
|         <!-- Default course format. --> | ||||
|         <core-loading [hideUntil]="loaded"> | ||||
|             <!-- Section selector. --> | ||||
|             <core-dynamic-component [component]="sectionSelectorComponent" [data]="data"> | ||||
| 
 | ||||
|                 <div *ngIf="displaySectionSelector && sections && hasSeveralSections" | ||||
|                     class="ion-text-wrap ion-justify-content-between ion-align-items-center core-button-selector-row" | ||||
|                     [class.core-section-download]="downloadEnabled"> | ||||
|                     <core-combobox | ||||
|                         [modalOptions]="sectionSelectorModalOptions" | ||||
|                         interface="modal" | ||||
|                         listboxId="core-course-section-button" | ||||
|                         icon="fas-folder" | ||||
|                         [label]="'core.course.section' | translate" | ||||
|                         [selection]="selectedSection ? selectedSection.name : 'core.course.sections' | translate" | ||||
|                         (onChange)="sectionChanged($event)" | ||||
|                     > | ||||
|                         <span slot="text"> | ||||
|                             <core-format-text *ngIf="selectedSection" [text]="selectedSection.name" contextLevel="course" | ||||
|                                 [contextInstanceId]="course?.id" [clean]="true" [singleLine]="true"> | ||||
|                             </core-format-text> | ||||
|                             <ng-container *ngIf="!selectedSection">{{ 'core.course.sections' | translate }}</ng-container> | ||||
|                         </span> | ||||
|                     </core-combobox> | ||||
|                     <!-- Section download. --> | ||||
|                     <ng-container *ngTemplateOutlet="sectionDownloadTemplate; context: {section: selectedSection}"></ng-container> | ||||
|         <!-- Course summary. By default we only display the course progress. --> | ||||
|         <core-dynamic-component [component]="courseSummaryComponent" [data]="data"> | ||||
|             <ion-list lines="none" class="core-format-progress-list" *ngIf="imageThumb || (selectedSection?.id == allSectionsId && progress !== undefined) || | ||||
|                     (selectedSection && selectedSection.id != allSectionsId && | ||||
|                     (selectedSection.availabilityinfo || selectedSection.visible === 0))"> | ||||
|                 <div *ngIf="imageThumb" class="core-course-thumb"> | ||||
|                     <img [src]="imageThumb" core-external-content alt="" /> | ||||
|                 </div> | ||||
|                 <ng-container *ngIf="selectedSection"> | ||||
|                     <ion-item class="core-course-progress" *ngIf="selectedSection?.id == allSectionsId && progress !== undefined"> | ||||
|                         <core-progress-bar [progress]="progress" a11yText="core.course.aria:sectionprogress"> | ||||
|                         </core-progress-bar> | ||||
|                     </ion-item> | ||||
|                     <ion-item *ngIf="selectedSection && selectedSection.id != allSectionsId && | ||||
|                         (selectedSection.availabilityinfo || selectedSection.visible === 0)"> | ||||
|                         <ion-badge color="info" class="ion-text-wrap" | ||||
|                             *ngIf="selectedSection.visible === 0 && selectedSection.uservisible !== false"> | ||||
|                             {{ 'core.course.hiddenfromstudents' | translate }} | ||||
|                         </ion-badge> | ||||
|                         <ion-badge color="info" class="ion-text-wrap" | ||||
|                             *ngIf="selectedSection.visible === 0 && selectedSection.uservisible === false"> | ||||
|                             {{ 'core.notavailable' | translate }} | ||||
|                         </ion-badge> | ||||
|                         <ion-badge color="info" class="ion-text-wrap" *ngIf="selectedSection.availabilityinfo"> | ||||
|                             <core-format-text [text]="selectedSection.availabilityinfo" contextLevel="course" | ||||
|                                 [contextInstanceId]="course?.id"> | ||||
|                             </core-format-text> | ||||
|                         </ion-badge> | ||||
|                     </ion-item> | ||||
|                 </ng-container> | ||||
|             </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="fas-th-large" | ||||
|                     [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> | ||||
| 
 | ||||
|             <!-- Course summary. By default we only display the course progress. --> | ||||
|             <core-dynamic-component [component]="courseSummaryComponent" [data]="data"> | ||||
|                 <ion-list lines="none" class="core-format-progress-list" | ||||
|                     *ngIf="imageThumb || (selectedSection?.id == allSectionsId && progress !== undefined) || | ||||
|                         (selectedSection && selectedSection.id != allSectionsId && | ||||
|                         (selectedSection.availabilityinfo || selectedSection.visible === 0))"> | ||||
|                     <div *ngIf="imageThumb" class="core-course-thumb"> | ||||
|                         <img [src]="imageThumb" core-external-content alt=""/> | ||||
|                     </div> | ||||
|                     <ng-container *ngIf="selectedSection"> | ||||
|                         <ion-item class="core-course-progress" | ||||
|                             *ngIf="selectedSection?.id == allSectionsId && progress !== undefined"> | ||||
|                             <core-progress-bar [progress]="progress" a11yText="core.course.aria:sectionprogress"> | ||||
|                             </core-progress-bar> | ||||
|                         </ion-item> | ||||
|                         <ion-item *ngIf="selectedSection && selectedSection.id != allSectionsId && | ||||
|                             (selectedSection.availabilityinfo || selectedSection.visible === 0)"> | ||||
|                             <ion-badge color="info" class="ion-text-wrap" | ||||
|                                 *ngIf="selectedSection.visible === 0 && selectedSection.uservisible !== false"> | ||||
|                                 {{ 'core.course.hiddenfromstudents' | translate }} | ||||
|                             </ion-badge> | ||||
|                             <ion-badge color="info" class="ion-text-wrap" | ||||
|                                 *ngIf="selectedSection.visible === 0 && selectedSection.uservisible === false"> | ||||
|                                 {{ 'core.notavailable' | translate }} | ||||
|                             </ion-badge> | ||||
|                             <ion-badge color="info" class="ion-text-wrap" *ngIf="selectedSection.availabilityinfo"> | ||||
|                                 <core-format-text [text]="selectedSection.availabilityinfo" contextLevel="course" | ||||
|                                     [contextInstanceId]="course?.id"> | ||||
|                                 </core-format-text> | ||||
|                             </ion-badge> | ||||
|                         </ion-item> | ||||
|                     </ng-container> | ||||
|                 </ion-list> | ||||
|             </core-dynamic-component> | ||||
|             <core-infinite-loading [enabled]="canLoadMore" (action)="showMoreActivities($event)"></core-infinite-loading> | ||||
|         </div> | ||||
| 
 | ||||
|             <!-- 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="fas-th-large" | ||||
|                         [message]="'core.course.nocontentavailable' | translate"> | ||||
|                     </core-empty-box> | ||||
|                 </core-dynamic-component> | ||||
|             </div> | ||||
|         <ion-buttons class="ion-padding core-course-section-nav-buttons safe-area-padding-horizontal" | ||||
|             *ngIf="displaySectionSelector && sections?.length"> | ||||
|             <ion-button *ngIf="previousSection" (click)="sectionChanged(previousSection)" fill="outline" color="primary" | ||||
|                 [attr.aria-label]="('core.previous' | translate) + ': ' + previousSection.name"> | ||||
|                 <ion-icon name="fas-chevron-left" slot="icon-only" aria-hidden="true"></ion-icon> | ||||
|                 <core-format-text class="sr-only" [text]="previousSection.name" contextLevel="course" [contextInstanceId]="course?.id"> | ||||
|                 </core-format-text> | ||||
|             </ion-button> | ||||
|             <ion-button *ngIf="nextSection" (click)="sectionChanged(nextSection)" fill="solid" color="primary" | ||||
|                 [attr.aria-label]="('core.next' | translate) + ': ' + nextSection.name"> | ||||
|                 <core-format-text class="sr-only" [text]="nextSection.name" contextLevel="course" [contextInstanceId]="course?.id"> | ||||
|                 </core-format-text> | ||||
|                 <ion-icon name="fas-chevron-right" slot="icon-only" aria-hidden="true"></ion-icon> | ||||
|             </ion-button> | ||||
|         </ion-buttons> | ||||
| 
 | ||||
|             <!-- 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> | ||||
| 
 | ||||
|             <ion-buttons class="ion-padding core-course-section-nav-buttons safe-area-padding-horizontal" | ||||
|                 *ngIf="displaySectionSelector && sections?.length"> | ||||
|                 <ion-button *ngIf="previousSection" (click)="sectionChanged(previousSection)" fill="outline" color="primary" | ||||
|                     [attr.aria-label]="('core.previous' | translate) + ': ' + previousSection.name"> | ||||
|                     <ion-icon name="fas-chevron-left" slot="icon-only" aria-hidden="true"></ion-icon> | ||||
|                     <core-format-text class="sr-only" [text]="previousSection.name" contextLevel="course" | ||||
|                         [contextInstanceId]="course?.id"> | ||||
|                     </core-format-text> | ||||
|                 </ion-button> | ||||
|                 <ion-button *ngIf="nextSection" (click)="sectionChanged(nextSection)" fill="solid" color="primary" | ||||
|                     [attr.aria-label]="('core.next' | translate) + ': ' + nextSection.name"> | ||||
|                     <core-format-text class="sr-only" [text]="nextSection.name" contextLevel="course" | ||||
|                         [contextInstanceId]="course?.id"> | ||||
|                     </core-format-text> | ||||
|                     <ion-icon name="fas-chevron-right" slot="icon-only" aria-hidden="true"></ion-icon> | ||||
|                 </ion-button> | ||||
|             </ion-buttons> | ||||
|         </core-loading> | ||||
|     </core-dynamic-component> | ||||
| </core-block-course-blocks> | ||||
|         <core-block-side-blocks-button *ngIf="course && displayBlocks && hasBlocks" [courseId]="course.id" | ||||
|             [downloadEnabled]="downloadEnabled"> | ||||
|         </core-block-side-blocks-button> | ||||
|     </core-loading> | ||||
| </core-dynamic-component> | ||||
| 
 | ||||
| <!-- Template to render a section. --> | ||||
| <ng-template #sectionTemplate let-section="section"> | ||||
|     <section *ngIf="!section.hiddenbynumsections && section.id != allSectionsId && section.id != stealthModulesSectionId"> | ||||
|         <!-- Title is only displayed when viewing all sections. --> | ||||
|         <ion-item-divider *ngIf="selectedSection?.id == allSectionsId && section.name" class="ion-text-wrap" color="light" | ||||
|             [class.core-section-download]="downloadEnabled" | ||||
|             [class.item-dimmed]="section.visible === 0 || section.uservisible === false"> | ||||
|             [class.core-section-download]="downloadEnabled" [class.item-dimmed]="section.visible === 0 || section.uservisible === false"> | ||||
|             <ion-label> | ||||
|                 <h2> | ||||
|                     <core-format-text [text]="section.name" contextLevel="course" [contextInstanceId]="course?.id"> | ||||
|  | ||||
| @ -24,7 +24,6 @@ import { | ||||
|     ViewChildren, | ||||
|     QueryList, | ||||
|     Type, | ||||
|     ViewChild, | ||||
|     ElementRef, | ||||
| } from '@angular/core'; | ||||
| import { ModalOptions } from '@ionic/core'; | ||||
| @ -48,8 +47,8 @@ import { CoreEventObserver, CoreEvents } from '@singletons/events'; | ||||
| import { IonContent, IonRefresher } from '@ionic/angular'; | ||||
| import { CoreUtils } from '@services/utils/utils'; | ||||
| import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate'; | ||||
| import { CoreBlockCourseBlocksComponent } from '@features/block/components/course-blocks/course-blocks'; | ||||
| import { CoreCourseSectionSelectorComponent } from '../section-selector/section-selector'; | ||||
| import { CoreBlockHelper } from '@features/block/services/block-helper'; | ||||
| 
 | ||||
| /** | ||||
|  * Component to display course contents using a certain format. If the format isn't found, use default one. | ||||
| @ -79,7 +78,6 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { | ||||
|     @Output() completionChanged = new EventEmitter<CoreCourseModuleCompletionData>(); // Notify when any module completion changes.
 | ||||
| 
 | ||||
|     @ViewChildren(CoreDynamicComponent) dynamicComponents?: QueryList<CoreDynamicComponent>; | ||||
|     @ViewChild(CoreBlockCourseBlocksComponent) courseBlocksComponent?: CoreBlockCourseBlocksComponent; | ||||
| 
 | ||||
|     // All the possible component classes.
 | ||||
|     courseFormatComponent?: Type<unknown>; | ||||
| @ -92,8 +90,9 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { | ||||
|     showSectionId = 0; | ||||
|     data: Record<string, unknown> = {}; // Data to pass to the components.
 | ||||
| 
 | ||||
|     displaySectionSelector?: boolean; | ||||
|     displayBlocks?: boolean; | ||||
|     displaySectionSelector = false; | ||||
|     displayBlocks = false; | ||||
|     hasBlocks = false; | ||||
|     selectedSection?: CoreCourseSection; | ||||
|     previousSection?: CoreCourseSection; | ||||
|     nextSection?: CoreCourseSection; | ||||
| @ -180,7 +179,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { | ||||
|     /** | ||||
|      * Detect changes on input properties. | ||||
|      */ | ||||
|     ngOnChanges(changes: { [name: string]: SimpleChange }): void { | ||||
|     async ngOnChanges(changes: { [name: string]: SimpleChange }): Promise<void> { | ||||
|         this.setInputData(); | ||||
|         this.sectionSelectorModalOptions.componentProps!.course = this.course; | ||||
|         this.sectionSelectorModalOptions.componentProps!.sections = this.sections; | ||||
| @ -191,6 +190,9 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { | ||||
| 
 | ||||
|             this.displaySectionSelector = CoreCourseFormatDelegate.displaySectionSelector(this.course); | ||||
|             this.displayBlocks = CoreCourseFormatDelegate.displayBlocks(this.course); | ||||
| 
 | ||||
|             this.hasBlocks = await CoreBlockHelper.hasCourseBlocks(this.course.id); | ||||
| 
 | ||||
|             this.updateProgress(); | ||||
| 
 | ||||
|             if ('overviewfiles' in this.course) { | ||||
| @ -498,8 +500,13 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { | ||||
|             await component.callComponentFunction('doRefresh', [refresher, done, afterCompletionChange]); | ||||
|         }) || []; | ||||
| 
 | ||||
|         if (this.courseBlocksComponent) { | ||||
|             promises.push(this.courseBlocksComponent.doRefresh()); | ||||
|         if (this.course) { | ||||
|             const courseId = this.course.id; | ||||
|             promises.push(CoreCourse.invalidateCourseBlocks(courseId).then(async () => { | ||||
|                 this.hasBlocks = await CoreBlockHelper.hasCourseBlocks(courseId); | ||||
| 
 | ||||
|                 return; | ||||
|             })); | ||||
|         } | ||||
| 
 | ||||
|         await Promise.all(promises); | ||||
|  | ||||
| @ -3,11 +3,9 @@ | ||||
|         <ion-icon name="fas-search" slot="icon-only" aria-hidden="true"></ion-icon> | ||||
|     </ion-button> | ||||
|     <core-context-menu> | ||||
|         <core-context-menu-item [priority]="1000" *ngIf="displayEnableDownload" | ||||
|             [content]="'core.settings.showdownloadoptions' | translate" (action)="switchDownload()" | ||||
|             iconAction="toggle" [(toggle)]="downloadEnabled"></core-context-menu-item> | ||||
|         <core-context-menu-item [priority]="500" | ||||
|             [content]="'addon.storagemanager.managestorage' | translate" | ||||
|         <core-context-menu-item [priority]="1000" *ngIf="displayEnableDownload" [content]="'core.settings.showdownloadoptions' | translate" | ||||
|             (action)="switchDownload()" iconAction="toggle" [(toggle)]="downloadEnabled"></core-context-menu-item> | ||||
|         <core-context-menu-item [priority]="500" [content]="'addon.storagemanager.managestorage' | translate" | ||||
|             (action)="manageCoursesStorage()" iconAction="fas-archive"></core-context-menu-item> | ||||
|     </core-context-menu> | ||||
| </core-navbar-buttons> | ||||
| @ -15,49 +13,52 @@ | ||||
|     <ion-refresher slot="fixed" [disabled]="!dataLoaded" (ionRefresh)="doRefresh($event.target)"> | ||||
|         <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content> | ||||
|     </ion-refresher> | ||||
|     <core-block-course-blocks [courseId]="siteHomeId" [downloadEnabled]="downloadEnabled"> | ||||
|         <core-loading [hideUntil]="dataLoaded"> | ||||
|                 <ion-list> | ||||
|                     <!-- Site home main contents. --> | ||||
|                     <ng-container *ngIf="section && section.hasContent"> | ||||
|                         <ion-item class="ion-text-wrap" *ngIf="section.summary"> | ||||
|                             <ion-label><core-format-text [text]="section.summary" contextLevel="course" [contextInstanceId]="siteHomeId"> | ||||
|                             </core-format-text></ion-label> | ||||
|                         </ion-item> | ||||
|     <core-loading [hideUntil]="dataLoaded"> | ||||
|         <ion-list> | ||||
|             <!-- Site home main contents. --> | ||||
|             <ng-container *ngIf="section && section.hasContent"> | ||||
|                 <ion-item class="ion-text-wrap" *ngIf="section.summary"> | ||||
|                     <ion-label> | ||||
|                         <core-format-text [text]="section.summary" contextLevel="course" [contextInstanceId]="siteHomeId"> | ||||
|                         </core-format-text> | ||||
|                     </ion-label> | ||||
|                 </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"> | ||||
|                         <core-spacer *ngIf="section && section!.hasContent"></core-spacer> | ||||
|                         <ng-container *ngFor="let item of items"> | ||||
|                             <ng-container [ngSwitch]="item"> | ||||
|                                 <ng-container *ngSwitchCase="'LIST_OF_COURSE'"> | ||||
|                                     <ng-template *ngTemplateOutlet="allCourseList"></ng-template> | ||||
|                                 </ng-container> | ||||
|                                 <ng-container *ngSwitchCase="'LIST_OF_CATEGORIES'"> | ||||
|                                     <ng-template *ngTemplateOutlet="categories"></ng-template> | ||||
|                                 </ng-container> | ||||
|                                 <ng-container *ngSwitchCase="'COURSE_SEARCH_BOX'"> | ||||
|                                     <ng-template *ngTemplateOutlet="courseSearch"></ng-template> | ||||
|                                 </ng-container> | ||||
|                                 <ng-container *ngSwitchCase="'ENROLLED_COURSES'"> | ||||
|                                     <ng-template *ngTemplateOutlet="enrolledCourseList"></ng-template> | ||||
|                                 </ng-container> | ||||
|                                 <ng-container *ngSwitchCase="'NEWS_ITEMS'"> | ||||
|                                     <ng-template *ngTemplateOutlet="news"></ng-template> | ||||
|                                 </ng-container> | ||||
|                             </ng-container> | ||||
|             <!-- Site home items: news, categories, courses, etc. --> | ||||
|             <ng-container *ngIf="items.length > 0"> | ||||
|                 <core-spacer *ngIf="section && section!.hasContent"></core-spacer> | ||||
|                 <ng-container *ngFor="let item of items"> | ||||
|                     <ng-container [ngSwitch]="item"> | ||||
|                         <ng-container *ngSwitchCase="'LIST_OF_COURSE'"> | ||||
|                             <ng-template *ngTemplateOutlet="allCourseList"></ng-template> | ||||
|                         </ng-container> | ||||
|                         <ng-container *ngSwitchCase="'LIST_OF_CATEGORIES'"> | ||||
|                             <ng-template *ngTemplateOutlet="categories"></ng-template> | ||||
|                         </ng-container> | ||||
|                         <ng-container *ngSwitchCase="'COURSE_SEARCH_BOX'"> | ||||
|                             <ng-template *ngTemplateOutlet="courseSearch"></ng-template> | ||||
|                         </ng-container> | ||||
|                         <ng-container *ngSwitchCase="'ENROLLED_COURSES'"> | ||||
|                             <ng-template *ngTemplateOutlet="enrolledCourseList"></ng-template> | ||||
|                         </ng-container> | ||||
|                         <ng-container *ngSwitchCase="'NEWS_ITEMS'"> | ||||
|                             <ng-template *ngTemplateOutlet="news"></ng-template> | ||||
|                         </ng-container> | ||||
|                     </ng-container> | ||||
|                 </ion-list> | ||||
|             <core-empty-box *ngIf="!hasContent" icon="fas-box-open" [message]="'core.course.nocontentavailable' | translate"> | ||||
|                 </ng-container> | ||||
|             </ng-container> | ||||
|         </ion-list> | ||||
|         <core-block-side-blocks-button *ngIf="hasBlocks" [courseId]="siteHomeId" [downloadEnabled]="downloadEnabled"> | ||||
|         </core-block-side-blocks-button> | ||||
| 
 | ||||
|             </core-empty-box> | ||||
|         </core-loading> | ||||
|     </core-block-course-blocks> | ||||
|         <core-empty-box *ngIf="!hasContent" icon="fas-box-open" [message]="'core.course.nocontentavailable' | translate"> | ||||
| 
 | ||||
|         </core-empty-box> | ||||
|     </core-loading> | ||||
| </ion-content> | ||||
| 
 | ||||
| <ng-template #allCourseList> | ||||
| @ -88,13 +89,17 @@ | ||||
|     <ion-item button class="ion-text-wrap" (click)="openMyCourses()" detail="true"> | ||||
|         <ion-icon name="fas-graduation-cap" fixed-width slot="start" aria-hidden="true"> | ||||
|         </ion-icon> | ||||
|         <ion-label><h2>{{ 'core.courses.mycourses' | translate}}</h2></ion-label> | ||||
|         <ion-label> | ||||
|             <h2>{{ 'core.courses.mycourses' | translate}}</h2> | ||||
|         </ion-label> | ||||
|     </ion-item> | ||||
| </ng-template> | ||||
| 
 | ||||
| <ng-template #courseSearch> | ||||
|     <ion-item button class="ion-text-wrap" (click)="openSearch()" detail="true"> | ||||
|         <ion-icon name="fas-search" slot="start" aria-hidden="true"></ion-icon> | ||||
|         <ion-label><h2>{{ 'core.courses.searchcourses' | translate}}</h2></ion-label> | ||||
|         <ion-label> | ||||
|             <h2>{{ 'core.courses.searchcourses' | translate}}</h2> | ||||
|         </ion-label> | ||||
|     </ion-item> | ||||
| </ng-template> | ||||
|  | ||||
| @ -12,7 +12,7 @@ | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; | ||||
| import { Component, OnDestroy, OnInit } from '@angular/core'; | ||||
| import { IonRefresher } from '@ionic/angular'; | ||||
| import { Params } from '@angular/router'; | ||||
| 
 | ||||
| @ -24,10 +24,11 @@ import { CoreSiteHome } from '@features/sitehome/services/sitehome'; | ||||
| import { CoreCourses, CoreCoursesProvider } from '@features//courses/services/courses'; | ||||
| import { CoreEventObserver, CoreEvents } from '@singletons/events'; | ||||
| import { CoreCourseHelper, CoreCourseModule } from '@features/course/services/course-helper'; | ||||
| import { CoreBlockCourseBlocksComponent } from '@features/block/components/course-blocks/course-blocks'; | ||||
| import { CoreCourseModuleDelegate, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate'; | ||||
| import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate'; | ||||
| import { CoreNavigator } from '@services/navigator'; | ||||
| import { CoreBlockHelper } from '@features/block/services/block-helper'; | ||||
| import { CoreUtils } from '@services/utils/utils'; | ||||
| 
 | ||||
| /** | ||||
|  * Page that displays site home index. | ||||
| @ -38,14 +39,13 @@ import { CoreNavigator } from '@services/navigator'; | ||||
| }) | ||||
| export class CoreSiteHomeIndexPage implements OnInit, OnDestroy { | ||||
| 
 | ||||
|     @ViewChild(CoreBlockCourseBlocksComponent) courseBlocksComponent?: CoreBlockCourseBlocksComponent; | ||||
| 
 | ||||
|     dataLoaded = false; | ||||
|     section?: CoreCourseWSSection & { | ||||
|         hasContent?: boolean; | ||||
|     }; | ||||
| 
 | ||||
|     hasContent = false; | ||||
|     hasBlocks = false; | ||||
|     items: string[] = []; | ||||
|     siteHomeId = 1; | ||||
|     currentSite!: CoreSite; | ||||
| @ -106,8 +106,8 @@ export class CoreSiteHomeIndexPage implements OnInit, OnDestroy { | ||||
|         this.items = await CoreSiteHome.getFrontPageItems(config.frontpageloggedin); | ||||
|         this.hasContent = this.items.length > 0; | ||||
| 
 | ||||
|         if (this.items.some((item) => item == 'NEWS_ITEMS')) { | ||||
|             // Get the news forum.
 | ||||
|         // Get the news forum.
 | ||||
|         if (this.items.includes('NEWS_ITEMS')) { | ||||
|             try { | ||||
|                 const forum = await CoreSiteHome.getNewsForum(this.siteHomeId); | ||||
|                 this.newsForumModule = await CoreCourse.getModule(forum.cmid, forum.course); | ||||
| @ -140,17 +140,17 @@ export class CoreSiteHomeIndexPage implements OnInit, OnDestroy { | ||||
|             } | ||||
| 
 | ||||
|             // Add log in Moodle.
 | ||||
|             CoreCourse.logView( | ||||
|             CoreUtils.ignoreErrors(CoreCourse.logView( | ||||
|                 this.siteHomeId, | ||||
|                 undefined, | ||||
|                 undefined, | ||||
|                 this.currentSite.getInfo()?.sitename, | ||||
|             ).catch(() => { | ||||
|                 // Ignore errors.
 | ||||
|             }); | ||||
|             )); | ||||
|         } catch (error) { | ||||
|             CoreDomUtils.showErrorModalDefault(error, 'core.course.couldnotloadsectioncontent', true); | ||||
|         } | ||||
| 
 | ||||
|         this.hasBlocks = await CoreBlockHelper.hasCourseBlocks(this.siteHomeId); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -170,24 +170,15 @@ export class CoreSiteHomeIndexPage implements OnInit, OnDestroy { | ||||
|             return; | ||||
|         })); | ||||
| 
 | ||||
|         promises.push(CoreCourse.invalidateCourseBlocks(this.siteHomeId)); | ||||
| 
 | ||||
|         if (this.section && this.section.modules) { | ||||
|             // Invalidate modules prefetch data.
 | ||||
|             promises.push(CoreCourseModulePrefetchDelegate.invalidateModules(this.section.modules, this.siteHomeId)); | ||||
|         } | ||||
| 
 | ||||
|         if (this.courseBlocksComponent) { | ||||
|             promises.push(this.courseBlocksComponent.invalidateBlocks()); | ||||
|         } | ||||
| 
 | ||||
|         Promise.all(promises).finally(async () => { | ||||
|             const p2: Promise<unknown>[] = []; | ||||
| 
 | ||||
|             p2.push(this.loadContent()); | ||||
|             if (this.courseBlocksComponent) { | ||||
|                 p2.push(this.courseBlocksComponent.loadContent()); | ||||
|             } | ||||
| 
 | ||||
|             await Promise.all(p2).finally(() => { | ||||
|             await this.loadContent().finally(() => { | ||||
|                 refresher?.complete(); | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
| @ -197,13 +197,6 @@ | ||||
|         --background: var(--core-progressbar-background); | ||||
|     } | ||||
| 
 | ||||
|     --core-side-blocks-max-width: 30%; | ||||
|     --core-side-blocks-min-width: 280px; | ||||
|     core-block-course-blocks { | ||||
|         --side-blocks-max-width: var(--core-side-blocks-max-width); | ||||
|         --side-blocks-min-width: var(--core-side-blocks-min-width); | ||||
|     } | ||||
| 
 | ||||
|     --ion-item-background:        #{$ion-item-background}; | ||||
|     --ion-item-detail-icon-color: var(--gray-darker); | ||||
|     --ion-item-detail-icon-font-size: 20px; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user