commit
						ce50a29975
					
				| @ -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,39 @@ | ||||
| // 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'; | ||||
| import { CoreCoursesDashboard } from '@features/courses/services/dashboard'; | ||||
| 
 | ||||
| /** | ||||
|  * 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() courseId?: number; | ||||
|     @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; | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
| @ -67,8 +56,10 @@ export class CoreBlockCourseBlocksComponent implements OnInit { | ||||
|     async invalidateBlocks(): Promise<void> { | ||||
|         const promises: Promise<void>[] = []; | ||||
| 
 | ||||
|         if (CoreBlockHelper.canGetCourseBlocks()) { | ||||
|         if (this.courseId) { | ||||
|             promises.push(CoreCourse.invalidateCourseBlocks(this.courseId)); | ||||
|         } else { | ||||
|             promises.push(CoreCoursesDashboard.invalidateDashboardBlocks()); | ||||
|         } | ||||
| 
 | ||||
|         // Invalidate the blocks.
 | ||||
| @ -87,37 +78,39 @@ export class CoreBlockCourseBlocksComponent implements OnInit { | ||||
|      * @return Promise resolved when done. | ||||
|      */ | ||||
|     async loadContent(): Promise<void> { | ||||
| 
 | ||||
|         try { | ||||
|             if (this.courseId) { | ||||
|                 this.blocks = await CoreBlockHelper.getCourseBlocks(this.courseId); | ||||
|             } else { | ||||
|                 const blocks = await CoreCoursesDashboard.getDashboardBlocks(); | ||||
| 
 | ||||
|                 this.blocks = blocks.sideBlocks; | ||||
|             } | ||||
|         } catch (error) { | ||||
|             CoreDomUtils.showErrorModal(error); | ||||
| 
 | ||||
|             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,10 +6,6 @@ | ||||
|         </core-context-menu-item> | ||||
|     </core-context-menu> | ||||
| </core-navbar-buttons> | ||||
| 
 | ||||
| <core-block-course-blocks *ngIf="loaded" [courseId]="course!.id" [hideBlocks]="!displayBlocks" [downloadEnabled]="downloadEnabled" | ||||
|     [hideBottomBlocks]="selectedSection && selectedSection.id == allSectionsId && canLoadMore"> | ||||
| 
 | ||||
| <core-dynamic-component [component]="courseFormatComponent" [data]="data"> | ||||
|     <!-- Default course format. --> | ||||
|     <core-loading [hideUntil]="loaded"> | ||||
| @ -19,15 +15,10 @@ | ||||
|             <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" | ||||
|                 <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)" | ||||
|                     > | ||||
|                     (onChange)="sectionChanged($event)"> | ||||
|                     <span slot="text"> | ||||
|                         <core-format-text *ngIf="selectedSection" [text]="selectedSection.name" contextLevel="course" | ||||
|                             [contextInstanceId]="course?.id" [clean]="true" [singleLine]="true"> | ||||
| @ -42,16 +33,14 @@ | ||||
| 
 | ||||
|         <!-- 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) || | ||||
|             <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"> | ||||
|                     <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> | ||||
| @ -103,29 +92,29 @@ | ||||
|             <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 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 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-block-side-blocks-button *ngIf="course && displayBlocks && hasBlocks" [courseId]="course.id" | ||||
|             [downloadEnabled]="downloadEnabled"> | ||||
|         </core-block-side-blocks-button> | ||||
|     </core-loading> | ||||
| </core-dynamic-component> | ||||
| </core-block-course-blocks> | ||||
| 
 | ||||
