forked from CIT/Vmeda.Online
		
	MOBILE-3980 book: Add index page for book
This commit is contained in:
		
							parent
							
								
									c91f24245b
								
							
						
					
					
						commit
						f4cb040aa0
					
				@ -450,6 +450,7 @@
 | 
				
			|||||||
  "addon.mod_bigbluebuttonbn.view_message_session_started_at": "bigbluebuttonbn",
 | 
					  "addon.mod_bigbluebuttonbn.view_message_session_started_at": "bigbluebuttonbn",
 | 
				
			||||||
  "addon.mod_bigbluebuttonbn.view_message_viewer": "bigbluebuttonbn",
 | 
					  "addon.mod_bigbluebuttonbn.view_message_viewer": "bigbluebuttonbn",
 | 
				
			||||||
  "addon.mod_bigbluebuttonbn.view_message_viewers": "bigbluebuttonbn",
 | 
					  "addon.mod_bigbluebuttonbn.view_message_viewers": "bigbluebuttonbn",
 | 
				
			||||||
 | 
					  "addon.mod_book.book:read": "book",
 | 
				
			||||||
  "addon.mod_book.errorchapter": "book",
 | 
					  "addon.mod_book.errorchapter": "book",
 | 
				
			||||||
  "addon.mod_book.modulenameplural": "book",
 | 
					  "addon.mod_book.modulenameplural": "book",
 | 
				
			||||||
  "addon.mod_book.navnexttitle": "book",
 | 
					  "addon.mod_book.navnexttitle": "book",
 | 
				
			||||||
 | 
				
			|||||||
