MOBILE-3833 course: Collapse sections in downloads page
This commit is contained in:
		
							parent
							
								
									37af8e3c69
								
							
						
					
					
						commit
						0298273fc4
					
				| @ -47,7 +47,13 @@ | ||||
|         <ng-container *ngFor="let section of sections"> | ||||
|             <ion-card class="section" *ngIf="section.modules.length > 0"> | ||||
|                 <ion-card-header> | ||||
|                     <ion-item class="ion-no-padding" lines="full"> | ||||
|                     <ion-item class="ion-no-padding" [lines]="section.expanded ? 'full' : 'none'" button detail="false" | ||||
|                         (click)="toggleExpand($event, section)" [class.core-course-storage-section-expanded]="section.expanded" | ||||
|                         [attr.aria-label]="(section.expanded ? 'core.collapse' : 'core.expand') | translate" | ||||
|                         [attr.aria-expanded]="section.expanded" [attr.aria-controls]="'core-course-storage-section-' + section.id"> | ||||
|                         <ion-icon name="fas-chevron-right" flip-rtl slot="start" class="expandable-status-icon" | ||||
|                             [class.expandable-status-icon-expanded]="section.expanded"> | ||||
|                         </ion-icon> | ||||
|                         <ion-label> | ||||
|                             <p class="item-heading ion-text-wrap"> | ||||
|                                 <core-format-text [text]="section.name" contextLevel="course" [contextInstanceId]="section.course" | ||||
| @ -93,7 +99,8 @@ | ||||
|                         </div> | ||||
|                     </ion-item> | ||||
|                 </ion-card-header> | ||||
|                 <ion-card-content> | ||||
|                 <ion-card-content id="core-course-storage-section-{{section.id}}"> | ||||
|                     <ng-container *ngIf="section.expanded"> | ||||
|                         <ng-container *ngFor="let module of section.modules"> | ||||
|                             <ion-item class="ion-no-padding core-course-storage-activity" | ||||
|                                 *ngIf="downloadEnabled || (!module.calculatingSize && module.totalSize > 0)"> | ||||
| @ -132,6 +139,7 @@ | ||||
|                                 </div> | ||||
|                             </ion-item> | ||||
|                         </ng-container> | ||||
|                     </ng-container> | ||||
|                 </ion-card-content> | ||||
|             </ion-card> | ||||
|         </ng-container> | ||||
|  | ||||
| @ -13,7 +13,7 @@ | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { CoreConstants } from '@/core/constants'; | ||||
| import { Component, OnDestroy, OnInit } from '@angular/core'; | ||||
| import { Component, ElementRef, OnDestroy, OnInit } from '@angular/core'; | ||||
| import { CoreCourse, CoreCourseProvider } from '@features/course/services/course'; | ||||
| import { | ||||
|     CoreCourseHelper, | ||||
| @ -30,6 +30,7 @@ import { CoreSites } from '@services/sites'; | ||||
| import { CoreDomUtils } from '@services/utils/dom'; | ||||
| import { CoreUtils } from '@services/utils/utils'; | ||||
| import { Translate } from '@singletons'; | ||||
| import { CoreDom } from '@singletons/dom'; | ||||
| import { CoreEventObserver, CoreEvents } from '@singletons/events'; | ||||
| 
 | ||||
| /** | ||||
| @ -62,6 +63,7 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy { | ||||
| 
 | ||||
|     statusDownloaded = CoreConstants.DOWNLOADED; | ||||
| 
 | ||||
|     protected initialSectionId?: number; | ||||
|     protected siteUpdatedObserver?: CoreEventObserver; | ||||
|     protected courseStatusObserver?: CoreEventObserver; | ||||
|     protected sectionStatusObserver?: CoreEventObserver; | ||||
| @ -69,7 +71,7 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy { | ||||
|     protected isDestroyed = false; | ||||
|     protected isGuest = false; | ||||
| 
 | ||||
|     constructor() { | ||||
|     constructor(protected elementRef: ElementRef) { | ||||
|         // Refresh the enabled flags if site is updated.
 | ||||
|         this.siteUpdatedObserver = CoreEvents.on(CoreEvents.SITE_UPDATED, () => { | ||||
|             this.downloadCourseEnabled = !CoreCourses.isDownloadCourseDisabledInSite(); | ||||
| @ -100,16 +102,19 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy { | ||||
|         } | ||||
| 
 | ||||
|         this.isGuest = !!CoreNavigator.getRouteBooleanParam('isGuest'); | ||||
|         this.initialSectionId = CoreNavigator.getRouteNumberParam('sectionId'); | ||||
| 
 | ||||
|         this.downloadCourseEnabled = !CoreCourses.isDownloadCourseDisabledInSite(); | ||||
|         this.downloadEnabled = !CoreSites.getRequiredCurrentSite().isOfflineDisabled(); | ||||
| 
 | ||||
|         const sections = await CoreCourse.getSections(this.courseId, false, true); | ||||
|         const sections = (await CoreCourse.getSections(this.courseId, false, true)) | ||||
|             .filter((section) => !CoreCourseHelper.isSectionStealth(section)); | ||||
|         this.sections = (await CoreCourseHelper.addHandlerDataForModules(sections, this.courseId)).sections | ||||
|             .map(section => ({ | ||||
|                 ...section, | ||||
|                 totalSize: 0, | ||||
|                 calculatingSize: true, | ||||
|                 expanded: section.id === this.initialSectionId, | ||||
|                 modules: section.modules.map(module => ({ | ||||
|                     ...module, | ||||
|                     calculatingSize: true, | ||||
| @ -118,6 +123,12 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy { | ||||
| 
 | ||||
|         this.loaded = true; | ||||
| 
 | ||||
|         CoreDom.scrollToElement( | ||||
|             this.elementRef.nativeElement, | ||||
|             '.core-course-storage-section-expanded', | ||||
|             { addYAxis: -10 }, | ||||
|         ); | ||||
| 
 | ||||
|         await Promise.all([ | ||||
|             this.initSizes(), | ||||
|             this.initCoursePrefetch(), | ||||
| @ -641,6 +652,18 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Toggle expand status. | ||||
|      * | ||||
|      * @param event Event object. | ||||
|      * @param section Section to expand / collapse. | ||||
|      */ | ||||
|     toggleExpand(event: Event, section: AddonStorageManagerCourseSection): void { | ||||
|         section.expanded = !section.expanded; | ||||
|         event.stopPropagation(); | ||||
|         event.preventDefault(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
| @ -663,6 +686,7 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy { | ||||
| type AddonStorageManagerCourseSection = Omit<CoreCourseSectionWithStatus, 'modules'> & { | ||||
|     totalSize: number; | ||||
|     calculatingSize: boolean; | ||||
|     expanded: boolean; | ||||
|     modules: AddonStorageManagerModule[]; | ||||
| }; | ||||
| 
 | ||||
|  | ||||
| @ -1,3 +1,8 @@ | ||||
| <core-navbar-buttons slot="end" prepend> | ||||
|     <ion-button fill="clear" (click)="gotoCourseDownloads()" [attr.aria-label]="'addon.storagemanager.coursedownloads' | translate"> | ||||
|         <ion-icon name="fas-cloud-download-alt" slot="icon-only" aria-hidden="true"></ion-icon> | ||||
|     </ion-button> | ||||
| </core-navbar-buttons> | ||||
| <core-dynamic-component [component]="courseFormatComponent" [data]="data"> | ||||
|     <!-- Default course format. --> | ||||
|     <core-loading [hideUntil]="loaded"> | ||||
|  | ||||
| @ -392,15 +392,18 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Display the course index modal. | ||||
|      * Get selected section ID. If viewing all sections, use current scrolled section. | ||||
|      * | ||||
|      * @return Section ID, undefined if not found. | ||||
|      */ | ||||
|     async openCourseIndex(): Promise<void> { | ||||
|         let selectedId = this.selectedSection?.id; | ||||
|     protected async getSelectedSectionId(): Promise<number | undefined> { | ||||
|         if (this.selectedSection?.id !== this.allSectionsId) { | ||||
|             return this.selectedSection?.id; | ||||
|         } | ||||
| 
 | ||||
|         if (selectedId == this.allSectionsId) { | ||||
|         // Check current scrolled section.
 | ||||
|         const allSectionElements: NodeListOf<HTMLElement> = | ||||
|                 this.elementRef.nativeElement.querySelectorAll('section.section-wrapper'); | ||||
|             this.elementRef.nativeElement.querySelectorAll('section.core-course-module-list-wrapper'); | ||||
| 
 | ||||
|         const scroll = await this.content.getScrollElement(); | ||||
|         const containerTop = scroll.getBoundingClientRect().top; | ||||
| @ -412,9 +415,15 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { | ||||
|             return position.bottom >= containerTop; | ||||
|         }); | ||||
| 
 | ||||
|             selectedId = Number(element?.getAttribute('id')) || undefined; | ||||
|         return Number(element?.getAttribute('id')) || undefined; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Display the course index modal. | ||||
|      */ | ||||
|     async openCourseIndex(): Promise<void> { | ||||
|         const selectedId = await this.getSelectedSectionId(); | ||||
| 
 | ||||
|         const data = await CoreDomUtils.openModal<CoreCourseIndexSectionWithModule>({ | ||||
|             component: CoreCourseCourseIndexComponent, | ||||
|             componentProps: { | ||||
| @ -453,6 +462,23 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { | ||||
|         this.moduleId = data.moduleId; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Open course downloads page. | ||||
|      */ | ||||
|     async gotoCourseDownloads(): Promise<void> { | ||||
|         const selectedId = await this.getSelectedSectionId(); | ||||
| 
 | ||||
|         CoreNavigator.navigateToSitePath( | ||||
|             `storage/${this.course.id}`, | ||||
|             { | ||||
|                 params: { | ||||
|                     title: this.course.fullname, | ||||
|                     sectionId: selectedId, | ||||
|                 }, | ||||
|             }, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Function called when selected section changes. | ||||
|      * | ||||
|  | ||||
| @ -48,14 +48,15 @@ | ||||
|                     <ion-icon name="fas-eye-slash" *ngIf="!section.visible && section.uservisible" slot="end" class="restricted" | ||||
|                         [attr.aria-label]="'core.course.hiddenfromstudents' | translate"></ion-icon> | ||||
|                 </ion-item> | ||||
|                 <div id="core-course-index-section-{{section.id}}"> | ||||
|                     <ng-container *ngIf="section.expanded"> | ||||
|                         <ng-container *ngFor="let module of section.modules"> | ||||
|                             <ion-item class="module" [class.item-dimmed]="!module.visible" [class.item-hightlighted]="section.highlighted" | ||||
|                                 (click)="selectSectionOrModule($event, section.id, module.id)" button> | ||||
|                                 <ion-icon class="completioninfo completion_none" name="" *ngIf="module.completionStatus === undefined" | ||||
|                                     slot="start" aria-hidden="true"></ion-icon> | ||||
|                             <ion-icon class="completioninfo completion_incomplete" name="far-circle" *ngIf="module.completionStatus === 0" | ||||
|                                 slot="start" [attr.aria-label]="'core.course.todo' | translate"> | ||||
|                                 <ion-icon class="completioninfo completion_incomplete" name="far-circle" | ||||
|                                     *ngIf="module.completionStatus === 0" slot="start" [attr.aria-label]="'core.course.todo' | translate"> | ||||
|                                 </ion-icon> | ||||
|                                 <ion-icon class="completioninfo completion_complete" name="fas-circle" | ||||
|                                     *ngIf="module.completionStatus === 1 || module.completionStatus === 2" color="success" slot="start" | ||||
| @ -76,6 +77,7 @@ | ||||
|                             </ion-item> | ||||
|                         </ng-container> | ||||
|                     </ng-container> | ||||
|                 </div> | ||||
|             </ng-container> | ||||
|         </ng-container> | ||||
|     </ion-list> | ||||
|  | ||||
| @ -1,8 +1,3 @@ | ||||
| <core-navbar-buttons slot="end" prepend> | ||||
|     <ion-button fill="clear" (click)="gotoCourseDownloads()" [attr.aria-label]="'addon.storagemanager.coursedownloads' | translate"> | ||||
|         <ion-icon name="fas-cloud-download-alt" slot="icon-only" aria-hidden="true"></ion-icon> | ||||
|     </ion-button> | ||||
| </core-navbar-buttons> | ||||
| <ion-content> | ||||
|     <ion-refresher slot="fixed" [disabled]="!dataLoaded || !displayRefresher" (ionRefresh)="doRefresh($event.target)"> | ||||
|         <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content> | ||||
|  | ||||
| @ -366,14 +366,6 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     gotoCourseDownloads(): void { | ||||
|         CoreNavigator.navigateToSitePath( | ||||
|             `storage/${this.course.id}`, | ||||
|             { params: { title: this.course.fullname } }, | ||||
|         ); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user