MOBILE-3998 imscp: Add entry page to IMSCP
This commit is contained in:
		
							parent
							
								
									e37c75ff54
								
							
						
					
					
						commit
						223fd19f1d
					
				@ -92,7 +92,7 @@ export class AddonModBookIndexComponent extends CoreCourseModuleMainResourceComp
 | 
			
		||||
    /**
 | 
			
		||||
     * Open the book in a certain chapter.
 | 
			
		||||
     *
 | 
			
		||||
     * @param chapterId Chapter to open, undefined for first chapter.
 | 
			
		||||
     * @param chapterId Chapter to open, undefined for last chapter viewed.
 | 
			
		||||
     */
 | 
			
		||||
    openBook(chapterId?: number): void {
 | 
			
		||||
        CoreNavigator.navigate('contents', {
 | 
			
		||||
 | 
			
		||||
@ -53,7 +53,7 @@ export class AddonModBookContentsPage implements OnInit, OnDestroy {
 | 
			
		||||
 | 
			
		||||
    @ViewChild(CoreSwipeSlidesComponent) slides?: CoreSwipeSlidesComponent;
 | 
			
		||||
 | 
			
		||||
    title!: string;
 | 
			
		||||
    title = '';
 | 
			
		||||
    cmId!: number;
 | 
			
		||||
    courseId!: number;
 | 
			
		||||
    initialChapterId?: number;
 | 
			
		||||
@ -147,6 +147,8 @@ export class AddonModBookContentsPage implements OnInit, OnDestroy {
 | 
			
		||||
            } else {
 | 
			
		||||
                this.warning = '';
 | 
			
		||||
            }
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            CoreDomUtils.showErrorModalDefault(error, 'core.course.errorgetmodule', true);
 | 
			
		||||
        } finally {
 | 
			
		||||
            this.loaded = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,5 @@
 | 
			
		||||
<!-- Buttons to add to the header. -->
 | 
			
		||||
<core-navbar-buttons slot="end">
 | 
			
		||||
    <ion-button *ngIf="!showLoading" (click)="showToc()" aria-haspopup="true" [attr.aria-label]="'addon.mod_imscp.toc' | translate">
 | 
			
		||||
        <ion-icon name="fas-bookmark" slot="icon-only" aria-hidden="true"></ion-icon>
 | 
			
		||||
    </ion-button>
 | 
			
		||||
 | 
			
		||||
    <ion-button fill="clear" (click)="openModuleSummary()" aria-haspopup="true" [attr.aria-label]="'core.info' | translate">
 | 
			
		||||
        <ion-icon name="fas-info-circle" slot="icon-only" aria-hidden="true"></ion-icon>
 | 
			
		||||
    </ion-button>
 | 
			
		||||
@ -13,24 +9,35 @@
 | 
			
		||||
<core-loading [hideUntil]="!showLoading" class="safe-area-padding core-loading-full-height">
 | 
			
		||||
 | 
			
		||||
    <!-- Activity info. -->
 | 
			
		||||
    <core-course-module-info [module]="module">
 | 
			
		||||
    <core-course-module-info [module]="module" [description]="description" [component]="component" [componentId]="componentId"
 | 
			
		||||
        [courseId]="courseId">
 | 
			
		||||
    </core-course-module-info>
 | 
			
		||||
 | 
			
		||||
    <ion-card class="core-warning-card" *ngIf="warning">
 | 
			
		||||
    <ion-list>
 | 
			
		||||
        <ion-item>
 | 
			
		||||
            <ion-icon name="fas-exclamation-triangle" slot="start" aria-hidden="true"></ion-icon>
 | 
			
		||||
            <ion-label><span [innerHTML]="warning"></span></ion-label>
 | 
			
		||||
            <ion-label>
 | 
			
		||||
                <h2>{{ 'addon.mod_imscp.toc' | translate }}</h2>
 | 
			
		||||
            </ion-label>
 | 
			
		||||
        </ion-item>
 | 
			
		||||
    </ion-card>
 | 
			
		||||
 | 
			
		||||
    <div class="addon-mod-imscp-container">
 | 
			
		||||
        <core-iframe *ngIf="!showLoading" [src]="src" [showFullscreenOnToolbar]="true" [autoFullscreenOnRotate]="true"></core-iframe>
 | 
			
		||||
    </div>
 | 
			
		||||
        <ion-item class="ion-text-wrap" *ngFor="let item of items" (click)="openImscp(item.href)" button detail="true">
 | 
			
		||||
            <ion-label [class.core-bold]="!item.href">
 | 
			
		||||
                <p>
 | 
			
		||||
                    <span class="ion-padding-start" *ngFor="let i of getNumberForPadding(item.level)"></span>
 | 
			
		||||
                    {{item.title}}
 | 
			
		||||
                </p>
 | 
			
		||||
            </ion-label>
 | 
			
		||||
        </ion-item>
 | 
			
		||||
    </ion-list>
 | 
			
		||||
 | 
			
		||||
    <div collapsible-footer *ngIf="!showLoading" slot="fixed">
 | 
			
		||||
        <!-- TODO Add a contents page to avoid having both bars. Please add here start/resume buttons. -->
 | 
			
		||||
        <core-navigation-bar *ngIf="navigationItems.length > 1" [items]="navigationItems" (action)="loadItem($event)">
 | 
			
		||||
        </core-navigation-bar>
 | 
			
		||||
        <div class="list-item-limited-width">
 | 
			
		||||
            <ion-button class="ion-margin ion-text-wrap" expand="block" (click)="openImscp()">
 | 
			
		||||
                <span *ngIf="!hasStarted">{{ 'core.start' | translate }}</span>
 | 
			
		||||
                <span *ngIf="hasStarted">{{ 'core.resume' | translate }}</span>
 | 
			
		||||
            </ion-button>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <core-course-module-navigation [courseId]="courseId" [currentModule]="module" (completionChanged)="onCompletionChange()">
 | 
			
		||||
        </core-course-module-navigation>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
@ -13,14 +13,11 @@
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { Component, OnInit, Optional } from '@angular/core';
 | 
			
		||||
import { CoreSilentError } from '@classes/errors/silenterror';
 | 
			
		||||
import { CoreNavigationBarItem } from '@components/navigation-bar/navigation-bar';
 | 
			
		||||
import { CoreCourseModuleMainResourceComponent } from '@features/course/classes/main-resource-component';
 | 
			
		||||
import { CoreCourseContentsPage } from '@features/course/pages/contents/contents';
 | 
			
		||||
import { CoreCourse } from '@features/course/services/course';
 | 
			
		||||
import { CoreDomUtils } from '@services/utils/dom';
 | 
			
		||||
import { CoreNavigator } from '@services/navigator';
 | 
			
		||||
import { AddonModImscpProvider, AddonModImscp, AddonModImscpTocItem } from '../../services/imscp';
 | 
			
		||||
import { AddonModImscpTocComponent } from '../toc/toc';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component that displays a IMSCP.
 | 
			
		||||
@ -33,13 +30,9 @@ import { AddonModImscpTocComponent } from '../toc/toc';
 | 
			
		||||
export class AddonModImscpIndexComponent extends CoreCourseModuleMainResourceComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
    component = AddonModImscpProvider.COMPONENT;
 | 
			
		||||
    src = '';
 | 
			
		||||
    warning = '';
 | 
			
		||||
    navigationItems: CoreNavigationBarItem<AddonModImscpTocItem>[] = [];
 | 
			
		||||
 | 
			
		||||
    protected items: AddonModImscpTocItem[] = [];
 | 
			
		||||
    protected currentHref?: string;
 | 
			
		||||
    protected displayDescription = false;
 | 
			
		||||
    items: AddonModImscpTocItem[] = [];
 | 
			
		||||
    hasStarted = false;
 | 
			
		||||
 | 
			
		||||
    constructor(@Optional() courseContentsPage?: CoreCourseContentsPage) {
 | 
			
		||||
        super('AddonModImscpIndexComponent', courseContentsPage);
 | 
			
		||||
@ -73,85 +66,67 @@ export class AddonModImscpIndexComponent extends CoreCourseModuleMainResourceCom
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    protected async fetchContent(refresh = false): Promise<void> {
 | 
			
		||||
        const downloadResult = await this.downloadResourceIfNeeded(refresh);
 | 
			
		||||
 | 
			
		||||
        const imscp = await AddonModImscp.getImscp(this.courseId, this.module.id);
 | 
			
		||||
        this.description = imscp.intro;
 | 
			
		||||
        this.dataRetrieved.emit(imscp);
 | 
			
		||||
 | 
			
		||||
        // Get contents. No need to refresh, it has been done in downloadResourceIfNeeded.
 | 
			
		||||
        const contents = await CoreCourse.getModuleContents(this.module);
 | 
			
		||||
 | 
			
		||||
        this.items = AddonModImscp.createItemList(contents);
 | 
			
		||||
 | 
			
		||||
        if (this.items.length && this.currentHref === undefined) {
 | 
			
		||||
            this.currentHref = this.items[0].href;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            await this.loadItemHref(this.currentHref);
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            CoreDomUtils.showErrorModalDefault(error, 'addon.mod_imscp.deploymenterror', true);
 | 
			
		||||
 | 
			
		||||
            throw new CoreSilentError(error);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.warning = downloadResult.failed ? this.getErrorDownloadingSomeFilesMessage(downloadResult.error!) : '';
 | 
			
		||||
    protected async fetchContent(): Promise<void> {
 | 
			
		||||
        await Promise.all([
 | 
			
		||||
            this.loadImscp(),
 | 
			
		||||
            this.loadTOC(),
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Loads an item.
 | 
			
		||||
     * Load IMSCP data.
 | 
			
		||||
     *
 | 
			
		||||
     * @param itemHref Item Href.
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    async loadItemHref(itemHref?: string): Promise<void> {
 | 
			
		||||
        const src = await AddonModImscp.getIframeSrc(this.module, itemHref);
 | 
			
		||||
        this.currentHref = itemHref;
 | 
			
		||||
    protected async loadImscp(): Promise<void> {
 | 
			
		||||
        const imscp = await AddonModImscp.getImscp(this.courseId, this.module.id);
 | 
			
		||||
 | 
			
		||||
        this.navigationItems = this.items.map((item) => ({
 | 
			
		||||
            item: item,
 | 
			
		||||
            current: item.href == this.currentHref,
 | 
			
		||||
            enabled: !!item.href,
 | 
			
		||||
        }));
 | 
			
		||||
        this.dataRetrieved.emit(imscp);
 | 
			
		||||
 | 
			
		||||
        if (this.src && src == this.src) {
 | 
			
		||||
            // Re-loading same page. Set it to empty and then re-set the src in the next digest so it detects it has changed.
 | 
			
		||||
            this.src = '';
 | 
			
		||||
            setTimeout(() => {
 | 
			
		||||
                this.src = src;
 | 
			
		||||
            });
 | 
			
		||||
        } else {
 | 
			
		||||
            this.src = src;
 | 
			
		||||
        }
 | 
			
		||||
        this.dataRetrieved.emit(imscp);
 | 
			
		||||
 | 
			
		||||
        this.description = imscp.intro;
 | 
			
		||||
 | 
			
		||||
        // @todo: Check if user already started the IMSCP.
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Loads an item.
 | 
			
		||||
     * Load book TOC.
 | 
			
		||||
     *
 | 
			
		||||
     * @param item Item.
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    loadItem(item: AddonModImscpTocItem): void {
 | 
			
		||||
        this.loadItemHref(item.href);
 | 
			
		||||
    protected async loadTOC(): Promise<void> {
 | 
			
		||||
        // Get contents. No need to refresh, it has been done in downloadResourceIfNeeded.
 | 
			
		||||
        const contents = await CoreCourse.getModuleContents(this.module, this.courseId);
 | 
			
		||||
 | 
			
		||||
        this.items = AddonModImscp.createItemList(contents);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Show the TOC.
 | 
			
		||||
     * Open IMSCP book with a certain item.
 | 
			
		||||
     *
 | 
			
		||||
     * @param href Item href to open, undefined for last item seen.
 | 
			
		||||
     */
 | 
			
		||||
    async showToc(): Promise<void> {
 | 
			
		||||
        // Create the toc modal.
 | 
			
		||||
        const modalData = await CoreDomUtils.openSideModal<string>({
 | 
			
		||||
            component: AddonModImscpTocComponent,
 | 
			
		||||
            componentProps: {
 | 
			
		||||
                items: this.items,
 | 
			
		||||
                selected: this.currentHref,
 | 
			
		||||
    openImscp(href?: string): void {
 | 
			
		||||
        CoreNavigator.navigate('view', {
 | 
			
		||||
            params: {
 | 
			
		||||
                cmId: this.module.id,
 | 
			
		||||
                courseId: this.courseId,
 | 
			
		||||
                initialHref: href,
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        if (modalData) {
 | 
			
		||||
            this.loadItemHref(modalData);
 | 
			
		||||
        }
 | 
			
		||||
        this.hasStarted = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get dummy array for padding.
 | 
			
		||||
     *
 | 
			
		||||
     * @param n Array length.
 | 
			
		||||
     * @return Dummy array with n elements.
 | 
			
		||||
     */
 | 
			
		||||
    getNumberForPadding(n: number): number[] {
 | 
			
		||||
        return new Array(n);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -24,6 +24,10 @@ const routes: Routes = [
 | 
			
		||||
        path: ':courseId/:cmId',
 | 
			
		||||
        component: AddonModImscpIndexPage,
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        path: ':courseId/:cmId/view',
 | 
			
		||||
        loadChildren: () => import('./pages/view/view.module').then(m => m.AddonModImscpViewPageModule),
 | 
			
		||||
    },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										39
									
								
								src/addons/mod/imscp/pages/view/view.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/addons/mod/imscp/pages/view/view.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,39 @@
 | 
			
		||||
<ion-header>
 | 
			
		||||
    <ion-toolbar>
 | 
			
		||||
        <ion-buttons slot="start">
 | 
			
		||||
            <ion-back-button [text]="'core.back' | translate"></ion-back-button>
 | 
			
		||||
        </ion-buttons>
 | 
			
		||||
        <ion-title>
 | 
			
		||||
            <h1>
 | 
			
		||||
                <core-format-text [text]="title" contextLevel="module" [contextInstanceId]="cmId" [courseId]="courseId">
 | 
			
		||||
                </core-format-text>
 | 
			
		||||
            </h1>
 | 
			
		||||
        </ion-title>
 | 
			
		||||
 | 
			
		||||
        <ion-buttons slot="end">
 | 
			
		||||
            <ion-button *ngIf="loaded" (click)="showToc()" aria-haspopup="true" [attr.aria-label]="'addon.mod_imscp.toc' | translate">
 | 
			
		||||
                <ion-icon name="fas-bookmark" slot="icon-only" aria-hidden="true"></ion-icon>
 | 
			
		||||
            </ion-button>
 | 
			
		||||
        </ion-buttons>
 | 
			
		||||
    </ion-toolbar>
 | 
			
		||||
</ion-header>
 | 
			
		||||
<ion-content>
 | 
			
		||||
    <!-- Content. -->
 | 
			
		||||
    <core-loading [hideUntil]="loaded" class="safe-area-padding core-loading-full-height">
 | 
			
		||||
 | 
			
		||||
        <ion-card class="core-warning-card" *ngIf="warning">
 | 
			
		||||
            <ion-item>
 | 
			
		||||
                <ion-icon name="fas-exclamation-triangle" slot="start" aria-hidden="true"></ion-icon>
 | 
			
		||||
                <ion-label><span [innerHTML]="warning"></span></ion-label>
 | 
			
		||||
            </ion-item>
 | 
			
		||||
        </ion-card>
 | 
			
		||||
 | 
			
		||||
        <div class="addon-mod-imscp-container">
 | 
			
		||||
            <core-iframe *ngIf="loaded" [src]="src" [showFullscreenOnToolbar]="true" [autoFullscreenOnRotate]="true"></core-iframe>
 | 
			
		||||
        </div>
 | 
			
		||||
    </core-loading>
 | 
			
		||||
 | 
			
		||||
    <core-navigation-bar collapsible-footer *ngIf="loaded && navigationItems.length > 1 && false" [items]="navigationItems"
 | 
			
		||||
        (action)="loadItem($event)">
 | 
			
		||||
    </core-navigation-bar>
 | 
			
		||||
</ion-content>
 | 
			
		||||
							
								
								
									
										38
									
								
								src/addons/mod/imscp/pages/view/view.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/addons/mod/imscp/pages/view/view.module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,38 @@
 | 
			
		||||
// (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 { NgModule } from '@angular/core';
 | 
			
		||||
import { RouterModule, Routes } from '@angular/router';
 | 
			
		||||
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { AddonModImscpViewPage } from './view';
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
    {
 | 
			
		||||
        path: '',
 | 
			
		||||
        component: AddonModImscpViewPage,
 | 
			
		||||
    },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    imports: [
 | 
			
		||||
        RouterModule.forChild(routes),
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    declarations: [
 | 
			
		||||
        AddonModImscpViewPage,
 | 
			
		||||
    ],
 | 
			
		||||
    exports: [RouterModule],
 | 
			
		||||
})
 | 
			
		||||
export class AddonModImscpViewPageModule {}
 | 
			
		||||
							
								
								
									
										273
									
								
								src/addons/mod/imscp/pages/view/view.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										273
									
								
								src/addons/mod/imscp/pages/view/view.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,273 @@
 | 
			
		||||
// (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 { CoreConstants } from '@/core/constants';
 | 
			
		||||
import { Component, OnInit } from '@angular/core';
 | 
			
		||||
import { CoreNavigationBarItem } from '@components/navigation-bar/navigation-bar';
 | 
			
		||||
import { CoreCourseResourceDownloadResult } from '@features/course/classes/main-resource-component';
 | 
			
		||||
import { CoreCourse } from '@features/course/services/course';
 | 
			
		||||
import { CoreCourseModuleData } from '@features/course/services/course-helper';
 | 
			
		||||
import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate';
 | 
			
		||||
import { IonRefresher } from '@ionic/angular';
 | 
			
		||||
import { CoreApp } from '@services/app';
 | 
			
		||||
import { CoreNavigator } from '@services/navigator';
 | 
			
		||||
import { CoreDomUtils } from '@services/utils/dom';
 | 
			
		||||
import { CoreTextUtils } from '@services/utils/text';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { Translate } from '@singletons';
 | 
			
		||||
import { AddonModImscpTocComponent } from '../../components/toc/toc';
 | 
			
		||||
import { AddonModImscp, AddonModImscpImscp, AddonModImscpTocItem } from '../../services/imscp';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Page that displays a IMSCP content.
 | 
			
		||||
 */
 | 
			
		||||
@Component({
 | 
			
		||||
    selector: 'page-addon-mod-imscp-view',
 | 
			
		||||
    templateUrl: 'view.html',
 | 
			
		||||
})
 | 
			
		||||
export class AddonModImscpViewPage implements OnInit {
 | 
			
		||||
 | 
			
		||||
    title = '';
 | 
			
		||||
    cmId!: number;
 | 
			
		||||
    courseId!: number;
 | 
			
		||||
    initialItemHref?: string;
 | 
			
		||||
    src = '';
 | 
			
		||||
    warning = '';
 | 
			
		||||
    navigationItems: CoreNavigationBarItem<AddonModImscpTocItem>[] = [];
 | 
			
		||||
    loaded = false;
 | 
			
		||||
 | 
			
		||||
    protected module?: CoreCourseModuleData;
 | 
			
		||||
    protected imscp?: AddonModImscpImscp;
 | 
			
		||||
    protected items: AddonModImscpTocItem[] = [];
 | 
			
		||||
    protected currentHref?: string;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    ngOnInit(): void {
 | 
			
		||||
        try {
 | 
			
		||||
            this.cmId = CoreNavigator.getRequiredRouteNumberParam('cmId');
 | 
			
		||||
            this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId');
 | 
			
		||||
            this.initialItemHref = CoreNavigator.getRouteParam('initialHref');
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            CoreDomUtils.showErrorModal(error);
 | 
			
		||||
 | 
			
		||||
            CoreNavigator.back();
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.fetchContent();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Download IMSCP contents and load the current item.
 | 
			
		||||
     *
 | 
			
		||||
     * @param refresh Whether we're refreshing data.
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    protected async fetchContent(refresh = false): Promise<void> {
 | 
			
		||||
        try {
 | 
			
		||||
            const { module, imscp } = await this.loadImscpData();
 | 
			
		||||
 | 
			
		||||
            this.title = imscp.name;
 | 
			
		||||
 | 
			
		||||
            const downloadResult = await this.downloadResourceIfNeeded(module, refresh);
 | 
			
		||||
 | 
			
		||||
            // Get contents. No need to refresh, it has been done in downloadResourceIfNeeded.
 | 
			
		||||
            const contents = await CoreCourse.getModuleContents(module, this.courseId);
 | 
			
		||||
 | 
			
		||||
            this.items = AddonModImscp.createItemList(contents);
 | 
			
		||||
 | 
			
		||||
            if (this.items.length) {
 | 
			
		||||
                if (this.initialItemHref) {
 | 
			
		||||
                    // Check it's valid.
 | 
			
		||||
                    if (this.items.some(item => item.href === this.initialItemHref)) {
 | 
			
		||||
                        this.currentHref = this.initialItemHref;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (this.currentHref === undefined) {
 | 
			
		||||
                    // @todo: Use last item viewed.
 | 
			
		||||
                    this.currentHref = this.items[0].href;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                await this.loadItemHref(this.currentHref);
 | 
			
		||||
            } catch (error) {
 | 
			
		||||
                CoreDomUtils.showErrorModalDefault(error, 'addon.mod_imscp.deploymenterror', true);
 | 
			
		||||
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (downloadResult?.failed) {
 | 
			
		||||
                const error = CoreTextUtils.getErrorMessageFromError(downloadResult.error) || downloadResult.error;
 | 
			
		||||
                this.warning = Translate.instant('core.errordownloadingsomefiles') + (error ? ' ' + error : '');
 | 
			
		||||
            } else {
 | 
			
		||||
                this.warning = '';
 | 
			
		||||
            }
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            CoreDomUtils.showErrorModalDefault(error, 'core.course.errorgetmodule', true);
 | 
			
		||||
        } finally {
 | 
			
		||||
            this.loaded = true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Load IMSCP data from WS.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    async loadImscpData(): Promise<{ module: CoreCourseModuleData; imscp: AddonModImscpImscp }> {
 | 
			
		||||
        this.module = await CoreCourse.getModule(this.cmId, this.courseId);
 | 
			
		||||
        this.imscp = await AddonModImscp.getImscp(this.courseId, this.cmId);
 | 
			
		||||
 | 
			
		||||
        return {
 | 
			
		||||
            module: this.module,
 | 
			
		||||
            imscp: this.imscp,
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Download a resource if needed.
 | 
			
		||||
     * If the download call fails the promise won't be rejected, but the error will be included in the returned object.
 | 
			
		||||
     * If module.contents cannot be loaded then the Promise will be rejected.
 | 
			
		||||
     *
 | 
			
		||||
     * @param refresh Whether we're refreshing data.
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    protected async downloadResourceIfNeeded(
 | 
			
		||||
        module: CoreCourseModuleData,
 | 
			
		||||
        refresh = false,
 | 
			
		||||
    ): Promise<CoreCourseResourceDownloadResult> {
 | 
			
		||||
 | 
			
		||||
        const result: CoreCourseResourceDownloadResult = {
 | 
			
		||||
            failed: false,
 | 
			
		||||
        };
 | 
			
		||||
        let contentsAlreadyLoaded = false;
 | 
			
		||||
 | 
			
		||||
        // Get module status to determine if it needs to be downloaded.
 | 
			
		||||
        const status = await CoreCourseModulePrefetchDelegate.getModuleStatus(module, this.courseId, undefined, refresh);
 | 
			
		||||
 | 
			
		||||
        if (status !== CoreConstants.DOWNLOADED) {
 | 
			
		||||
            // Download content. This function also loads module contents if needed.
 | 
			
		||||
            try {
 | 
			
		||||
                await CoreCourseModulePrefetchDelegate.downloadModule(module, this.courseId);
 | 
			
		||||
 | 
			
		||||
                // If we reach here it means the download process already loaded the contents, no need to do it again.
 | 
			
		||||
                contentsAlreadyLoaded = true;
 | 
			
		||||
            } catch (error) {
 | 
			
		||||
                // Mark download as failed but go on since the main files could have been downloaded.
 | 
			
		||||
                result.failed = true;
 | 
			
		||||
                result.error = error;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!module.contents?.length || (refresh && !contentsAlreadyLoaded)) {
 | 
			
		||||
            // Try to load the contents.
 | 
			
		||||
            const ignoreCache = refresh && CoreApp.isOnline();
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                await CoreCourse.loadModuleContents(module, undefined, undefined, false, ignoreCache);
 | 
			
		||||
            } catch (error) {
 | 
			
		||||
                // Error loading contents. If we ignored cache, try to get the cached value.
 | 
			
		||||
                if (ignoreCache && !module.contents) {
 | 
			
		||||
                    await CoreCourse.loadModuleContents(module);
 | 
			
		||||
                } else if (!module.contents) {
 | 
			
		||||
                    // Not able to load contents, throw the error.
 | 
			
		||||
                    throw error;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Refresh the data.
 | 
			
		||||
     *
 | 
			
		||||
     * @param refresher Refresher.
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    async doRefresh(refresher?: IonRefresher): Promise<void> {
 | 
			
		||||
        await CoreUtils.ignoreErrors(Promise.all([
 | 
			
		||||
            AddonModImscp.invalidateContent(this.cmId, this.courseId),
 | 
			
		||||
            CoreCourseModulePrefetchDelegate.invalidateCourseUpdates(this.courseId), // To detect if IMSCP was updated.
 | 
			
		||||
        ]));
 | 
			
		||||
 | 
			
		||||
        await CoreUtils.ignoreErrors(this.fetchContent(true));
 | 
			
		||||
 | 
			
		||||
        refresher?.complete();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Loads an item.
 | 
			
		||||
     *
 | 
			
		||||
     * @param itemHref Item Href.
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    async loadItemHref(itemHref?: string): Promise<void> {
 | 
			
		||||
        if (!this.module) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const src = await AddonModImscp.getIframeSrc(this.module, itemHref);
 | 
			
		||||
        this.currentHref = itemHref;
 | 
			
		||||
 | 
			
		||||
        this.navigationItems = this.items.map((item) => ({
 | 
			
		||||
            item: item,
 | 
			
		||||
            current: item.href == this.currentHref,
 | 
			
		||||
            enabled: !!item.href,
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        if (this.src && src == this.src) {
 | 
			
		||||
            // Re-loading same page. Set it to empty and then re-set the src in the next digest so it detects it has changed.
 | 
			
		||||
            this.src = '';
 | 
			
		||||
            setTimeout(() => {
 | 
			
		||||
                this.src = src;
 | 
			
		||||
            });
 | 
			
		||||
        } else {
 | 
			
		||||
            this.src = src;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Loads an item.
 | 
			
		||||
     *
 | 
			
		||||
     * @param item Item.
 | 
			
		||||
     */
 | 
			
		||||
    loadItem(item: AddonModImscpTocItem): void {
 | 
			
		||||
        this.loadItemHref(item.href);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Show the TOC.
 | 
			
		||||
     */
 | 
			
		||||
    async showToc(): Promise<void> {
 | 
			
		||||
        // Create the toc modal.
 | 
			
		||||
        const modalData = await CoreDomUtils.openSideModal<string>({
 | 
			
		||||
            component: AddonModImscpTocComponent,
 | 
			
		||||
            componentProps: {
 | 
			
		||||
                items: this.items,
 | 
			
		||||
                selected: this.currentHref,
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        if (modalData) {
 | 
			
		||||
            this.loadItemHref(modalData);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user