@ -23,6 +23,10 @@ const routes: Routes = [
 | 
				
			|||||||
        path: ':courseId/:cmId',
 | 
					        path: ':courseId/:cmId',
 | 
				
			||||||
        component: AddonModBookIndexPage,
 | 
					        component: AddonModBookIndexPage,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        path: ':courseId/:cmId/contents',
 | 
				
			||||||
 | 
					        loadChildren: () => import('./pages/contents/contents.module').then(m => m.AddonModBookContentsPageModule),
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@NgModule({
 | 
					@NgModule({
 | 
				
			||||||
 | 
				
			|||||||
@ -16,7 +16,6 @@ import { NgModule } from '@angular/core';
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
					import { CoreSharedModule } from '@/core/shared.module';
 | 
				
			||||||
import { CoreCourseComponentsModule } from '@features/course/components/components.module';
 | 
					import { CoreCourseComponentsModule } from '@features/course/components/components.module';
 | 
				
			||||||
import { CoreTagComponentsModule } from '@features/tag/components/components.module';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { AddonModBookIndexComponent } from './index/index';
 | 
					import { AddonModBookIndexComponent } from './index/index';
 | 
				
			||||||
import { AddonModBookTocComponent } from './toc/toc';
 | 
					import { AddonModBookTocComponent } from './toc/toc';
 | 
				
			||||||
@ -29,7 +28,6 @@ import { AddonModBookTocComponent } from './toc/toc';
 | 
				
			|||||||
    imports: [
 | 
					    imports: [
 | 
				
			||||||
        CoreSharedModule,
 | 
					        CoreSharedModule,
 | 
				
			||||||
        CoreCourseComponentsModule,
 | 
					        CoreCourseComponentsModule,
 | 
				
			||||||
        CoreTagComponentsModule,
 | 
					 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    exports: [
 | 
					    exports: [
 | 
				
			||||||
        AddonModBookIndexComponent,
 | 
					        AddonModBookIndexComponent,
 | 
				
			||||||
 | 
				
			|||||||
@ -1,8 +1,5 @@
 | 
				
			|||||||
<!-- Buttons to add to the header. -->
 | 
					<!-- Buttons to add to the header. -->
 | 
				
			||||||
<core-navbar-buttons slot="end">
 | 
					<core-navbar-buttons slot="end">
 | 
				
			||||||
    <ion-button (click)="showToc()" [attr.aria-label]="'addon.mod_book.toc' | translate" aria-haspopup="true" *ngIf="loaded">
 | 
					 | 
				
			||||||
        <ion-icon name="fas-bookmark" slot="icon-only" aria-hidden="true"></ion-icon>
 | 
					 | 
				
			||||||
    </ion-button>
 | 
					 | 
				
			||||||
    <core-context-menu>
 | 
					    <core-context-menu>
 | 
				
			||||||
        <core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate" [href]="externalUrl"
 | 
					        <core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate" [href]="externalUrl"
 | 
				
			||||||
            iconAction="fas-external-link-alt" [showBrowserWarning]="false"></core-context-menu-item>
 | 
					            iconAction="fas-external-link-alt" [showBrowserWarning]="false"></core-context-menu-item>
 | 
				
			||||||
@ -28,31 +25,30 @@
 | 
				
			|||||||
        [courseId]="courseId">
 | 
					        [courseId]="courseId">
 | 
				
			||||||
    </core-course-module-info>
 | 
					    </core-course-module-info>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <ion-card class="core-warning-card" *ngIf="warning">
 | 
					    <ion-list>
 | 
				
			||||||
        <ion-item>
 | 
					        <ion-item>
 | 
				
			||||||
            <ion-icon name="fas-exclamation-triangle" slot="start" aria-hidden="true"></ion-icon>
 | 
					            <ion-label>
 | 
				
			||||||
            <ion-label><span [innerHTML]="warning"></span></ion-label>
 | 
					                <h2>{{ 'addon.mod_book.toc' | translate }}</h2>
 | 
				
			||||||
 | 
					            </ion-label>
 | 
				
			||||||
        </ion-item>
 | 
					        </ion-item>
 | 
				
			||||||
    </ion-card>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <div class="safe-area-padding-horizontal">
 | 
					        <ion-item class="ion-text-wrap" *ngFor="let chapter of chapters" [class.item-dimmed]="chapter.hidden" button detail="true"
 | 
				
			||||||
        <core-navigation-bar *ngIf="displayNavBar" [items]="navigationItems" [showTitles]="displayTitlesInNavBar"
 | 
					            (click)="openBook(chapter.id)">
 | 
				
			||||||
            previousTranslate="addon.mod_book.navprevtitle" nextTranslate="addon.mod_book.navnexttitle" (action)="changeChapter($event.id)">
 | 
					            <ion-label>
 | 
				
			||||||
        </core-navigation-bar>
 | 
					                <p [class.ion-padding-start]="addPadding && chapter.level == 1 ? true : null">
 | 
				
			||||||
 | 
					                    <span *ngIf="showNumbers" class="addon-mod-book-number">{{chapter.indexNumber}} </span>
 | 
				
			||||||
 | 
					                    <span *ngIf="showBullets" class="addon-mod-book-bullet">• </span>
 | 
				
			||||||
 | 
					                    <core-format-text [text]="chapter.title" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
 | 
				
			||||||
 | 
					                    </core-format-text>
 | 
				
			||||||
 | 
					                </p>
 | 
				
			||||||
 | 
					            </ion-label>
 | 
				
			||||||
 | 
					        </ion-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <core-swipe-slides [manager]="manager" [options]="slidesOpts">
 | 
					        <ion-button class="ion-margin" expand="block" (click)="openBook()">
 | 
				
			||||||
            <ng-template let-chapter="item">
 | 
					            {{ 'addon.mod_book.book:read' | translate }}
 | 
				
			||||||
                <div class="ion-padding">
 | 
					        </ion-button>
 | 
				
			||||||
                    <core-format-text [component]="component" [componentId]="componentId" [text]="chapter.content" contextLevel="module"
 | 
					
 | 
				
			||||||
                        [contextInstanceId]="module.id" [courseId]="courseId"></core-format-text>
 | 
					    </ion-list>
 | 
				
			||||||
                    <div class="ion-margin-top" *ngIf="chapter.tags?.length > 0">
 | 
					 | 
				
			||||||
                        <strong>{{ 'core.tag.tags' | translate }}: </strong>
 | 
					 | 
				
			||||||
                        <core-tag-list [tags]="chapter.tags"></core-tag-list>
 | 
					 | 
				
			||||||
                    </div>
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
            </ng-template>
 | 
					 | 
				
			||||||
        </core-swipe-slides>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
</core-loading>
 | 
					</core-loading>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -12,33 +12,15 @@
 | 
				
			|||||||
// See the License for the specific language governing permissions and
 | 
					// See the License for the specific language governing permissions and
 | 
				
			||||||
// limitations under the License.
 | 
					// limitations under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { Component, Optional, Input, OnInit, ViewChild, ElementRef, OnDestroy } from '@angular/core';
 | 
					import { Component, Optional, OnInit, OnDestroy } from '@angular/core';
 | 
				
			||||||
import { CoreCourseModuleMainResourceComponent } from '@features/course/classes/main-resource-component';
 | 
					import { CoreCourseModuleMainResourceComponent } from '@features/course/classes/main-resource-component';
 | 
				
			||||||
import {
 | 
					import { AddonModBook, AddonModBookBookWSData, AddonModBookNumbering, AddonModBookTocChapter } from '../../services/book';
 | 
				
			||||||
    AddonModBookProvider,
 | 
					 | 
				
			||||||
    AddonModBookContentsMap,
 | 
					 | 
				
			||||||
    AddonModBookTocChapter,
 | 
					 | 
				
			||||||
    AddonModBookNavStyle,
 | 
					 | 
				
			||||||
    AddonModBook,
 | 
					 | 
				
			||||||
    AddonModBookBookWSData,
 | 
					 | 
				
			||||||
} from '../../services/book';
 | 
					 | 
				
			||||||
import { CoreTag, CoreTagItem } from '@features/tag/services/tag';
 | 
					 | 
				
			||||||
import { CoreDomUtils } from '@services/utils/dom';
 | 
					 | 
				
			||||||
import { CoreCourseContentsPage } from '@features/course/pages/contents/contents';
 | 
					import { CoreCourseContentsPage } from '@features/course/pages/contents/contents';
 | 
				
			||||||
import { CoreUtils } from '@services/utils/utils';
 | 
					 | 
				
			||||||
import { CoreCourse } from '@features/course/services/course';
 | 
					import { CoreCourse } from '@features/course/services/course';
 | 
				
			||||||
import { AddonModBookTocComponent } from '../toc/toc';
 | 
					import { CoreNavigator } from '@services/navigator';
 | 
				
			||||||
import { CoreNavigationBarItem } from '@components/navigation-bar/navigation-bar';
 | 
					 | 
				
			||||||
import { CoreError } from '@classes/errors/error';
 | 
					 | 
				
			||||||
import { Translate } from '@singletons';
 | 
					 | 
				
			||||||
import { CoreSwipeSlidesComponent, CoreSwipeSlidesOptions } from '@components/swipe-slides/swipe-slides';
 | 
					 | 
				
			||||||
import { CoreSwipeSlidesItemsManagerSource } from '@classes/items-management/swipe-slides-items-manager-source';
 | 
					 | 
				
			||||||
import { CoreCourseModule } from '@features/course/services/course-helper';
 | 
					 | 
				
			||||||
import { CoreSwipeSlidesItemsManager } from '@classes/items-management/swipe-slides-items-manager';
 | 
					 | 
				
			||||||
import { CoreTextUtils } from '@services/utils/text';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Component that displays a book.
 | 
					 * Component that displays a book entry page.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
    selector: 'addon-mod-book-index',
 | 
					    selector: 'addon-mod-book-index',
 | 
				
			||||||
@ -46,191 +28,63 @@ import { CoreTextUtils } from '@services/utils/text';
 | 
				
			|||||||
})
 | 
					})
 | 
				
			||||||
export class AddonModBookIndexComponent extends CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy {
 | 
					export class AddonModBookIndexComponent extends CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @ViewChild(CoreSwipeSlidesComponent) slides?: CoreSwipeSlidesComponent;
 | 
					    showNumbers = true;
 | 
				
			||||||
 | 
					    addPadding = true;
 | 
				
			||||||
 | 
					    showBullets = false;
 | 
				
			||||||
 | 
					    chapters: AddonModBookTocChapter[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Input() initialChapterId?: number; // The initial chapter ID to load.
 | 
					    protected book?: AddonModBookBookWSData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    component = AddonModBookProvider.COMPONENT;
 | 
					    constructor( @Optional() courseContentsPage?: CoreCourseContentsPage) {
 | 
				
			||||||
    manager?: CoreSwipeSlidesItemsManager<LoadedChapter, AddonModBookSlidesItemsManagerSource>;
 | 
					 | 
				
			||||||
    warning = '';
 | 
					 | 
				
			||||||
    displayNavBar = true;
 | 
					 | 
				
			||||||
    navigationItems: CoreNavigationBarItem<AddonModBookTocChapter>[] = [];
 | 
					 | 
				
			||||||
    displayTitlesInNavBar = false;
 | 
					 | 
				
			||||||
    slidesOpts: CoreSwipeSlidesOptions = {
 | 
					 | 
				
			||||||
        autoHeight: true,
 | 
					 | 
				
			||||||
        scrollOnChange: 'top',
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected firstLoad = true;
 | 
					 | 
				
			||||||
    protected element: HTMLElement;
 | 
					 | 
				
			||||||
    protected managerUnsubscribe?: () => void;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    constructor(
 | 
					 | 
				
			||||||
        elementRef: ElementRef,
 | 
					 | 
				
			||||||
        @Optional() courseContentsPage?: CoreCourseContentsPage,
 | 
					 | 
				
			||||||
    ) {
 | 
					 | 
				
			||||||
        super('AddonModBookIndexComponent', courseContentsPage);
 | 
					        super('AddonModBookIndexComponent', courseContentsPage);
 | 
				
			||||||
 | 
					 | 
				
			||||||
        this.element = elementRef.nativeElement;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Component being initialized.
 | 
					     * @inheritdoc
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    async ngOnInit(): Promise<void> {
 | 
					    async ngOnInit(): Promise<void> {
 | 
				
			||||||
        super.ngOnInit();
 | 
					        super.ngOnInit();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const source = new AddonModBookSlidesItemsManagerSource(
 | 
					 | 
				
			||||||
            this.courseId,
 | 
					 | 
				
			||||||
            this.module,
 | 
					 | 
				
			||||||
            CoreTag.areTagsAvailableInSite(),
 | 
					 | 
				
			||||||
            this.initialChapterId,
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        this.manager = new CoreSwipeSlidesItemsManager(source);
 | 
					 | 
				
			||||||
        this.managerUnsubscribe = this.manager.addListener({
 | 
					 | 
				
			||||||
            onSelectedItemUpdated: (item) => {
 | 
					 | 
				
			||||||
                this.onChapterViewed(item.id);
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        this.loadContent();
 | 
					        this.loadContent();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    get book(): AddonModBookBookWSData | undefined {
 | 
					    /**
 | 
				
			||||||
        return this.manager?.getSource().book;
 | 
					     * @inheritdoc
 | 
				
			||||||
    }
 | 
					     */
 | 
				
			||||||
 | 
					    protected async fetchContent(refresh?: boolean): Promise<void> {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            this.book = await AddonModBook.getBook(this.courseId, this.module.id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    get chapters(): AddonModBookTocChapter[] {
 | 
					            if (this.book) {
 | 
				
			||||||
        return this.manager?.getSource().chapters || [];
 | 
					                this.dataRetrieved.emit(this.book);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                this.description = this.book.intro;
 | 
				
			||||||
 | 
					                this.showNumbers = this.book.numbering == AddonModBookNumbering.NUMBERS;
 | 
				
			||||||
 | 
					                this.showBullets = this.book.numbering == AddonModBookNumbering.BULLETS;
 | 
				
			||||||
 | 
					                this.addPadding = this.book.numbering != AddonModBookNumbering.NONE;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const contents = await CoreCourse.getModuleContents(this.module, this.courseId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.chapters = AddonModBook.getTocList(contents);
 | 
				
			||||||
 | 
					        } finally {
 | 
				
			||||||
 | 
					            this.fillContextMenu(refresh);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Show the TOC.
 | 
					     * Open the book in a certain chapter.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param chapterId Chapter to open, undefined for first chapter.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    async showToc(): Promise<void> {
 | 
					    openBook(chapterId?: number): void {
 | 
				
			||||||
        // Create the toc modal.
 | 
					        CoreNavigator.navigate('contents', {
 | 
				
			||||||
        const visibleChapter = this.manager?.getSelectedItem();
 | 
					            params: {
 | 
				
			||||||
 | 
					                cmId: this.module.id,
 | 
				
			||||||
        const modalData = await CoreDomUtils.openSideModal<number>({
 | 
					 | 
				
			||||||
            component: AddonModBookTocComponent,
 | 
					 | 
				
			||||||
            componentProps: {
 | 
					 | 
				
			||||||
                moduleId: this.module.id,
 | 
					 | 
				
			||||||
                chapters: this.chapters,
 | 
					 | 
				
			||||||
                selected: visibleChapter,
 | 
					 | 
				
			||||||
                courseId: this.courseId,
 | 
					                courseId: this.courseId,
 | 
				
			||||||
                book: this.book,
 | 
					                chapterId,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (modalData) {
 | 
					 | 
				
			||||||
            this.changeChapter(modalData);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Change the current chapter.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param chapterId Chapter to load.
 | 
					 | 
				
			||||||
     * @return Promise resolved when done.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    changeChapter(chapterId: number): void {
 | 
					 | 
				
			||||||
        if (!chapterId) {
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        this.slides?.slideToItem({ id: chapterId });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Perform the invalidate content function.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return Resolved when done.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    protected async invalidateContent(): Promise<void> {
 | 
					 | 
				
			||||||
        await this.manager?.getSource().invalidateContent();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Download book contents and load the current chapter.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param refresh Whether we're refreshing data.
 | 
					 | 
				
			||||||
     * @return Promise resolved when done.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    protected async fetchContent(refresh = false): Promise<void> {
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            const source = this.manager?.getSource();
 | 
					 | 
				
			||||||
            if (!source) {
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            const downloadResult = await this.downloadResourceIfNeeded(refresh);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            const book = await source.loadBookData();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (book) {
 | 
					 | 
				
			||||||
                this.dataRetrieved.emit(book);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                this.description = book.intro;
 | 
					 | 
				
			||||||
                this.displayNavBar = book.navstyle != AddonModBookNavStyle.TOC_ONLY;
 | 
					 | 
				
			||||||
                this.displayTitlesInNavBar = book.navstyle == AddonModBookNavStyle.TEXT;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // Get contents. No need to refresh, it has been done in downloadResourceIfNeeded.
 | 
					 | 
				
			||||||
            await source.loadContents();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            await source.load();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            this.warning = downloadResult?.failed ? this.getErrorDownloadingSomeFilesMessage(downloadResult.error || '') : '';
 | 
					 | 
				
			||||||
        } finally {
 | 
					 | 
				
			||||||
            // Pass false because downloadResourceIfNeeded already invalidates and refresh data if refresh=true.
 | 
					 | 
				
			||||||
            this.fillContextMenu(false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Update data related to chapter being viewed.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param chapterId Chapter viewed.
 | 
					 | 
				
			||||||
     * @return Promise resolved when done.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    protected async onChapterViewed(chapterId: number): Promise<void> {
 | 
					 | 
				
			||||||
        // Don't log the chapter ID when the user has just opened the book.
 | 
					 | 
				
			||||||
        const logChapterId = this.firstLoad;
 | 
					 | 
				
			||||||
        this.firstLoad = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (this.displayNavBar) {
 | 
					 | 
				
			||||||
            this.navigationItems = this.getNavigationItems(chapterId);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Chapter loaded, log view.
 | 
					 | 
				
			||||||
        await CoreUtils.ignoreErrors(AddonModBook.logView(
 | 
					 | 
				
			||||||
            this.module.instance,
 | 
					 | 
				
			||||||
            logChapterId ? chapterId : undefined,
 | 
					 | 
				
			||||||
            this.module.name,
 | 
					 | 
				
			||||||
        ));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const currentChapterIndex = this.chapters.findIndex((chapter) => chapter.id == chapterId);
 | 
					 | 
				
			||||||
        const isLastChapter = currentChapterIndex < 0 || this.chapters[currentChapterIndex + 1] === undefined;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Module is completed when last chapter is viewed, so we only check completion if the last is reached.
 | 
					 | 
				
			||||||
        if (isLastChapter) {
 | 
					 | 
				
			||||||
            CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Converts chapters to navigation items.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param chapterId Current chapter Id.
 | 
					 | 
				
			||||||
     * @return Navigation items.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    protected getNavigationItems(chapterId: number): CoreNavigationBarItem<AddonModBookTocChapter>[] {
 | 
					 | 
				
			||||||
        return this.chapters.map((chapter) => ({
 | 
					 | 
				
			||||||
            item: chapter,
 | 
					 | 
				
			||||||
            title: chapter.title,
 | 
					 | 
				
			||||||
            current: chapter.id == chapterId,
 | 
					 | 
				
			||||||
            enabled: true,
 | 
					 | 
				
			||||||
        }));
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
@ -238,99 +92,6 @@ export class AddonModBookIndexComponent extends CoreCourseModuleMainResourceComp
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    ngOnDestroy(): void {
 | 
					    ngOnDestroy(): void {
 | 
				
			||||||
        super.ngOnDestroy();
 | 
					        super.ngOnDestroy();
 | 
				
			||||||
 | 
					 | 
				
			||||||
        this.managerUnsubscribe && this.managerUnsubscribe();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type LoadedChapter = {
 | 
					 | 
				
			||||||
    id: number;
 | 
					 | 
				
			||||||
    content?: string;
 | 
					 | 
				
			||||||
    tags?: CoreTagItem[];
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Helper to manage swiping within a collection of chapters.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
class AddonModBookSlidesItemsManagerSource extends CoreSwipeSlidesItemsManagerSource<LoadedChapter> {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    readonly COURSE_ID: number;
 | 
					 | 
				
			||||||
    readonly MODULE: CoreCourseModule;
 | 
					 | 
				
			||||||
    readonly TAGS_ENABLED: boolean;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    book?: AddonModBookBookWSData;
 | 
					 | 
				
			||||||
    chapters: AddonModBookTocChapter[] = [];
 | 
					 | 
				
			||||||
    contentsMap: AddonModBookContentsMap = {};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    constructor(courseId: number, module: CoreCourseModule, tagsEnabled: boolean, initialChapterId?: number) {
 | 
					 | 
				
			||||||
        super(initialChapterId ? { id: initialChapterId } : undefined);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        this.COURSE_ID = courseId;
 | 
					 | 
				
			||||||
        this.MODULE = module;
 | 
					 | 
				
			||||||
        this.TAGS_ENABLED = tagsEnabled;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * @inheritdoc
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    getItemId(item: LoadedChapter): string | number {
 | 
					 | 
				
			||||||
        return item.id;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Load book data from WS.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return Promise resolved when done.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    async loadBookData(): Promise<AddonModBookBookWSData> {
 | 
					 | 
				
			||||||
        this.book = await AddonModBook.getBook(this.COURSE_ID, this.MODULE.id);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return this.book;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Load module contents.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    async loadContents(): Promise<void> {
 | 
					 | 
				
			||||||
        const contents = await CoreCourse.getModuleContents(this.MODULE, this.COURSE_ID);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        this.contentsMap = AddonModBook.getContentsMap(contents);
 | 
					 | 
				
			||||||
        this.chapters = AddonModBook.getTocList(contents);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * @inheritdoc
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    protected async loadItems(): Promise<LoadedChapter[]> {
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            const newChapters = await Promise.all(this.chapters.map(async (chapter) => {
 | 
					 | 
				
			||||||
                const content = await AddonModBook.getChapterContent(this.contentsMap, chapter.id, this.MODULE.id);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                return {
 | 
					 | 
				
			||||||
                    id: chapter.id,
 | 
					 | 
				
			||||||
                    content,
 | 
					 | 
				
			||||||
                    tags: this.TAGS_ENABLED ? this.contentsMap[chapter.id].tags : [],
 | 
					 | 
				
			||||||
                };
 | 
					 | 
				
			||||||
            }));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return newChapters;
 | 
					 | 
				
			||||||
        } catch (error) {
 | 
					 | 
				
			||||||
            if (!CoreTextUtils.getErrorMessageFromError(error)) {
 | 
					 | 
				
			||||||
                throw new CoreError(Translate.instant('addon.mod_book.errorchapter'));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            throw error;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Perform the invalidate content function.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return Resolved when done.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    invalidateContent(): Promise<void> {
 | 
					 | 
				
			||||||
        return AddonModBook.invalidateContent(this.MODULE.id, this.COURSE_ID);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,8 +1,9 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
 | 
					    "book:read": "View book",
 | 
				
			||||||
    "errorchapter": "Error reading chapter of book.",
 | 
					    "errorchapter": "Error reading chapter of book.",
 | 
				
			||||||
    "modulenameplural": "Books",
 | 
					    "modulenameplural": "Books",
 | 
				
			||||||
    "navnexttitle": "Next: {{$a}}",
 | 
					    "navnexttitle": "Next: {{$a}}",
 | 
				
			||||||
    "navprevtitle": "Previous: {{$a}}",
 | 
					    "navprevtitle": "Previous: {{$a}}",
 | 
				
			||||||
    "tagarea_book_chapters": "Book chapters",
 | 
					    "tagarea_book_chapters": "Book chapters",
 | 
				
			||||||
    "toc": "Table of contents"
 | 
					    "toc": "Table of contents"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										53
									
								
								src/addons/mod/book/pages/contents/contents.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								src/addons/mod/book/pages/contents/contents.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,53 @@
 | 
				
			|||||||
 | 
					<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 (click)="showToc()" [attr.aria-label]="'addon.mod_book.toc' | translate" aria-haspopup="true" *ngIf="loaded">
 | 
				
			||||||
 | 
					                <ion-icon name="fas-bookmark" slot="icon-only" aria-hidden="true"></ion-icon>
 | 
				
			||||||
 | 
					            </ion-button>
 | 
				
			||||||
 | 
					        </ion-buttons>
 | 
				
			||||||
 | 
					    </ion-toolbar>
 | 
				
			||||||
 | 
					</ion-header>
 | 
				
			||||||
 | 
					<ion-content>
 | 
				
			||||||
 | 
					    <ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="doRefresh($event.target)">
 | 
				
			||||||
 | 
					        <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
 | 
				
			||||||
 | 
					    </ion-refresher>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <core-loading [hideUntil]="loaded">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <ion-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="safe-area-padding-horizontal">
 | 
				
			||||||
 | 
					            <core-navigation-bar *ngIf="displayNavBar" [items]="navigationItems" [showTitles]="displayTitlesInNavBar"
 | 
				
			||||||
 | 
					                previousTranslate="addon.mod_book.navprevtitle" nextTranslate="addon.mod_book.navnexttitle"
 | 
				
			||||||
 | 
					                (action)="changeChapter($event.id)">
 | 
				
			||||||
 | 
					            </core-navigation-bar>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <core-swipe-slides [manager]="manager" [options]="slidesOpts">
 | 
				
			||||||
 | 
					                <ng-template let-chapter="item">
 | 
				
			||||||
 | 
					                    <div class="ion-padding">
 | 
				
			||||||
 | 
					                        <core-format-text [component]="component" [componentId]="cmId" [text]="chapter.content" contextLevel="module"
 | 
				
			||||||
 | 
					                            [contextInstanceId]="cmId" [courseId]="courseId"></core-format-text>
 | 
				
			||||||
 | 
					                        <div class="ion-margin-top" *ngIf="chapter.tags?.length > 0">
 | 
				
			||||||
 | 
					                            <strong>{{ 'core.tag.tags' | translate }}: </strong>
 | 
				
			||||||
 | 
					                            <core-tag-list [tags]="chapter.tags"></core-tag-list>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                </ng-template>
 | 
				
			||||||
 | 
					            </core-swipe-slides>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </core-loading>
 | 
				
			||||||
 | 
					</ion-content>
 | 
				
			||||||
							
								
								
									
										40
									
								
								src/addons/mod/book/pages/contents/contents.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/addons/mod/book/pages/contents/contents.module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,40 @@
 | 
				
			|||||||
 | 
					// (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 { AddonModBookContentsPage } from './contents';
 | 
				
			||||||
 | 
					import { CoreTagComponentsModule } from '@features/tag/components/components.module';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const routes: Routes = [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        path: '',
 | 
				
			||||||
 | 
					        component: AddonModBookContentsPage,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@NgModule({
 | 
				
			||||||
 | 
					    imports: [
 | 
				
			||||||
 | 
					        RouterModule.forChild(routes),
 | 
				
			||||||
 | 
					        CoreSharedModule,
 | 
				
			||||||
 | 
					        CoreTagComponentsModule,
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    declarations: [
 | 
				
			||||||
 | 
					        AddonModBookContentsPage,
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    exports: [RouterModule],
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					export class AddonModBookContentsPageModule {}
 | 
				
			||||||
							
								
								
									
										428
									
								
								src/addons/mod/book/pages/contents/contents.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										428
									
								
								src/addons/mod/book/pages/contents/contents.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,428 @@
 | 
				
			|||||||
 | 
					// (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, OnDestroy, OnInit, ViewChild } from '@angular/core';
 | 
				
			||||||
 | 
					import { CoreError } from '@classes/errors/error';
 | 
				
			||||||
 | 
					import { CoreSwipeSlidesItemsManager } from '@classes/items-management/swipe-slides-items-manager';
 | 
				
			||||||
 | 
					import { CoreSwipeSlidesItemsManagerSource } from '@classes/items-management/swipe-slides-items-manager-source';
 | 
				
			||||||
 | 
					import { CoreNavigationBarItem } from '@components/navigation-bar/navigation-bar';
 | 
				
			||||||
 | 
					import { CoreSwipeSlidesComponent, CoreSwipeSlidesOptions } from '@components/swipe-slides/swipe-slides';
 | 
				
			||||||
 | 
					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 { CoreTag, CoreTagItem } from '@features/tag/services/tag';
 | 
				
			||||||
 | 
					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 { AddonModBookTocComponent } from '../../components/toc/toc';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					    AddonModBook,
 | 
				
			||||||
 | 
					    AddonModBookBookWSData,
 | 
				
			||||||
 | 
					    AddonModBookContentsMap,
 | 
				
			||||||
 | 
					    AddonModBookNavStyle,
 | 
				
			||||||
 | 
					    AddonModBookProvider,
 | 
				
			||||||
 | 
					    AddonModBookTocChapter,
 | 
				
			||||||
 | 
					} from '../../services/book';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Page that displays a book contents.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Component({
 | 
				
			||||||
 | 
					    selector: 'page-addon-mod-book-contents',
 | 
				
			||||||
 | 
					    templateUrl: 'contents.html',
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					export class AddonModBookContentsPage implements OnInit, OnDestroy {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @ViewChild(CoreSwipeSlidesComponent) slides?: CoreSwipeSlidesComponent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    title!: string;
 | 
				
			||||||
 | 
					    cmId!: number;
 | 
				
			||||||
 | 
					    courseId!: number;
 | 
				
			||||||
 | 
					    initialChapterId?: number;
 | 
				
			||||||
 | 
					    component = AddonModBookProvider.COMPONENT;
 | 
				
			||||||
 | 
					    manager?: CoreSwipeSlidesItemsManager<LoadedChapter, AddonModBookSlidesItemsManagerSource>;
 | 
				
			||||||
 | 
					    warning = '';
 | 
				
			||||||
 | 
					    displayNavBar = true;
 | 
				
			||||||
 | 
					    navigationItems: CoreNavigationBarItem<AddonModBookTocChapter>[] = [];
 | 
				
			||||||
 | 
					    displayTitlesInNavBar = false;
 | 
				
			||||||
 | 
					    slidesOpts: CoreSwipeSlidesOptions = {
 | 
				
			||||||
 | 
					        autoHeight: true,
 | 
				
			||||||
 | 
					        scrollOnChange: 'top',
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    loaded = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected firstLoad = true;
 | 
				
			||||||
 | 
					    protected managerUnsubscribe?: () => void;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @inheritdoc
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    ngOnInit(): void {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            this.cmId = CoreNavigator.getRequiredRouteNumberParam('cmId');
 | 
				
			||||||
 | 
					            this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId');
 | 
				
			||||||
 | 
					            this.initialChapterId = CoreNavigator.getRouteNumberParam('chapterId');
 | 
				
			||||||
 | 
					        } catch (error) {
 | 
				
			||||||
 | 
					            CoreDomUtils.showErrorModal(error);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            CoreNavigator.back();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const source = new AddonModBookSlidesItemsManagerSource(
 | 
				
			||||||
 | 
					            this.courseId,
 | 
				
			||||||
 | 
					            this.cmId,
 | 
				
			||||||
 | 
					            CoreTag.areTagsAvailableInSite(),
 | 
				
			||||||
 | 
					            this.initialChapterId,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        this.manager = new CoreSwipeSlidesItemsManager(source);
 | 
				
			||||||
 | 
					        this.managerUnsubscribe = this.manager.addListener({
 | 
				
			||||||
 | 
					            onSelectedItemUpdated: (item) => {
 | 
				
			||||||
 | 
					                this.onChapterViewed(item.id);
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.fetchContent();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    get module(): CoreCourseModuleData | undefined {
 | 
				
			||||||
 | 
					        return this.manager?.getSource().module;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    get book(): AddonModBookBookWSData | undefined {
 | 
				
			||||||
 | 
					        return this.manager?.getSource().book;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    get chapters(): AddonModBookTocChapter[] {
 | 
				
			||||||
 | 
					        return this.manager?.getSource().chapters || [];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Download book contents and load the current chapter.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param refresh Whether we're refreshing data.
 | 
				
			||||||
 | 
					     * @return Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async fetchContent(refresh = false): Promise<void> {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            const source = this.manager?.getSource();
 | 
				
			||||||
 | 
					            if (!source) {
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const { module, book } = await source.loadBookData();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const downloadResult = await this.downloadResourceIfNeeded(module, refresh);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (book) {
 | 
				
			||||||
 | 
					                this.displayNavBar = book.navstyle != AddonModBookNavStyle.TOC_ONLY;
 | 
				
			||||||
 | 
					                this.displayTitlesInNavBar = book.navstyle == AddonModBookNavStyle.TEXT;
 | 
				
			||||||
 | 
					                this.title = book.name;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                this.title = this.title || Translate.instant('addon.mod_book.book:read');
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Get contents. No need to refresh, it has been done in downloadResourceIfNeeded.
 | 
				
			||||||
 | 
					            await source.loadContents();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await source.load();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (downloadResult?.failed) {
 | 
				
			||||||
 | 
					                const error = CoreTextUtils.getErrorMessageFromError(downloadResult.error) || downloadResult.error;
 | 
				
			||||||
 | 
					                this.warning = Translate.instant('core.errordownloadingsomefiles') + (error ? ' ' + error : '');
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                this.warning = '';
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } finally {
 | 
				
			||||||
 | 
					            this.loaded = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 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;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Change the current chapter.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param chapterId Chapter to load.
 | 
				
			||||||
 | 
					     * @return Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    changeChapter(chapterId: number): void {
 | 
				
			||||||
 | 
					        if (!chapterId) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.slides?.slideToItem({ id: chapterId });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Refresh the data.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param refresher Refresher.
 | 
				
			||||||
 | 
					     * @return Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async doRefresh(refresher?: IonRefresher): Promise<void> {
 | 
				
			||||||
 | 
					        if (this.manager) {
 | 
				
			||||||
 | 
					            await CoreUtils.ignoreErrors(Promise.all([
 | 
				
			||||||
 | 
					                this.manager.getSource().invalidateContent(),
 | 
				
			||||||
 | 
					                CoreCourseModulePrefetchDelegate.invalidateCourseUpdates(this.courseId), // To detect if book was updated.
 | 
				
			||||||
 | 
					            ]));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await CoreUtils.ignoreErrors(this.fetchContent(true));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        refresher?.complete();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Show the TOC.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async showToc(): Promise<void> {
 | 
				
			||||||
 | 
					        // Create the toc modal.
 | 
				
			||||||
 | 
					        const visibleChapter = this.manager?.getSelectedItem();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const modalData = await CoreDomUtils.openSideModal<number>({
 | 
				
			||||||
 | 
					            component: AddonModBookTocComponent,
 | 
				
			||||||
 | 
					            componentProps: {
 | 
				
			||||||
 | 
					                moduleId: this.cmId,
 | 
				
			||||||
 | 
					                chapters: this.chapters,
 | 
				
			||||||
 | 
					                selected: visibleChapter,
 | 
				
			||||||
 | 
					                courseId: this.courseId,
 | 
				
			||||||
 | 
					                book: this.book,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (modalData) {
 | 
				
			||||||
 | 
					            this.changeChapter(modalData);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Update data related to chapter being viewed.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param chapterId Chapter viewed.
 | 
				
			||||||
 | 
					     * @return Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async onChapterViewed(chapterId: number): Promise<void> {
 | 
				
			||||||
 | 
					        // Don't log the chapter ID when the user has just opened the book.
 | 
				
			||||||
 | 
					        const logChapterId = this.firstLoad;
 | 
				
			||||||
 | 
					        this.firstLoad = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (this.displayNavBar) {
 | 
				
			||||||
 | 
					            this.navigationItems = this.getNavigationItems(chapterId);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!this.module) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Chapter loaded, log view.
 | 
				
			||||||
 | 
					        await CoreUtils.ignoreErrors(AddonModBook.logView(
 | 
				
			||||||
 | 
					            this.module.instance,
 | 
				
			||||||
 | 
					            logChapterId ? chapterId : undefined,
 | 
				
			||||||
 | 
					            this.module.name,
 | 
				
			||||||
 | 
					        ));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const currentChapterIndex = this.chapters.findIndex((chapter) => chapter.id == chapterId);
 | 
				
			||||||
 | 
					        const isLastChapter = currentChapterIndex < 0 || this.chapters[currentChapterIndex + 1] === undefined;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Module is completed when last chapter is viewed, so we only check completion if the last is reached.
 | 
				
			||||||
 | 
					        if (isLastChapter) {
 | 
				
			||||||
 | 
					            CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Converts chapters to navigation items.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param chapterId Current chapter Id.
 | 
				
			||||||
 | 
					     * @return Navigation items.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected getNavigationItems(chapterId: number): CoreNavigationBarItem<AddonModBookTocChapter>[] {
 | 
				
			||||||
 | 
					        return this.chapters.map((chapter) => ({
 | 
				
			||||||
 | 
					            item: chapter,
 | 
				
			||||||
 | 
					            title: chapter.title,
 | 
				
			||||||
 | 
					            current: chapter.id == chapterId,
 | 
				
			||||||
 | 
					            enabled: true,
 | 
				
			||||||
 | 
					        }));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @inheritdoc
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    ngOnDestroy(): void {
 | 
				
			||||||
 | 
					        this.managerUnsubscribe && this.managerUnsubscribe();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type LoadedChapter = {
 | 
				
			||||||
 | 
					    id: number;
 | 
				
			||||||
 | 
					    content?: string;
 | 
				
			||||||
 | 
					    tags?: CoreTagItem[];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Helper to manage swiping within a collection of chapters.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class AddonModBookSlidesItemsManagerSource extends CoreSwipeSlidesItemsManagerSource<LoadedChapter> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    readonly COURSE_ID: number;
 | 
				
			||||||
 | 
					    readonly CM_ID: number;
 | 
				
			||||||
 | 
					    readonly TAGS_ENABLED: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    module?: CoreCourseModuleData;
 | 
				
			||||||
 | 
					    book?: AddonModBookBookWSData;
 | 
				
			||||||
 | 
					    chapters: AddonModBookTocChapter[] = [];
 | 
				
			||||||
 | 
					    contentsMap: AddonModBookContentsMap = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(courseId: number, cmId: number, tagsEnabled: boolean, initialChapterId?: number) {
 | 
				
			||||||
 | 
					        super(initialChapterId ? { id: initialChapterId } : undefined);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.COURSE_ID = courseId;
 | 
				
			||||||
 | 
					        this.CM_ID = cmId;
 | 
				
			||||||
 | 
					        this.TAGS_ENABLED = tagsEnabled;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @inheritdoc
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    getItemId(item: LoadedChapter): string | number {
 | 
				
			||||||
 | 
					        return item.id;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Load book data from WS.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async loadBookData(): Promise<{ module: CoreCourseModuleData; book: AddonModBookBookWSData }> {
 | 
				
			||||||
 | 
					        this.module = await CoreCourse.getModule(this.CM_ID, this.COURSE_ID);
 | 
				
			||||||
 | 
					        this.book = await AddonModBook.getBook(this.COURSE_ID, this.CM_ID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					            module: this.module,
 | 
				
			||||||
 | 
					            book: this.book,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Load module contents.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async loadContents(): Promise<void> {
 | 
				
			||||||
 | 
					        if (!this.module) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const contents = await CoreCourse.getModuleContents(this.module, this.COURSE_ID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.contentsMap = AddonModBook.getContentsMap(contents);
 | 
				
			||||||
 | 
					        this.chapters = AddonModBook.getTocList(contents);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @inheritdoc
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async loadItems(): Promise<LoadedChapter[]> {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            const newChapters = await Promise.all(this.chapters.map(async (chapter) => {
 | 
				
			||||||
 | 
					                const content = await AddonModBook.getChapterContent(this.contentsMap, chapter.id, this.CM_ID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return {
 | 
				
			||||||
 | 
					                    id: chapter.id,
 | 
				
			||||||
 | 
					                    content,
 | 
				
			||||||
 | 
					                    tags: this.TAGS_ENABLED ? this.contentsMap[chapter.id].tags : [],
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					            }));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return newChapters;
 | 
				
			||||||
 | 
					        } catch (error) {
 | 
				
			||||||
 | 
					            if (!CoreTextUtils.getErrorMessageFromError(error)) {
 | 
				
			||||||
 | 
					                throw new CoreError(Translate.instant('addon.mod_book.errorchapter'));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            throw error;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Perform the invalidate content function.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    invalidateContent(): Promise<void> {
 | 
				
			||||||
 | 
					        return AddonModBook.invalidateContent(this.CM_ID, this.COURSE_ID);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -19,6 +19,6 @@
 | 
				
			|||||||
        <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
 | 
					        <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
 | 
				
			||||||
    </ion-refresher>
 | 
					    </ion-refresher>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <addon-mod-book-index [module]="module" [courseId]="courseId" [initialChapterId]="chapterId" (dataRetrieved)="updateData($event)">
 | 
					    <addon-mod-book-index [module]="module" [courseId]="courseId" (dataRetrieved)="updateData($event)">
 | 
				
			||||||
    </addon-mod-book-index>
 | 
					    </addon-mod-book-index>
 | 
				
			||||||
</ion-content>
 | 
					</ion-content>
 | 
				
			||||||
 | 
				
			|||||||
@ -12,30 +12,19 @@
 | 
				
			|||||||
// See the License for the specific language governing permissions and
 | 
					// See the License for the specific language governing permissions and
 | 
				
			||||||
// limitations under the License.
 | 
					// limitations under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { Component, OnInit, ViewChild } from '@angular/core';
 | 
					import { Component, ViewChild } from '@angular/core';
 | 
				
			||||||
import { CoreCourseModuleMainActivityPage } from '@features/course/classes/main-activity-page';
 | 
					import { CoreCourseModuleMainActivityPage } from '@features/course/classes/main-activity-page';
 | 
				
			||||||
import { CoreNavigator } from '@services/navigator';
 | 
					 | 
				
			||||||
import { AddonModBookIndexComponent } from '../../components/index/index';
 | 
					import { AddonModBookIndexComponent } from '../../components/index/index';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Page that displays a book.
 | 
					 * Page that displays a book entry page.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
    selector: 'page-addon-mod-book-index',
 | 
					    selector: 'page-addon-mod-book-index',
 | 
				
			||||||
    templateUrl: 'index.html',
 | 
					    templateUrl: 'index.html',
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class AddonModBookIndexPage extends CoreCourseModuleMainActivityPage<AddonModBookIndexComponent> implements OnInit {
 | 
					export class AddonModBookIndexPage extends CoreCourseModuleMainActivityPage<AddonModBookIndexComponent> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @ViewChild(AddonModBookIndexComponent) activityComponent?: AddonModBookIndexComponent;
 | 
					    @ViewChild(AddonModBookIndexComponent) activityComponent?: AddonModBookIndexComponent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    chapterId?: number;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Component being initialized.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    ngOnInit(): void {
 | 
					 | 
				
			||||||
        super.ngOnInit();
 | 
					 | 
				
			||||||
        this.chapterId = CoreNavigator.getRouteNumberParam('chapterId');
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user