forked from CIT/Vmeda.Online
		
	Merge pull request #4154 from crazyserver/MOBILE-4636
MOBILE-4636 course: Enclose sections in cards instead of modules
This commit is contained in:
		
						commit
						404cbf7965
					
				@ -4,7 +4,7 @@
 | 
				
			|||||||
    </ion-label>
 | 
					    </ion-label>
 | 
				
			||||||
</ion-item-divider>
 | 
					</ion-item-divider>
 | 
				
			||||||
<core-loading [hideUntil]="loaded">
 | 
					<core-loading [hideUntil]="loaded">
 | 
				
			||||||
    <ion-list *ngIf="mainMenuBlock" class="core-course-module-list-wrapper">
 | 
					    <ion-list *ngIf="mainMenuBlock" class="core-course-module-list-wrapper list-item-limited-width">
 | 
				
			||||||
        <ion-item class="ion-text-wrap" *ngIf="mainMenuBlock.summary">
 | 
					        <ion-item class="ion-text-wrap" *ngIf="mainMenuBlock.summary">
 | 
				
			||||||
            <ion-label>
 | 
					            <ion-label>
 | 
				
			||||||
                <core-format-text [text]="mainMenuBlock.summary" [component]="component" [componentId]="siteHomeId" contextLevel="course"
 | 
					                <core-format-text [text]="mainMenuBlock.summary" [component]="component" [componentId]="siteHomeId" contextLevel="course"
 | 
				
			||||||
 | 
				
			|||||||