| <!-- 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); | ||||
|  | ||||
| @ -4,10 +4,9 @@ | ||||
|     </ion-button> | ||||
|     <core-context-menu> | ||||
|         <core-context-menu-item *ngIf="(downloadCourseEnabled || downloadCoursesEnabled)" [priority]="1000" | ||||
|             [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" | ||||
|             [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> | ||||
| @ -24,7 +23,9 @@ | ||||
|             </ng-container> | ||||
|         </ion-list> | ||||
| 
 | ||||
|         <core-empty-box *ngIf="blocks.length == 0" icon="fas-th-large" [message]="'core.course.nocontentavailable' | translate"> | ||||
|         <core-block-side-blocks-button *ngIf="hasSideBlocks" [downloadEnabled]="downloadEnabled"></core-block-side-blocks-button> | ||||
| 
 | ||||
|         <core-empty-box *ngIf="blocks.length == 0" icon="fas-cubes" [message]="'core.course.nocontentavailable' | translate"> | ||||
|         </core-empty-box> | ||||
|     </core-loading> | ||||
| </ion-content> | ||||
|  | ||||
| @ -23,6 +23,7 @@ import { CoreDomUtils } from '@services/utils/dom'; | ||||
| import { CoreCourseBlock } from '@features/course/services/course'; | ||||
| import { CoreBlockComponent } from '@features/block/components/block/block'; | ||||
| import { CoreNavigator } from '@services/navigator'; | ||||
| import { CoreBlockDelegate } from '@features/block/services/block-delegate'; | ||||
| 
 | ||||
| /** | ||||
|  * Page that displays the dashboard page. | ||||
| @ -36,6 +37,7 @@ export class CoreCoursesDashboardPage implements OnInit, OnDestroy { | ||||
| 
 | ||||
|     @ViewChildren(CoreBlockComponent) blocksComponents?: QueryList<CoreBlockComponent>; | ||||
| 
 | ||||
|     hasSideBlocks = false; | ||||
|     searchEnabled = false; | ||||
|     downloadEnabled = false; | ||||
|     downloadCourseEnabled = false; | ||||
| @ -88,7 +90,11 @@ export class CoreCoursesDashboardPage implements OnInit, OnDestroy { | ||||
|             this.userId = CoreSites.getCurrentSiteUserId(); | ||||
| 
 | ||||
|             try { | ||||
|                 this.blocks = await CoreCoursesDashboard.getDashboardBlocks(); | ||||
|                 const blocks = await CoreCoursesDashboard.getDashboardBlocks(); | ||||
| 
 | ||||
|                 this.blocks = blocks.mainBlocks; | ||||
| 
 | ||||
|                 this.hasSideBlocks = CoreBlockDelegate.hasSupportedBlock(blocks.sideBlocks); | ||||
|             } catch (error) { | ||||
|                 CoreDomUtils.showErrorModal(error); | ||||
| 
 | ||||
|  | ||||
| @ -38,14 +38,14 @@ export class CoreCoursesDashboardProvider { | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get dashboard blocks. | ||||
|      * Get dashboard blocks from WS. | ||||
|      * | ||||
|      * @param userId User ID. Default, current user. | ||||
|      * @param siteId Site ID. If not defined, current site. | ||||
|      * @return Promise resolved with the list of blocks. | ||||
|      * @since 3.6 | ||||
|      */ | ||||
|     async getDashboardBlocks(userId?: number, siteId?: string): Promise<CoreCourseBlock[]> { | ||||
|     protected async getDashboardBlocksFromWS(userId?: number, siteId?: string): Promise<CoreCourseBlock[]> { | ||||
|         const site = await CoreSites.getSite(siteId); | ||||
| 
 | ||||
|         const params: CoreBlockGetDashboardBlocksWSParams = { | ||||
| @ -63,6 +63,44 @@ export class CoreCoursesDashboardProvider { | ||||
|         return result.blocks || []; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get dashboard blocks. | ||||
|      * | ||||
|      * @param userId User ID. Default, current user. | ||||
|      * @param siteId Site ID. If not defined, current site. | ||||
|      * @return Promise resolved with the list of blocks. | ||||
|      */ | ||||
|     async getDashboardBlocks(userId?: number, siteId?: string): Promise<CoreCoursesDashboardBlocks> { | ||||
|         const blocks = await CoreCoursesDashboard.getDashboardBlocksFromWS(userId, siteId); | ||||
| 
 | ||||
|         let mainBlocks: CoreCourseBlock[] = []; | ||||
|         let sideBlocks: CoreCourseBlock[] = []; | ||||
| 
 | ||||
|         blocks.forEach((block) => { | ||||
|             if (block.region == 'content' || block.region == 'main') { | ||||
|                 mainBlocks.push(block); | ||||
|             } else { | ||||
|                 sideBlocks.push(block); | ||||
|             } | ||||
|         }); | ||||
| 
 | ||||
|         if (mainBlocks.length == 0) { | ||||
|             mainBlocks = []; | ||||
|             sideBlocks = []; | ||||
| 
 | ||||
|             blocks.forEach((block) => { | ||||
|                 if (block.region.match('side')) { | ||||
|                     sideBlocks.push(block); | ||||
|                 } else { | ||||
|                     mainBlocks.push(block); | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
| 
 | ||||
|         return { mainBlocks, sideBlocks }; | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Invalidates dashboard blocks WS call. | ||||
|      * | ||||
| @ -122,6 +160,11 @@ export class CoreCoursesDashboardProvider { | ||||
| 
 | ||||
| export const CoreCoursesDashboard = makeSingleton(CoreCoursesDashboardProvider); | ||||
| 
 | ||||
| export type CoreCoursesDashboardBlocks = { | ||||
|     mainBlocks: CoreCourseBlock[]; | ||||
|     sideBlocks: CoreCourseBlock[]; | ||||
| }; | ||||
| 
 | ||||
| /** | ||||
|  * Params of core_block_get_dashboard_blocks WS. | ||||
|  */ | ||||
|  | ||||
| @ -68,7 +68,7 @@ export class CoreDashboardHomeHandlerService implements CoreMainMenuHomeHandler | ||||
|         if (dashboardAvailable && blocksEnabled) { | ||||
|             const blocks = await CoreCoursesDashboard.getDashboardBlocks(undefined, siteId); | ||||
| 
 | ||||
|             return CoreBlockDelegate.hasSupportedBlock(blocks); | ||||
|             return CoreBlockDelegate.hasSupportedBlock(blocks.mainBlocks) || CoreBlockDelegate.hasSupportedBlock(blocks.sideBlocks); | ||||
|         } | ||||
| 
 | ||||
|         // Check if my overview is enabled. If it's enabled we will fake enabled blocks.
 | ||||
|  | ||||
| @ -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,14 +13,15 @@ | ||||
|     <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-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" | ||||
| @ -53,11 +52,13 @@ | ||||
|                 </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 *ngIf="!hasContent" icon="fas-box-open" [message]="'core.course.nocontentavailable' | translate"> | ||||
| 
 | ||||
|         </core-empty-box> | ||||
|     </core-loading> | ||||
|     </core-block-course-blocks> | ||||
| </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.
 | ||||
|         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(); | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
| @ -29,7 +29,15 @@ import { CoreIonLoadingElement } from '@classes/ion-loading'; | ||||
| import { CoreCanceledError } from '@classes/errors/cancelederror'; | ||||
| import { CoreAnyError, CoreError } from '@classes/errors/error'; | ||||
| import { CoreSilentError } from '@classes/errors/silenterror'; | ||||
| import { makeSingleton, Translate, AlertController, ToastController, PopoverController, ModalController } from '@singletons'; | ||||
| import { | ||||
|     makeSingleton, | ||||
|     Translate, | ||||
|     AlertController, | ||||
|     ToastController, | ||||
|     PopoverController, | ||||
|     ModalController, | ||||
|     Router, | ||||
| } from '@singletons'; | ||||
| import { CoreLogger } from '@singletons/logger'; | ||||
| import { CoreFileSizeSum } from '@services/plugin-file-delegate'; | ||||
| import { CoreNetworkError } from '@classes/errors/network-error'; | ||||
| @ -41,6 +49,9 @@ import { CoreZoomLevel } from '@features/settings/services/settings-helper'; | ||||
| import { CoreErrorWithTitle } from '@classes/errors/errorwithtitle'; | ||||
| import { AddonFilterMultilangHandler } from '@addons/filter/multilang/services/handlers/multilang'; | ||||
| import { CoreSites } from '@services/sites'; | ||||
| import { NavigationStart } from '@angular/router'; | ||||
| import { filter } from 'rxjs/operators'; | ||||
| import { Subscription } from 'rxjs'; | ||||
| 
 | ||||
| /* | ||||
|  * "Utils" service with helper functions for UI, DOM elements and HTML code. | ||||
| @ -1651,18 +1662,32 @@ export class CoreDomUtilsProvider { | ||||
|     /** | ||||
|      * Opens a Modal. | ||||
|      * | ||||
|      * @param modalOptions Modal Options. | ||||
|      * @param options Modal Options. | ||||
|      */ | ||||
|     async openModal<T = unknown>( | ||||
|         modalOptions: ModalOptions, | ||||
|         options: OpenModalOptions, | ||||
|     ): Promise<T | undefined> { | ||||
| 
 | ||||
|         const { waitForDismissCompleted, closeOnNavigate, ...modalOptions } = options; | ||||
|         const listenCloseEvents = closeOnNavigate ?? true; // Default to true.
 | ||||
| 
 | ||||
|         const modal = await ModalController.create(modalOptions); | ||||
| 
 | ||||
|         let navSubscription: Subscription | undefined; | ||||
|         if (listenCloseEvents) { | ||||
|             // Listen navigation events to close modals.
 | ||||
|             navSubscription = Router.events | ||||
|                 .pipe(filter(event => event instanceof NavigationStart)) | ||||
|                 .subscribe(async () => { | ||||
|                     modal.dismiss(); | ||||
|                 }); | ||||
|         } | ||||
| 
 | ||||
|         await modal.present(); | ||||
| 
 | ||||
|         // If onDidDismiss is nedded we can add a new param to the function to wait one function or the other.
 | ||||
|         const result = await modal.onWillDismiss<T>(); | ||||
|         const result = waitForDismissCompleted ? await modal.onDidDismiss<T>() : await modal.onWillDismiss<T>(); | ||||
|         navSubscription?.unsubscribe(); | ||||
| 
 | ||||
|         if (result?.data) { | ||||
|             return result?.data; | ||||
|         } | ||||
| @ -1671,21 +1696,21 @@ export class CoreDomUtilsProvider { | ||||
|     /** | ||||
|      * Opens a side Modal. | ||||
|      * | ||||
|      * @param modalOptions Modal Options. | ||||
|      * @param options Modal Options. | ||||
|      */ | ||||
|     async openSideModal<T = unknown>( | ||||
|         modalOptions: ModalOptions, | ||||
|         options: OpenModalOptions, | ||||
|     ): Promise<T | undefined> { | ||||
| 
 | ||||
|         modalOptions = Object.assign({ | ||||
|         options = Object.assign({ | ||||
|             cssClass: 'core-modal-lateral', | ||||
|             showBackdrop: true, | ||||
|             backdropDismiss: true, | ||||
|             enterAnimation: CoreModalLateralTransitionEnter, | ||||
|             leaveAnimation: CoreModalLateralTransitionLeave, | ||||
|         }, modalOptions); | ||||
|         }, options); | ||||
| 
 | ||||
|         return await this.openModal<T>(modalOptions); | ||||
|         return await this.openModal<T>(options); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -2012,3 +2037,11 @@ export const CoreDomUtils = makeSingleton(CoreDomUtilsProvider); | ||||
| export type OpenPopoverOptions = PopoverOptions & { | ||||
|     waitForDismissCompleted?: boolean; | ||||
| }; | ||||
| 
 | ||||
| /** | ||||
|  * Options for the openModal function. | ||||
|  */ | ||||
| export type OpenModalOptions = ModalOptions & { | ||||
|     waitForDismissCompleted?: boolean; | ||||
|     closeOnNavigate?: boolean; // Default true.
 | ||||
| }; | ||||
|  | ||||
| @ -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