@ -112,6 +112,13 @@ export class CoreInfiniteLoadingComponent implements OnChanges {
 | 
				
			|||||||
        this.action.emit(() => this.complete());
 | 
					        this.action.emit(() => this.complete());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Fire the infinite scroll load more action if needed.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async fireInfiniteScrollIfNeeded(): Promise<void> {
 | 
				
			||||||
 | 
					        this.checkScrollDistance();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Complete loading.
 | 
					     * Complete loading.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
 | 
				
			|||||||
@ -8,22 +8,27 @@
 | 
				
			|||||||
    <core-loading [hideUntil]="loaded">
 | 
					    <core-loading [hideUntil]="loaded">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <!-- Single section. -->
 | 
					        <!-- Single section. -->
 | 
				
			||||||
        <div *ngIf="selectedSection && selectedSection.id !== allSectionsId">
 | 
					        <div *ngIf="selectedSection && selectedSection.id !== allSectionsId" class="single-section list-item-limited-width">
 | 
				
			||||||
            <core-dynamic-component [component]="singleSectionComponent" [data]="data">
 | 
					            <core-dynamic-component [component]="singleSectionComponent" [data]="data">
 | 
				
			||||||
                <ng-container *ngTemplateOutlet="sectionTemplate; context: {section: selectedSection}" />
 | 
					                <ion-accordion-group [readonly]="true" value="single">
 | 
				
			||||||
 | 
					                    <ng-container *ngTemplateOutlet="sectionTemplate; context: {section: selectedSection, sectionId: 'single'}" />
 | 
				
			||||||
 | 
					                </ion-accordion-group>
 | 
				
			||||||
                <core-empty-box *ngIf="!selectedSection.hasContent" icon="fas-table-cells-large"
 | 
					                <core-empty-box *ngIf="!selectedSection.hasContent" icon="fas-table-cells-large"
 | 
				
			||||||
                    [message]="'core.course.nocontentavailable' | translate" />
 | 
					                    [message]="'core.course.nocontentavailable' | translate" />
 | 
				
			||||||
            </core-dynamic-component>
 | 
					            </core-dynamic-component>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <!-- Multiple sections. -->
 | 
					        <!-- Multiple sections. -->
 | 
				
			||||||
        <div *ngIf="selectedSection && selectedSection.id === allSectionsId">
 | 
					        <div *ngIf="selectedSection && selectedSection.id === allSectionsId" class="multiple-sections list-item-limited-width">
 | 
				
			||||||
            <core-dynamic-component [component]="allSectionsComponent" [data]="data">
 | 
					            <core-dynamic-component [component]="allSectionsComponent" [data]="data">
 | 
				
			||||||
                <ng-container *ngFor="let section of sections; index as i">
 | 
					                <ion-accordion-group [multiple]="true" (ionChange)="accordionMultipleChange($event.detail)" [value]="accordionMultipleValue"
 | 
				
			||||||
                    <ng-container *ngIf="i <= lastShownSectionIndex">
 | 
					                    #accordionMultiple>
 | 
				
			||||||
                        <ng-container *ngTemplateOutlet="sectionTemplate; context: {section: section}" />
 | 
					                    @for (section of sections; track section.id) {
 | 
				
			||||||
                    </ng-container>
 | 
					                        @if ($index <= lastShownSectionIndex) {
 | 
				
			||||||
                </ng-container>
 | 
					                            <ng-container *ngTemplateOutlet="sectionTemplate; context: {section: section, sectionId: section.id}" />
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                </ion-accordion-group>
 | 
				
			||||||
            </core-dynamic-component>
 | 
					            </core-dynamic-component>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <core-infinite-loading [enabled]="canLoadMore" (action)="showMoreActivities($event)" />
 | 
					            <core-infinite-loading [enabled]="canLoadMore" (action)="showMoreActivities($event)" />
 | 
				
			||||||
@ -62,12 +67,12 @@
 | 
				
			|||||||
</ion-fab>
 | 
					</ion-fab>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<!-- Template to render a section. -->
 | 
					<!-- Template to render a section. -->
 | 
				
			||||||
<ng-template #sectionTemplate let-section="section">
 | 
					<ng-template #sectionTemplate let-section="section" let-sectionId="sectionId">
 | 
				
			||||||
    <section *ngIf="!section.hiddenbynumsections && section.id !== allSectionsId && section.id !== stealthModulesSectionId"
 | 
					    <ion-accordion *ngIf="!section.hiddenbynumsections && section.id !== allSectionsId && section.id !== stealthModulesSectionId"
 | 
				
			||||||
        class="core-course-module-list-wrapper" [id]="section.id"
 | 
					        class="core-course-module-list-wrapper" [id]="section.id"
 | 
				
			||||||
        [attr.aria-labelledby]="section.name ? 'core-section-name-' + section.id : null">
 | 
					        [attr.aria-labelledby]="section.name ? 'core-section-name-' + section.id : null" [value]="''+sectionId" toggleIconSlot="start">
 | 
				
			||||||
        <ion-item-divider class="course-section ion-text-wrap" [class.item-dimmed]="section.visible === 0 || section.uservisible === false">
 | 
					        <ion-item class="course-section divider" [class.item-dimmed]="section.visible === 0 || section.uservisible === false" slot="header">
 | 
				
			||||||
            <ion-label>
 | 
					            <ion-label class="ion-text-wrap">
 | 
				
			||||||
                <h2 *ngIf="section.name" class="big" [id]="'core-section-name-' + section.id">
 | 
					                <h2 *ngIf="section.name" class="big" [id]="'core-section-name-' + section.id">
 | 
				
			||||||
                    <core-format-text [text]="section.name" contextLevel="course" [contextInstanceId]="course.id" />
 | 
					                    <core-format-text [text]="section.name" contextLevel="course" [contextInstanceId]="course.id" />
 | 
				
			||||||
                </h2>
 | 
					                </h2>
 | 
				
			||||||
@ -91,19 +96,24 @@
 | 
				
			|||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
            </ion-label>
 | 
					            </ion-label>
 | 
				
			||||||
            <ion-badge *ngIf="section.highlighted && highlighted" slot="end">{{highlighted}}</ion-badge>
 | 
					            <ion-badge *ngIf="section.highlighted && highlighted" slot="end">{{highlighted}}</ion-badge>
 | 
				
			||||||
        </ion-item-divider>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        <ion-item class="ion-text-wrap section-summary" *ngIf="section.summary">
 | 
					 | 
				
			||||||
            <ion-label>
 | 
					 | 
				
			||||||
                <core-format-text [text]="section.summary" contextLevel="course" [contextInstanceId]="course.id" />
 | 
					 | 
				
			||||||
            </ion-label>
 | 
					 | 
				
			||||||
        </ion-item>
 | 
					        </ion-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <ng-container *ngFor="let module of section.modules">
 | 
					        <div slot="content">
 | 
				
			||||||
            <core-course-module *ngIf="module.visibleoncoursepage !== 0" [module]="module" [section]="section"
 | 
					            <ng-container *ngIf="section.expanded">
 | 
				
			||||||
                [showActivityDates]="course.showactivitydates" [showCompletionConditions]="course.showcompletionconditions"
 | 
					                <ion-item class="ion-text-wrap section-summary" *ngIf="section.summary">
 | 
				
			||||||
                [isLastViewed]="lastModuleViewed && lastModuleViewed.cmId === module.id" [class.core-course-module-not-viewed]="
 | 
					                    <ion-label>
 | 
				
			||||||
                    !viewedModules[module.id] && (!module.completiondata || module.completiondata.state === completionStatusIncomplete)" />
 | 
					                        <core-format-text [text]="section.summary" contextLevel="course" [contextInstanceId]="course.id" />
 | 
				
			||||||
        </ng-container>
 | 
					                    </ion-label>
 | 
				
			||||||
    </section>
 | 
					                </ion-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <ng-container *ngFor="let module of section.modules">
 | 
				
			||||||
 | 
					                    <core-course-module *ngIf="module.visibleoncoursepage !== 0" [module]="module" [section]="section"
 | 
				
			||||||
 | 
					                        [showActivityDates]="course.showactivitydates" [showCompletionConditions]="course.showcompletionconditions"
 | 
				
			||||||
 | 
					                        [isLastViewed]="lastModuleViewed && lastModuleViewed.cmId === module.id"
 | 
				
			||||||
 | 
					                        [class.core-course-module-not-viewed]="
 | 
				
			||||||
 | 
					                            !viewedModules[module.id] && (!module.completiondata || module.completiondata.state === completionStatusIncomplete)" />
 | 
				
			||||||
 | 
					                </ng-container>
 | 
				
			||||||
 | 
					            </ng-container>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </ion-accordion>
 | 
				
			||||||
</ng-template>
 | 
					</ng-template>
 | 
				
			||||||
 | 
				
			|||||||
@ -14,3 +14,26 @@
 | 
				
			|||||||
.course-section {
 | 
					.course-section {
 | 
				
			||||||
    --inner-padding-end: 12px;
 | 
					    --inner-padding-end: 12px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.multiple-sections .core-course-module-list-wrapper {
 | 
				
			||||||
 | 
					    border: var(--ion-card-border-width) solid var(--ion-card-border-color);
 | 
				
			||||||
 | 
					    border-radius: var(--ion-card-radius);
 | 
				
			||||||
 | 
					    margin: 8px 4px;
 | 
				
			||||||
 | 
					    width: calc(100% - 8px);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ion-card {
 | 
				
			||||||
 | 
					        --ion-card-background: transparent;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ion-item.divider.course-section {
 | 
				
			||||||
 | 
					        --background: transparent;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.single-section ::ng-deep {
 | 
				
			||||||
 | 
					    ion-item.divider.course-section {
 | 
				
			||||||
 | 
					        ion-icon.ion-accordion-toggle-icon {
 | 
				
			||||||
 | 
					            display: none;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -24,6 +24,7 @@ import {
 | 
				
			|||||||
    Type,
 | 
					    Type,
 | 
				
			||||||
    ElementRef,
 | 
					    ElementRef,
 | 
				
			||||||
    ChangeDetectorRef,
 | 
					    ChangeDetectorRef,
 | 
				
			||||||
 | 
					    ViewChild,
 | 
				
			||||||
} from '@angular/core';
 | 
					} from '@angular/core';
 | 
				
			||||||
import { CoreDomUtils } from '@services/utils/dom';
 | 
					import { CoreDomUtils } from '@services/utils/dom';
 | 
				
			||||||
import { CoreDynamicComponent } from '@components/dynamic-component/dynamic-component';
 | 
					import { CoreDynamicComponent } from '@components/dynamic-component/dynamic-component';
 | 
				
			||||||
@ -39,7 +40,7 @@ import {
 | 
				
			|||||||
} from '@features/course/services/course-helper';
 | 
					} from '@features/course/services/course-helper';
 | 
				
			||||||
import { CoreCourseFormatDelegate } from '@features/course/services/format-delegate';
 | 
					import { CoreCourseFormatDelegate } from '@features/course/services/format-delegate';
 | 
				
			||||||
import { CoreEventObserver, CoreEvents } from '@singletons/events';
 | 
					import { CoreEventObserver, CoreEvents } from '@singletons/events';
 | 
				
			||||||
import { IonContent } from '@ionic/angular';
 | 
					import { AccordionGroupChangeEventDetail, IonContent } from '@ionic/angular';
 | 
				
			||||||
import { CoreUtils } from '@services/utils/utils';
 | 
					import { CoreUtils } from '@services/utils/utils';
 | 
				
			||||||
import { CoreCourseIndexSectionWithModule } from '../course-index/course-index';
 | 
					import { CoreCourseIndexSectionWithModule } from '../course-index/course-index';
 | 
				
			||||||
import { CoreBlockHelper } from '@features/block/services/block-helper';
 | 
					import { CoreBlockHelper } from '@features/block/services/block-helper';
 | 
				
			||||||
@ -57,8 +58,10 @@ import { CoreSharedModule } from '@/core/shared.module';
 | 
				
			|||||||
import { CoreBlockComponentsModule } from '@features/block/components/components.module';
 | 
					import { CoreBlockComponentsModule } from '@features/block/components/components.module';
 | 
				
			||||||
import { CoreCourseComponentsModule } from '../components.module';
 | 
					import { CoreCourseComponentsModule } from '../components.module';
 | 
				
			||||||
import { CoreSites } from '@services/sites';
 | 
					import { CoreSites } from '@services/sites';
 | 
				
			||||||
import { COURSE_ALL_SECTIONS_PREFERRED_PREFIX } from '@features/course/constants';
 | 
					import { COURSE_ALL_SECTIONS_PREFERRED_PREFIX, COURSE_EXPANDED_SECTIONS_PREFIX } from '@features/course/constants';
 | 
				
			||||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
					import { toBoolean } from '@/core/transforms/boolean';
 | 
				
			||||||
 | 
					import { CoreInfiniteLoadingComponent } from '@components/infinite-loading/infinite-loading';
 | 
				
			||||||
 | 
					import { CoreSite } from '@classes/sites/site';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Component to display course contents using a certain format. If the format isn't found, use default one.
 | 
					 * Component to display course contents using a certain format. If the format isn't found, use default one.
 | 
				
			||||||
@ -96,6 +99,10 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
 | 
				
			|||||||
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | 
					    // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | 
				
			||||||
    @ViewChildren(CoreDynamicComponent) dynamicComponents?: QueryList<CoreDynamicComponent<any>>;
 | 
					    @ViewChildren(CoreDynamicComponent) dynamicComponents?: QueryList<CoreDynamicComponent<any>>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @ViewChild(CoreInfiniteLoadingComponent) infiteLoading?: CoreInfiniteLoadingComponent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    accordionMultipleValue: string[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // All the possible component classes.
 | 
					    // All the possible component classes.
 | 
				
			||||||
    courseFormatComponent?: Type<unknown>;
 | 
					    courseFormatComponent?: Type<unknown>;
 | 
				
			||||||
    singleSectionComponent?: Type<unknown>;
 | 
					    singleSectionComponent?: Type<unknown>;
 | 
				
			||||||
@ -119,9 +126,9 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
 | 
				
			|||||||
    displayCourseIndex = false;
 | 
					    displayCourseIndex = false;
 | 
				
			||||||
    displayBlocks = false;
 | 
					    displayBlocks = false;
 | 
				
			||||||
    hasBlocks = false;
 | 
					    hasBlocks = false;
 | 
				
			||||||
    selectedSection?: CoreCourseSection;
 | 
					    selectedSection?: CoreCourseSectionToDisplay;
 | 
				
			||||||
    previousSection?: CoreCourseSection;
 | 
					    previousSection?: CoreCourseSectionToDisplay;
 | 
				
			||||||
    nextSection?: CoreCourseSection;
 | 
					    nextSection?: CoreCourseSectionToDisplay;
 | 
				
			||||||
    allSectionsId: number = CoreCourseProvider.ALL_SECTIONS_ID;
 | 
					    allSectionsId: number = CoreCourseProvider.ALL_SECTIONS_ID;
 | 
				
			||||||
    stealthModulesSectionId: number = CoreCourseProvider.STEALTH_MODULES_SECTION_ID;
 | 
					    stealthModulesSectionId: number = CoreCourseProvider.STEALTH_MODULES_SECTION_ID;
 | 
				
			||||||
    loaded = false;
 | 
					    loaded = false;
 | 
				
			||||||
@ -136,6 +143,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
 | 
				
			|||||||
    protected modViewedObserver?: CoreEventObserver;
 | 
					    protected modViewedObserver?: CoreEventObserver;
 | 
				
			||||||
    protected lastCourseFormat?: string;
 | 
					    protected lastCourseFormat?: string;
 | 
				
			||||||
    protected viewedModulesInitialized = false;
 | 
					    protected viewedModulesInitialized = false;
 | 
				
			||||||
 | 
					    protected currentSite?: CoreSite;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(
 | 
					    constructor(
 | 
				
			||||||
        protected content: IonContent,
 | 
					        protected content: IonContent,
 | 
				
			||||||
@ -158,6 +166,8 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
 | 
				
			|||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.currentSite = CoreSites.getRequiredCurrentSite();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Listen for select course tab events to select the right section if needed.
 | 
					        // Listen for select course tab events to select the right section if needed.
 | 
				
			||||||
        this.selectTabObserver = CoreEvents.on(CoreEvents.SELECT_COURSE_TAB, (data) => {
 | 
					        this.selectTabObserver = CoreEvents.on(CoreEvents.SELECT_COURSE_TAB, (data) => {
 | 
				
			||||||
            if (data.name) {
 | 
					            if (data.name) {
 | 
				
			||||||
@ -196,10 +206,12 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
            this.changeDetectorRef.markForCheck();
 | 
					            this.changeDetectorRef.markForCheck();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.initializeExpandedSections();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Detect changes on input properties.
 | 
					     * @inheritdoc
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    async ngOnChanges(changes: { [name: string]: SimpleChange }): Promise<void> {
 | 
					    async ngOnChanges(changes: { [name: string]: SimpleChange }): Promise<void> {
 | 
				
			||||||
        this.setInputData();
 | 
					        this.setInputData();
 | 
				
			||||||
@ -287,14 +299,12 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
 | 
				
			|||||||
     * Treat received sections.
 | 
					     * Treat received sections.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param sections Sections to treat.
 | 
					     * @param sections Sections to treat.
 | 
				
			||||||
     * @returns Promise resolved when done.
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    protected async treatSections(sections: CoreCourseSection[]): Promise<void> {
 | 
					    protected async treatSections(sections: CoreCourseSectionToDisplay[]): Promise<void> {
 | 
				
			||||||
        const hasAllSections = sections[0].id == CoreCourseProvider.ALL_SECTIONS_ID;
 | 
					        const hasAllSections = sections[0].id == CoreCourseProvider.ALL_SECTIONS_ID;
 | 
				
			||||||
        const hasSeveralSections = sections.length > 2 || (sections.length == 2 && !hasAllSections);
 | 
					        const hasSeveralSections = sections.length > 2 || (sections.length == 2 && !hasAllSections);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await this.initializeViewedModules();
 | 
					        await this.initializeViewedModules();
 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (this.selectedSection) {
 | 
					        if (this.selectedSection) {
 | 
				
			||||||
            const selectedSection = this.selectedSection;
 | 
					            const selectedSection = this.selectedSection;
 | 
				
			||||||
            // We have a selected section, but the list has changed. Search the section in the list.
 | 
					            // We have a selected section, but the list has changed. Search the section in the list.
 | 
				
			||||||
@ -366,14 +376,10 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
 | 
				
			|||||||
            this.loaded = true;
 | 
					            this.loaded = true;
 | 
				
			||||||
            this.sectionChanged(section, moduleId);
 | 
					            this.sectionChanged(section, moduleId);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Initialize viewed modules.
 | 
					     * Initialize viewed modules.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @returns Promise resolved when done.
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    protected async initializeViewedModules(): Promise<void> {
 | 
					    protected async initializeViewedModules(): Promise<void> {
 | 
				
			||||||
        if (this.viewedModulesInitialized) {
 | 
					        if (this.viewedModulesInitialized) {
 | 
				
			||||||
@ -387,6 +393,13 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
 | 
				
			|||||||
        viewedModules.forEach(entry => {
 | 
					        viewedModules.forEach(entry => {
 | 
				
			||||||
            this.viewedModules[entry.cmId] = true;
 | 
					            this.viewedModules[entry.cmId] = true;
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (this.lastModuleViewed) {
 | 
				
			||||||
 | 
					            const section = this.getViewedModuleSection(this.sections, this.lastModuleViewed);
 | 
				
			||||||
 | 
					            if (section) {
 | 
				
			||||||
 | 
					                this.setSectionExpanded(section);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
@ -426,7 +439,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        // Check current scrolled section.
 | 
					        // Check current scrolled section.
 | 
				
			||||||
        const allSectionElements: NodeListOf<HTMLElement> =
 | 
					        const allSectionElements: NodeListOf<HTMLElement> =
 | 
				
			||||||
            this.elementRef.nativeElement.querySelectorAll('section.core-course-module-list-wrapper');
 | 
					            this.elementRef.nativeElement.querySelectorAll('.core-course-module-list-wrapper');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const scroll = await this.content.getScrollElement();
 | 
					        const scroll = await this.content.getScrollElement();
 | 
				
			||||||
        const containerTop = scroll.getBoundingClientRect().top;
 | 
					        const containerTop = scroll.getBoundingClientRect().top;
 | 
				
			||||||
@ -515,12 +528,15 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
 | 
				
			|||||||
     * @param newSection The new selected section.
 | 
					     * @param newSection The new selected section.
 | 
				
			||||||
     * @param moduleId The module to scroll to.
 | 
					     * @param moduleId The module to scroll to.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    sectionChanged(newSection: CoreCourseSection, moduleId?: number): void {
 | 
					    sectionChanged(newSection: CoreCourseSectionToDisplay, moduleId?: number): void {
 | 
				
			||||||
        const previousValue = this.selectedSection;
 | 
					        const previousValue = this.selectedSection;
 | 
				
			||||||
        this.selectedSection = newSection;
 | 
					        this.selectedSection = newSection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.data.section = this.selectedSection;
 | 
					        this.data.section = this.selectedSection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (newSection.id !== this.allSectionsId) {
 | 
					        if (newSection.id !== this.allSectionsId) {
 | 
				
			||||||
 | 
					            this.setSectionExpanded(newSection);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Select next and previous sections to show the arrows.
 | 
					            // Select next and previous sections to show the arrows.
 | 
				
			||||||
            const i = this.sections.findIndex((value) => this.compareSections(value, newSection));
 | 
					            const i = this.sections.findIndex((value) => this.compareSections(value, newSection));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -627,7 +643,10 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
 | 
				
			|||||||
            modulesLoaded < CoreCourseFormatComponent.LOAD_MORE_ACTIVITIES) {
 | 
					            modulesLoaded < CoreCourseFormatComponent.LOAD_MORE_ACTIVITIES) {
 | 
				
			||||||
            this.lastShownSectionIndex++;
 | 
					            this.lastShownSectionIndex++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (!this.sections[this.lastShownSectionIndex].hasContent || !this.sections[this.lastShownSectionIndex].modules) {
 | 
					            // Skip sections without content, with stealth modules or collapsed.
 | 
				
			||||||
 | 
					            if (!this.sections[this.lastShownSectionIndex].hasContent ||
 | 
				
			||||||
 | 
					                !this.sections[this.lastShownSectionIndex].modules ||
 | 
				
			||||||
 | 
					                !this.sections[this.lastShownSectionIndex].expanded) {
 | 
				
			||||||
                continue;
 | 
					                continue;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -712,10 +731,8 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
 | 
				
			|||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param show Whether if all sections is preferred.
 | 
					     * @param show Whether if all sections is preferred.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    async setAllSectionsPreferred(show: boolean): Promise<void> {
 | 
					    protected async setAllSectionsPreferred(show: boolean): Promise<void> {
 | 
				
			||||||
        const site = CoreSites.getCurrentSite();
 | 
					        await this.currentSite?.setLocalSiteConfig(`${COURSE_ALL_SECTIONS_PREFERRED_PREFIX}${this.course.id}`, show ? 1 : 0);
 | 
				
			||||||
 | 
					 | 
				
			||||||
        await site?.setLocalSiteConfig(`${COURSE_ALL_SECTIONS_PREFERRED_PREFIX}${this.course.id}`, show ? 1 : 0);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
@ -723,17 +740,88 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
 | 
				
			|||||||
     *
 | 
					     *
 | 
				
			||||||
     * @returns Whether if all sections is preferred.
 | 
					     * @returns Whether if all sections is preferred.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    async isAllSectionsPreferred(): Promise<boolean> {
 | 
					    protected async isAllSectionsPreferred(): Promise<boolean> {
 | 
				
			||||||
        const site = CoreSites.getCurrentSite();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const showAllSections =
 | 
					        const showAllSections =
 | 
				
			||||||
            await site?.getLocalSiteConfig<number>(`${COURSE_ALL_SECTIONS_PREFERRED_PREFIX}${this.course.id}`, 0);
 | 
					            await this.currentSite?.getLocalSiteConfig<number>(`${COURSE_ALL_SECTIONS_PREFERRED_PREFIX}${this.course.id}`, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return !!showAllSections;
 | 
					        return !!showAllSections;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Save expanded sections for the course.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async saveExpandedSections(): Promise<void> {
 | 
				
			||||||
 | 
					        const expandedSections = this.sections.filter((section) => section.expanded).map((section) => section.id).join(',');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await this.currentSite?.setLocalSiteConfig(`${COURSE_EXPANDED_SECTIONS_PREFIX}${this.course.id}`, expandedSections);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Initializes the expanded sections for the course.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async initializeExpandedSections(): Promise<void> {
 | 
				
			||||||
 | 
					        const expandedSections = await CoreUtils.ignoreErrors(
 | 
				
			||||||
 | 
					            this.currentSite?.getLocalSiteConfig<string>(`${COURSE_EXPANDED_SECTIONS_PREFIX}${this.course.id}`),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Expand all sections if not defined.
 | 
				
			||||||
 | 
					        if (expandedSections === undefined) {
 | 
				
			||||||
 | 
					            this.sections.forEach((section) => {
 | 
				
			||||||
 | 
					                section.expanded = true;
 | 
				
			||||||
 | 
					                this.accordionMultipleValue.push(section.id.toString());
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.accordionMultipleValue = expandedSections.split(',');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.sections.forEach((section) => {
 | 
				
			||||||
 | 
					            section.expanded = this.accordionMultipleValue.includes(section.id.toString());
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Toogle the visibility of a section (expand/collapse).
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ev The event of the accordion.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    accordionMultipleChange(ev: AccordionGroupChangeEventDetail): void {
 | 
				
			||||||
 | 
					        const sectionIds = ev.value as string[] | undefined;
 | 
				
			||||||
 | 
					        this.sections.forEach((section) => {
 | 
				
			||||||
 | 
					            section.expanded = false;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        sectionIds?.forEach((sectionId) => {
 | 
				
			||||||
 | 
					            const sId = Number(sectionId);
 | 
				
			||||||
 | 
					            const section = this.sections.find((section) => section.id === sId);
 | 
				
			||||||
 | 
					            if (section) {
 | 
				
			||||||
 | 
					                section.expanded = true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Save course expanded sections.
 | 
				
			||||||
 | 
					        this.saveExpandedSections();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.infiteLoading?.fireInfiniteScrollIfNeeded();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Expands a section and save state.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param section The section to expand.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected setSectionExpanded(section: CoreCourseSectionToDisplay): void {
 | 
				
			||||||
 | 
					        section.expanded = true;
 | 
				
			||||||
 | 
					        if (!this.accordionMultipleValue.includes(section.id.toString())) {
 | 
				
			||||||
 | 
					            this.accordionMultipleValue.push(section.id.toString());
 | 
				
			||||||
 | 
					            this.saveExpandedSections();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type CoreCourseSectionToDisplay = CoreCourseSection & {
 | 
					type CoreCourseSectionToDisplay = CoreCourseSection & {
 | 
				
			||||||
    highlighted?: boolean;
 | 
					    highlighted?: boolean;
 | 
				
			||||||
 | 
					    expanded?: boolean; // The aim of this property is to avoid DOM overloading.
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
<ion-card *ngIf="module.handlerData && module.visibleoncoursepage !== 0"
 | 
					<ion-card *ngIf="module.handlerData && module.visibleoncoursepage !== 0"
 | 
				
			||||||
    class="activity-card core-course-module-handler {{module.handlerData.class}}" [class.core-course-module-with-view]="moduleHasView"
 | 
					    class="activity-card core-course-module-handler {{module.handlerData.class}}" [class.core-course-module-with-view]="moduleHasView"
 | 
				
			||||||
    [class.item-dimmed]="module.visible === 0 || module.uservisible === false" [class.activityinline]="activityInline"
 | 
					    [class.item-dimmed]="module.visible === 0 || module.uservisible === false" (click)="moduleClicked($event)"
 | 
				
			||||||
    (click)="moduleClicked($event)" [button]="module.handlerData.action && module.uservisible"
 | 
					    [button]="module.handlerData.action && module.uservisible"
 | 
				
			||||||
    [attr.aria-label]="module.handlerData.a11yTitle ? module.handlerData.a11yTitle : null" id="core-course-module-{{module.id}}">
 | 
					    [attr.aria-label]="module.handlerData.a11yTitle ? module.handlerData.a11yTitle : null" id="core-course-module-{{module.id}}">
 | 
				
			||||||
    <ng-container *ngIf="!module.handlerData.loading">
 | 
					    <ng-container *ngIf="!module.handlerData.loading">
 | 
				
			||||||
        <ion-item class="ion-text-wrap">
 | 
					        <ion-item class="ion-text-wrap">
 | 
				
			||||||
 | 
				
			|||||||
@ -4,13 +4,15 @@
 | 
				
			|||||||
    --horizontal-margin: 12px;
 | 
					    --horizontal-margin: 12px;
 | 
				
			||||||
    --vertical-margin: 12px;
 | 
					    --vertical-margin: 12px;
 | 
				
			||||||
    --card-padding: 16px;
 | 
					    --card-padding: 16px;
 | 
				
			||||||
 | 
					    --card-border-width: 0px;
 | 
				
			||||||
 | 
					    --card-radius: 0px;
 | 
				
			||||||
 | 
					    --card-background: transparent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ion-card {
 | 
					    ion-card {
 | 
				
			||||||
        margin: var(--vertical-margin) var(--horizontal-margin);
 | 
					        margin: var(--vertical-margin) var(--horizontal-margin);
 | 
				
			||||||
 | 
					        --ion-card-border-width: var(--card-border-width);
 | 
				
			||||||
        &.activityinline {
 | 
					        --ion-card-radius: var(--card-radius);
 | 
				
			||||||
            border: 0px;
 | 
					        --ion-card-background: var(--card-background);
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ion-item {
 | 
					    ion-item {
 | 
				
			||||||
@ -125,6 +127,7 @@
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    .activity-extrabadges {
 | 
					    .activity-extrabadges {
 | 
				
			||||||
 | 
					        font: var(--mdl-typography-body-font-md);
 | 
				
			||||||
        color: var(--medium);
 | 
					        color: var(--medium);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -182,13 +185,11 @@
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    &.indented ion-card {
 | 
					    &.indented ion-card {
 | 
				
			||||||
        border: none;
 | 
					 | 
				
			||||||
        --ion-card-radius: 0;
 | 
					 | 
				
			||||||
        @include margin-horizontal(calc(var(--horizontal-margin) + 1rem), null);
 | 
					        @include margin-horizontal(calc(var(--horizontal-margin) + 1rem), null);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    &.indented + ::ng-deep core-course-module.indented ion-card {
 | 
					    & + ::ng-deep core-course-module ion-card {
 | 
				
			||||||
        border-top: 1px solid var(--border-color);
 | 
					        border-top: 1px solid var(--ion-card-border-color);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Hide download folder icon meanwhile MOBILE-4147 is not solved
 | 
					    // Hide download folder icon meanwhile MOBILE-4147 is not solved
 | 
				
			||||||
 | 
				
			|||||||
@ -66,7 +66,6 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
    prefetchStatusIcon$ = new BehaviorSubject<string>(''); // Module prefetch status icon.
 | 
					    prefetchStatusIcon$ = new BehaviorSubject<string>(''); // Module prefetch status icon.
 | 
				
			||||||
    prefetchStatusText$ = new BehaviorSubject<string>(''); // Module prefetch status text.
 | 
					    prefetchStatusText$ = new BehaviorSubject<string>(''); // Module prefetch status text.
 | 
				
			||||||
    moduleHasView = true;
 | 
					    moduleHasView = true;
 | 
				
			||||||
    activityInline = false;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected prefetchHandler?: CoreCourseModulePrefetchHandler;
 | 
					    protected prefetchHandler?: CoreCourseModulePrefetchHandler;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -103,18 +102,6 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
        this.module.handlerData.a11yTitle = this.module.handlerData.a11yTitle ?? this.module.handlerData.title;
 | 
					        this.module.handlerData.a11yTitle = this.module.handlerData.a11yTitle ?? this.module.handlerData.title;
 | 
				
			||||||
        this.moduleHasView = CoreCourse.moduleHasView(this.module);
 | 
					        this.moduleHasView = CoreCourse.moduleHasView(this.module);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (
 | 
					 | 
				
			||||||
            this.module.handlerData.hasCustomCmListItem &&
 | 
					 | 
				
			||||||
            (!this.showAvailability || !this.module.availabilityinfo) &&
 | 
					 | 
				
			||||||
            (!this.showCompletion || !this.hasCompletion) &&
 | 
					 | 
				
			||||||
            (!this.showActivityDates || !this.module.dates?.length) &&
 | 
					 | 
				
			||||||
            !this.module.groupmode &&
 | 
					 | 
				
			||||||
            !(this.module.visible === 0) &&
 | 
					 | 
				
			||||||
            !(this.module.visible !== 0 && this.module.isStealth)
 | 
					 | 
				
			||||||
        ) {
 | 
					 | 
				
			||||||
            this.activityInline = true;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (this.showDownloadStatus && this.module.handlerData.showDownloadButton) {
 | 
					        if (this.showDownloadStatus && this.module.handlerData.showDownloadButton) {
 | 
				
			||||||
            const status = await CoreCourseModulePrefetchDelegate.getDownloadedModuleStatus(this.module, this.module.course);
 | 
					            const status = await CoreCourseModulePrefetchDelegate.getDownloadedModuleStatus(this.module, this.module.course);
 | 
				
			||||||
            this.updateModuleStatus(status);
 | 
					            this.updateModuleStatus(status);
 | 
				
			||||||
 | 
				
			|||||||
@ -18,3 +18,4 @@ export const CONTENTS_PAGE_NAME = 'contents';
 | 
				
			|||||||
export const COURSE_CONTENTS_PATH = `${COURSE_PAGE_NAME}/${COURSE_INDEX_PATH}/${CONTENTS_PAGE_NAME}`;
 | 
					export const COURSE_CONTENTS_PATH = `${COURSE_PAGE_NAME}/${COURSE_INDEX_PATH}/${CONTENTS_PAGE_NAME}`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const COURSE_ALL_SECTIONS_PREFERRED_PREFIX = 'CoreCourseFormatAllSectionsPreferred-';
 | 
					export const COURSE_ALL_SECTIONS_PREFERRED_PREFIX = 'CoreCourseFormatAllSectionsPreferred-';
 | 
				
			||||||
 | 
					export const COURSE_EXPANDED_SECTIONS_PREFIX = 'CoreCourseFormatExpandedSections-';
 | 
				
			||||||
 | 
				
			|||||||
@ -8,7 +8,7 @@
 | 
				
			|||||||
        </ion-title>
 | 
					        </ion-title>
 | 
				
			||||||
    </ion-toolbar>
 | 
					    </ion-toolbar>
 | 
				
			||||||
</ion-header>
 | 
					</ion-header>
 | 
				
			||||||
<ion-content>
 | 
					<ion-content class="limited-width">
 | 
				
			||||||
    <ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="refreshData($event.target)">
 | 
					    <ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="refreshData($event.target)">
 | 
				
			||||||
        <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
 | 
					        <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
 | 
				
			||||||
    </ion-refresher>
 | 
					    </ion-refresher>
 | 
				
			||||||
@ -18,7 +18,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        <ion-list class="core-course-module-list-wrapper">
 | 
					        <ion-list class="core-course-module-list-wrapper">
 | 
				
			||||||
            <ng-container *ngFor="let section of sections; index as i">
 | 
					            <ng-container *ngFor="let section of sections; index as i">
 | 
				
			||||||
                <ng-container *ngIf="i <= lastShownSectionIndex">
 | 
					                <ion-card *ngIf="i <= lastShownSectionIndex">
 | 
				
			||||||
                    <ion-item-divider class="course-section ion-text-wrap" *ngIf="section.name">
 | 
					                    <ion-item-divider class="course-section ion-text-wrap" *ngIf="section.name">
 | 
				
			||||||
                        <ion-label>
 | 
					                        <ion-label>
 | 
				
			||||||
                            <h2>
 | 
					                            <h2>
 | 
				
			||||||
@ -30,7 +30,7 @@
 | 
				
			|||||||
                        <core-course-module [module]="module" [section]="section" [showActivityDates]="false" [showAvailability]="false"
 | 
					                        <core-course-module [module]="module" [section]="section" [showActivityDates]="false" [showAvailability]="false"
 | 
				
			||||||
                            [showExtra]="false" [showDownloadStatus]="false" [showCompletion]="false" [showIndentation]="false" />
 | 
					                            [showExtra]="false" [showDownloadStatus]="false" [showCompletion]="false" [showIndentation]="false" />
 | 
				
			||||||
                    </ng-container>
 | 
					                    </ng-container>
 | 
				
			||||||
                </ng-container>
 | 
					                </ion-card>
 | 
				
			||||||
            </ng-container>
 | 
					            </ng-container>
 | 
				
			||||||
        </ion-list>
 | 
					        </ion-list>
 | 
				
			||||||
        <core-infinite-loading [enabled]="canLoadMore" (action)="showMoreActivities($event)" />
 | 
					        <core-infinite-loading [enabled]="canLoadMore" (action)="showMoreActivities($event)" />
 | 
				
			||||||
 | 
				
			|||||||
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 34 KiB  | 
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 39 KiB  | 
@ -8,9 +8,37 @@
 | 
				
			|||||||
        <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
 | 
					        <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
 | 
				
			||||||
    </ion-refresher>
 | 
					    </ion-refresher>
 | 
				
			||||||
    <core-loading [hideUntil]="dataLoaded">
 | 
					    <core-loading [hideUntil]="dataLoaded">
 | 
				
			||||||
        <ion-list class="core-course-module-list-wrapper">
 | 
					        <ion-list class="list-item-limited-width">
 | 
				
			||||||
            <!-- Site home main contents. -->
 | 
					            <!-- Site home main contents. -->
 | 
				
			||||||
            <ng-container *ngIf="section && section.hasContent">
 | 
					            <section *ngIf="section && section.hasContent" class="core-course-module-list-wrapper">
 | 
				
			||||||
 | 
					                <ion-item-divider class="course-section ion-text-wrap"
 | 
				
			||||||
 | 
					                    [class.item-dimmed]="section.visible === 0 || section.uservisible === false">
 | 
				
			||||||
 | 
					                    <ion-label>
 | 
				
			||||||
 | 
					                        <h2 *ngIf="section.name" class="big" [id]="'core-section-name-' + section.id">
 | 
				
			||||||
 | 
					                            <core-format-text [text]="section.name" contextLevel="course" [contextInstanceId]="siteHomeId" />
 | 
				
			||||||
 | 
					                        </h2>
 | 
				
			||||||
 | 
					                        <div *ngIf="section.visible === 0 && section.uservisible !== false">
 | 
				
			||||||
 | 
					                            <ion-badge color="warning">
 | 
				
			||||||
 | 
					                                {{ 'core.course.hiddenfromstudents' | translate }}
 | 
				
			||||||
 | 
					                            </ion-badge>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                        <div *ngIf="section.visible === 0 && section.uservisible === false">
 | 
				
			||||||
 | 
					                            <ion-badge color="warning">
 | 
				
			||||||
 | 
					                                {{ 'core.notavailable' | translate }}
 | 
				
			||||||
 | 
					                            </ion-badge>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                        <div *ngIf="section.availabilityinfo">
 | 
				
			||||||
 | 
					                            <ion-chip class="clickable">
 | 
				
			||||||
 | 
					                                <ion-icon name="fas-lock" [attr.aria-label]="'core.restricted' | translate" />
 | 
				
			||||||
 | 
					                                <ion-label>
 | 
				
			||||||
 | 
					                                    <core-format-text [text]=" section.availabilityinfo" contextLevel="course"
 | 
				
			||||||
 | 
					                                        [contextInstanceId]="siteHomeId" />
 | 
				
			||||||
 | 
					                                </ion-label>
 | 
				
			||||||
 | 
					                            </ion-chip>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                    </ion-label>
 | 
				
			||||||
 | 
					                </ion-item-divider>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <ion-item class="ion-text-wrap section-summary" *ngIf="section.summary">
 | 
					                <ion-item class="ion-text-wrap section-summary" *ngIf="section.summary">
 | 
				
			||||||
                    <ion-label>
 | 
					                    <ion-label>
 | 
				
			||||||
                        <core-format-text [text]="section.summary" contextLevel="course" [contextInstanceId]="siteHomeId" />
 | 
					                        <core-format-text [text]="section.summary" contextLevel="course" [contextInstanceId]="siteHomeId" />
 | 
				
			||||||
@ -18,11 +46,10 @@
 | 
				
			|||||||
                </ion-item>
 | 
					                </ion-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <core-course-module *ngFor="let module of section.modules" [module]="module" [section]="section" />
 | 
					                <core-course-module *ngFor="let module of section.modules" [module]="module" [section]="section" />
 | 
				
			||||||
            </ng-container>
 | 
					            </section>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <!-- Site home items: news, categories, courses, etc. -->
 | 
					            <!-- Site home items: news, categories, courses, etc. -->
 | 
				
			||||||
            <ng-container *ngIf="items.length > 0">
 | 
					            <ng-container *ngIf="items.length > 0">
 | 
				
			||||||
                <core-spacer *ngIf="section && section!.hasContent" />
 | 
					 | 
				
			||||||
                <ng-container *ngFor="let item of items">
 | 
					                <ng-container *ngFor="let item of items">
 | 
				
			||||||
                    <ng-container [ngSwitch]="item">
 | 
					                    <ng-container [ngSwitch]="item">
 | 
				
			||||||
                        <ng-container *ngSwitchCase="'LIST_OF_COURSE'">
 | 
					                        <ng-container *ngSwitchCase="'LIST_OF_COURSE'">
 | 
				
			||||||
 | 
				
			|||||||
@ -10,6 +10,21 @@ ion-item ion-icon {
 | 
				
			|||||||
    @include margin-horizontal(null, var(--margin-end));
 | 
					    @include margin-horizontal(null, var(--margin-end));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
core-spacer {
 | 
					section.core-course-module-list-wrapper {
 | 
				
			||||||
    --spacer-horizontal: 10px;
 | 
					    border: var(--ion-card-border-width) solid var(--ion-card-border-color);
 | 
				
			||||||
 | 
					    border-radius: var(--ion-card-radius);
 | 
				
			||||||
 | 
					    margin: 12px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ion-card {
 | 
				
			||||||
 | 
					        --ion-card-background: transparent;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ion-item-divider {
 | 
				
			||||||
 | 
					        --background: transparent;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					core-course-module.core-sitehome-news {
 | 
				
			||||||
 | 
					    --card-border-width: var(--ion-card-border-width);
 | 
				
			||||||
 | 
					    --card-radius: var(--ion-card-radius);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -24,7 +24,7 @@ import {
 | 
				
			|||||||
    NavigationError,
 | 
					    NavigationError,
 | 
				
			||||||
    NavigationStart,
 | 
					    NavigationStart,
 | 
				
			||||||
} from '@angular/router';
 | 
					} from '@angular/router';
 | 
				
			||||||
import { Subscription, filter } from 'rxjs';
 | 
					import { filter } from 'rxjs';
 | 
				
			||||||
import { CoreNavigator } from '@services/navigator';
 | 
					import { CoreNavigator } from '@services/navigator';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@ -39,7 +39,6 @@ export class TestingBehatBlockingService {
 | 
				
			|||||||
    protected lastMutation = 0;
 | 
					    protected lastMutation = 0;
 | 
				
			||||||
    protected initialized = false;
 | 
					    protected initialized = false;
 | 
				
			||||||
    protected keyIndex = 0;
 | 
					    protected keyIndex = 0;
 | 
				
			||||||
    protected navSubscription?: Subscription;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Listen to mutations and override XML Requests.
 | 
					     * Listen to mutations and override XML Requests.
 | 
				
			||||||
@ -60,7 +59,7 @@ export class TestingBehatBlockingService {
 | 
				
			|||||||
        win.M.util = win.M.util ?? {};
 | 
					        win.M.util = win.M.util ?? {};
 | 
				
			||||||
        win.M.util.pending_js = win.M.util.pending_js ?? [];
 | 
					        win.M.util.pending_js = win.M.util.pending_js ?? [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.navSubscription = Router.events
 | 
					        Router.events
 | 
				
			||||||
            .pipe(filter(event =>
 | 
					            .pipe(filter(event =>
 | 
				
			||||||
                event instanceof NavigationStart ||
 | 
					                event instanceof NavigationStart ||
 | 
				
			||||||
                event instanceof NavigationEnd ||
 | 
					                event instanceof NavigationEnd ||
 | 
				
			||||||
 | 
				
			|||||||
@ -207,9 +207,7 @@ img[core-external-content]:not([src]) {
 | 
				
			|||||||
    border-radius: var(--mdl-shape-borderRadius-lg);
 | 
					    border-radius: var(--mdl-shape-borderRadius-lg);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ion-list.core-course-module-list-wrapper,
 | 
					 | 
				
			||||||
.list-item-limited-width,
 | 
					.list-item-limited-width,
 | 
				
			||||||
.core-course-module-list-wrapper,
 | 
					 | 
				
			||||||
ion-content.limited-width > :not([slot]) {
 | 
					ion-content.limited-width > :not([slot]) {
 | 
				
			||||||
    max-width: var(--list-item-max-width);
 | 
					    max-width: var(--list-item-max-width);
 | 
				
			||||||
    margin-left: auto !important;
 | 
					    margin-left: auto !important;
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user