forked from CIT/Vmeda.Online
		
	MOBILE-3931 module: Add a new module summary page
This commit is contained in:
		
							parent
							
								
									279071634b
								
							
						
					
					
						commit
						d224876f42
					
				@ -24,6 +24,10 @@
 | 
			
		||||
            iconDescription="fas-archive" (action)="removeFiles($event)" iconAction="fas-trash" [closeOnClick]="false">
 | 
			
		||||
        </core-context-menu-item>
 | 
			
		||||
    </core-context-menu>
 | 
			
		||||
 | 
			
		||||
    <ion-button fill="clear" (click)="openModuleSummary()" aria-haspopup="true" [attr.aria-label]="'core.course.modulesummary' | translate">
 | 
			
		||||
        <ion-icon name="fas-info-circle" slot="icon-only" aria-hidden="true"></ion-icon>
 | 
			
		||||
    </ion-button>
 | 
			
		||||
</core-navbar-buttons>
 | 
			
		||||
 | 
			
		||||
<!-- Content. -->
 | 
			
		||||
 | 
			
		||||
@ -17,6 +17,10 @@
 | 
			
		||||
            [iconAction]="prefetchStatusIcon" [closeOnClick]="false">
 | 
			
		||||
        </core-context-menu-item>
 | 
			
		||||
    </core-context-menu>
 | 
			
		||||
 | 
			
		||||
    <ion-button fill="clear" (click)="openModuleSummary()" aria-haspopup="true" [attr.aria-label]="'core.course.modulesummary' | translate">
 | 
			
		||||
        <ion-icon name="fas-info-circle" slot="icon-only" aria-hidden="true"></ion-icon>
 | 
			
		||||
    </ion-button>
 | 
			
		||||
</core-navbar-buttons>
 | 
			
		||||
 | 
			
		||||
<!-- Content. -->
 | 
			
		||||
 | 
			
		||||
@ -15,6 +15,10 @@
 | 
			
		||||
            iconDescription="fas-archive" (action)="removeFiles($event)" iconAction="fas-trash" [closeOnClick]="false">
 | 
			
		||||
        </core-context-menu-item>
 | 
			
		||||
    </core-context-menu>
 | 
			
		||||
 | 
			
		||||
    <ion-button fill="clear" (click)="openModuleSummary()" aria-haspopup="true" [attr.aria-label]="'core.course.modulesummary' | translate">
 | 
			
		||||
        <ion-icon name="fas-info-circle" slot="icon-only" aria-hidden="true"></ion-icon>
 | 
			
		||||
    </ion-button>
 | 
			
		||||
</core-navbar-buttons>
 | 
			
		||||
 | 
			
		||||
<!-- Content. -->
 | 
			
		||||
 | 
			
		||||
@ -21,6 +21,10 @@
 | 
			
		||||
            [iconAction]="prefetchStatusIcon" [closeOnClick]="false">
 | 
			
		||||
        </core-context-menu-item>
 | 
			
		||||
    </core-context-menu>
 | 
			
		||||
 | 
			
		||||
    <ion-button fill="clear" (click)="openModuleSummary()" aria-haspopup="true" [attr.aria-label]="'core.course.modulesummary' | translate">
 | 
			
		||||
        <ion-icon name="fas-info-circle" slot="icon-only" aria-hidden="true"></ion-icon>
 | 
			
		||||
    </ion-button>
 | 
			
		||||
</core-navbar-buttons>
 | 
			
		||||
 | 
			
		||||
<!-- Content. -->
 | 
			
		||||
 | 
			
		||||
@ -23,6 +23,10 @@
 | 
			
		||||
            iconDescription="fas-archive" (action)="removeFiles($event)" iconAction="fas-trash" [closeOnClick]="false">
 | 
			
		||||
        </core-context-menu-item>
 | 
			
		||||
    </core-context-menu>
 | 
			
		||||
 | 
			
		||||
    <ion-button fill="clear" (click)="openModuleSummary()" aria-haspopup="true" [attr.aria-label]="'core.course.modulesummary' | translate">
 | 
			
		||||
        <ion-icon name="fas-info-circle" slot="icon-only" aria-hidden="true"></ion-icon>
 | 
			
		||||
    </ion-button>
 | 
			
		||||
</core-navbar-buttons>
 | 
			
		||||
 | 
			
		||||
<!-- Content. -->
 | 
			
		||||
 | 
			
		||||
@ -33,6 +33,10 @@
 | 
			
		||||
            iconDescription="fas-archive" (action)="removeFiles($event)" iconAction="fas-trash" [closeOnClick]="false">
 | 
			
		||||
        </core-context-menu-item>
 | 
			
		||||
    </core-context-menu>
 | 
			
		||||
 | 
			
		||||
    <ion-button fill="clear" (click)="openModuleSummary()" aria-haspopup="true" [attr.aria-label]="'core.course.modulesummary' | translate">
 | 
			
		||||
        <ion-icon name="fas-info-circle" slot="icon-only" aria-hidden="true"></ion-icon>
 | 
			
		||||
    </ion-button>
 | 
			
		||||
</core-navbar-buttons>
 | 
			
		||||
 | 
			
		||||
<!-- Content. -->
 | 
			
		||||
 | 
			
		||||
@ -23,6 +23,10 @@
 | 
			
		||||
            iconDescription="fas-archive" (action)="removeFiles($event)" iconAction="fas-trash" [closeOnClick]="false">
 | 
			
		||||
        </core-context-menu-item>
 | 
			
		||||
    </core-context-menu>
 | 
			
		||||
 | 
			
		||||
    <ion-button fill="clear" (click)="openModuleSummary()" aria-haspopup="true" [attr.aria-label]="'core.course.modulesummary' | translate">
 | 
			
		||||
        <ion-icon name="fas-info-circle" slot="icon-only" aria-hidden="true"></ion-icon>
 | 
			
		||||
    </ion-button>
 | 
			
		||||
</core-navbar-buttons>
 | 
			
		||||
 | 
			
		||||
<!-- Content. -->
 | 
			
		||||
 | 
			
		||||
@ -20,6 +20,10 @@
 | 
			
		||||
            iconDescription="fas-archive" (action)="removeFiles($event)" iconAction="fas-trash" [closeOnClick]="false">
 | 
			
		||||
        </core-context-menu-item>
 | 
			
		||||
    </core-context-menu>
 | 
			
		||||
 | 
			
		||||
    <ion-button fill="clear" (click)="openModuleSummary()" aria-haspopup="true" [attr.aria-label]="'core.course.modulesummary' | translate">
 | 
			
		||||
        <ion-icon name="fas-info-circle" slot="icon-only" aria-hidden="true"></ion-icon>
 | 
			
		||||
    </ion-button>
 | 
			
		||||
</core-navbar-buttons>
 | 
			
		||||
 | 
			
		||||
<!-- Content. -->
 | 
			
		||||
 | 
			
		||||
@ -28,6 +28,10 @@
 | 
			
		||||
            (action)="showSortOrderSelector()">
 | 
			
		||||
        </core-context-menu-item>
 | 
			
		||||
    </core-context-menu>
 | 
			
		||||
 | 
			
		||||
    <ion-button fill="clear" (click)="openModuleSummary()" aria-haspopup="true" [attr.aria-label]="'core.course.modulesummary' | translate">
 | 
			
		||||
        <ion-icon name="fas-info-circle" slot="icon-only" aria-hidden="true"></ion-icon>
 | 
			
		||||
    </ion-button>
 | 
			
		||||
</core-navbar-buttons>
 | 
			
		||||
 | 
			
		||||
<!-- Content. -->
 | 
			
		||||
 | 
			
		||||
@ -36,6 +36,10 @@
 | 
			
		||||
            iconDescription="fas-archive" (action)="removeFiles($event)" iconAction="fas-trash" [closeOnClick]="false">
 | 
			
		||||
        </core-context-menu-item>
 | 
			
		||||
    </core-context-menu>
 | 
			
		||||
 | 
			
		||||
    <ion-button fill="clear" (click)="openModuleSummary()" aria-haspopup="true" [attr.aria-label]="'core.course.modulesummary' | translate">
 | 
			
		||||
        <ion-icon name="fas-info-circle" slot="icon-only" aria-hidden="true"></ion-icon>
 | 
			
		||||
    </ion-button>
 | 
			
		||||
</core-navbar-buttons>
 | 
			
		||||
 | 
			
		||||
<!-- Content. -->
 | 
			
		||||
 | 
			
		||||
@ -30,6 +30,10 @@
 | 
			
		||||
            iconDescription="fas-archive" (action)="removeFiles($event)" iconAction="fas-trash" [closeOnClick]="false">
 | 
			
		||||
        </core-context-menu-item>
 | 
			
		||||
    </core-context-menu>
 | 
			
		||||
 | 
			
		||||
    <ion-button fill="clear" (click)="openModuleSummary()" aria-haspopup="true" [attr.aria-label]="'core.course.modulesummary' | translate">
 | 
			
		||||
        <ion-icon name="fas-info-circle" slot="icon-only" aria-hidden="true"></ion-icon>
 | 
			
		||||
    </ion-button>
 | 
			
		||||
</core-navbar-buttons>
 | 
			
		||||
 | 
			
		||||
<!-- Content. -->
 | 
			
		||||
 | 
			
		||||
@ -23,6 +23,10 @@
 | 
			
		||||
            iconDescription="fas-archive" (action)="removeFiles($event)" iconAction="fas-trash" [closeOnClick]="false">
 | 
			
		||||
        </core-context-menu-item>
 | 
			
		||||
    </core-context-menu>
 | 
			
		||||
 | 
			
		||||
    <ion-button fill="clear" (click)="openModuleSummary()" aria-haspopup="true" [attr.aria-label]="'core.course.modulesummary' | translate">
 | 
			
		||||
        <ion-icon name="fas-info-circle" slot="icon-only" aria-hidden="true"></ion-icon>
 | 
			
		||||
    </ion-button>
 | 
			
		||||
</core-navbar-buttons>
 | 
			
		||||
 | 
			
		||||
<!-- Content. -->
 | 
			
		||||
 | 
			
		||||
@ -23,6 +23,10 @@
 | 
			
		||||
            iconDescription="fas-archive" (action)="removeFiles($event)" iconAction="fas-trash" [closeOnClick]="false">
 | 
			
		||||
        </core-context-menu-item>
 | 
			
		||||
    </core-context-menu>
 | 
			
		||||
 | 
			
		||||
    <ion-button fill="clear" (click)="openModuleSummary()" aria-haspopup="true" [attr.aria-label]="'core.course.modulesummary' | translate">
 | 
			
		||||
        <ion-icon name="fas-info-circle" slot="icon-only" aria-hidden="true"></ion-icon>
 | 
			
		||||
    </ion-button>
 | 
			
		||||
</core-navbar-buttons>
 | 
			
		||||
 | 
			
		||||
<!-- Content. -->
 | 
			
		||||
 | 
			
		||||
@ -14,6 +14,10 @@
 | 
			
		||||
            (action)="doRefresh(null, $event)" [iconAction]="refreshIcon" [closeOnClick]="false">
 | 
			
		||||
        </core-context-menu-item>
 | 
			
		||||
    </core-context-menu>
 | 
			
		||||
 | 
			
		||||
    <ion-button fill="clear" (click)="openModuleSummary()" aria-haspopup="true" [attr.aria-label]="'core.course.modulesummary' | translate">
 | 
			
		||||
        <ion-icon name="fas-info-circle" slot="icon-only" aria-hidden="true"></ion-icon>
 | 
			
		||||
    </ion-button>
 | 
			
		||||
</core-navbar-buttons>
 | 
			
		||||
 | 
			
		||||
<!-- Content. -->
 | 
			
		||||
 | 
			
		||||
@ -20,6 +20,10 @@
 | 
			
		||||
            iconDescription="fas-archive" (action)="removeFiles($event)" iconAction="fas-trash" [closeOnClick]="false">
 | 
			
		||||
        </core-context-menu-item>
 | 
			
		||||
    </core-context-menu>
 | 
			
		||||
 | 
			
		||||
    <ion-button fill="clear" (click)="openModuleSummary()" aria-haspopup="true" [attr.aria-label]="'core.course.modulesummary' | translate">
 | 
			
		||||
        <ion-icon name="fas-info-circle" slot="icon-only" aria-hidden="true"></ion-icon>
 | 
			
		||||
    </ion-button>
 | 
			
		||||
</core-navbar-buttons>
 | 
			
		||||
 | 
			
		||||
<!-- Content. -->
 | 
			
		||||
 | 
			
		||||
@ -23,6 +23,10 @@
 | 
			
		||||
            iconDescription="fas-archive" (action)="removeFiles($event)" iconAction="fas-trash" [closeOnClick]="false">
 | 
			
		||||
        </core-context-menu-item>
 | 
			
		||||
    </core-context-menu>
 | 
			
		||||
 | 
			
		||||
    <ion-button fill="clear" (click)="openModuleSummary()" aria-haspopup="true" [attr.aria-label]="'core.course.modulesummary' | translate">
 | 
			
		||||
        <ion-icon name="fas-info-circle" slot="icon-only" aria-hidden="true"></ion-icon>
 | 
			
		||||
    </ion-button>
 | 
			
		||||
</core-navbar-buttons>
 | 
			
		||||
 | 
			
		||||
<!-- Content. -->
 | 
			
		||||
 | 
			
		||||
@ -15,6 +15,10 @@
 | 
			
		||||
            iconDescription="fas-archive" (action)="removeFiles($event)" iconAction="fas-trash" [closeOnClick]="false">
 | 
			
		||||
        </core-context-menu-item>
 | 
			
		||||
    </core-context-menu>
 | 
			
		||||
 | 
			
		||||
    <ion-button fill="clear" (click)="openModuleSummary()" aria-haspopup="true" [attr.aria-label]="'core.course.modulesummary' | translate">
 | 
			
		||||
        <ion-icon name="fas-info-circle" slot="icon-only" aria-hidden="true"></ion-icon>
 | 
			
		||||
    </ion-button>
 | 
			
		||||
</core-navbar-buttons>
 | 
			
		||||
 | 
			
		||||
<!-- Content. -->
 | 
			
		||||
 | 
			
		||||
@ -23,6 +23,10 @@
 | 
			
		||||
            iconDescription="fas-archive" (action)="removeFiles($event)" iconAction="fas-trash" [closeOnClick]="false">
 | 
			
		||||
        </core-context-menu-item>
 | 
			
		||||
    </core-context-menu>
 | 
			
		||||
 | 
			
		||||
    <ion-button fill="clear" (click)="openModuleSummary()" aria-haspopup="true" [attr.aria-label]="'core.course.modulesummary' | translate">
 | 
			
		||||
        <ion-icon name="fas-info-circle" slot="icon-only" aria-hidden="true"></ion-icon>
 | 
			
		||||
    </ion-button>
 | 
			
		||||
</core-navbar-buttons>
 | 
			
		||||
 | 
			
		||||
<!-- Content. -->
 | 
			
		||||
 | 
			
		||||
@ -24,6 +24,10 @@
 | 
			
		||||
            iconDescription="fas-archive" (action)="removeFiles($event)" iconAction="fas-trash" [closeOnClick]="false">
 | 
			
		||||
        </core-context-menu-item>
 | 
			
		||||
    </core-context-menu>
 | 
			
		||||
 | 
			
		||||
    <ion-button fill="clear" (click)="openModuleSummary()" aria-haspopup="true" [attr.aria-label]="'core.course.modulesummary' | translate">
 | 
			
		||||
        <ion-icon name="fas-info-circle" slot="icon-only" aria-hidden="true"></ion-icon>
 | 
			
		||||
    </ion-button>
 | 
			
		||||
</core-navbar-buttons>
 | 
			
		||||
 | 
			
		||||
<!-- Content. -->
 | 
			
		||||
 | 
			
		||||
@ -10,6 +10,10 @@
 | 
			
		||||
        <core-context-menu-item [priority]="700" [content]="'core.refresh' | translate" (action)="doRefresh(null, $event)"
 | 
			
		||||
            [iconAction]="refreshIcon" [closeOnClick]="false"></core-context-menu-item>
 | 
			
		||||
    </core-context-menu>
 | 
			
		||||
 | 
			
		||||
    <ion-button fill="clear" (click)="openModuleSummary()" aria-haspopup="true" [attr.aria-label]="'core.course.modulesummary' | translate">
 | 
			
		||||
        <ion-icon name="fas-info-circle" slot="icon-only" aria-hidden="true"></ion-icon>
 | 
			
		||||
    </ion-button>
 | 
			
		||||
</core-navbar-buttons>
 | 
			
		||||
 | 
			
		||||
<!-- Content. -->
 | 
			
		||||
 | 
			
		||||
@ -42,6 +42,10 @@
 | 
			
		||||
            iconDescription="fas-archive" (action)="removeFiles($event)" iconAction="fas-trash" [closeOnClick]="false">
 | 
			
		||||
        </core-context-menu-item>
 | 
			
		||||
    </core-context-menu>
 | 
			
		||||
 | 
			
		||||
    <ion-button fill="clear" (click)="openModuleSummary()" aria-haspopup="true" [attr.aria-label]="'core.course.modulesummary' | translate">
 | 
			
		||||
        <ion-icon name="fas-info-circle" slot="icon-only" aria-hidden="true"></ion-icon>
 | 
			
		||||
    </ion-button>
 | 
			
		||||
</core-navbar-buttons>
 | 
			
		||||
 | 
			
		||||
<!-- Content. -->
 | 
			
		||||
 | 
			
		||||
@ -24,6 +24,10 @@
 | 
			
		||||
            iconDescription="fas-archive" (action)="removeFiles($event)" iconAction="fas-trash" [closeOnClick]="false">
 | 
			
		||||
        </core-context-menu-item>
 | 
			
		||||
    </core-context-menu>
 | 
			
		||||
 | 
			
		||||
    <ion-button fill="clear" (click)="openModuleSummary()" aria-haspopup="true" [attr.aria-label]="'core.course.modulesummary' | translate">
 | 
			
		||||
        <ion-icon name="fas-info-circle" slot="icon-only" aria-hidden="true"></ion-icon>
 | 
			
		||||
    </ion-button>
 | 
			
		||||
</core-navbar-buttons>
 | 
			
		||||
 | 
			
		||||
<!-- Content. -->
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,5 @@
 | 
			
		||||
<ion-header>
 | 
			
		||||
<ion-header class="no-title">
 | 
			
		||||
    <ion-toolbar>
 | 
			
		||||
        <ion-title>
 | 
			
		||||
            <h2>{{ 'core.block.blocks' | translate }}</h2>
 | 
			
		||||
        </ion-title>
 | 
			
		||||
        <ion-buttons slot="end">
 | 
			
		||||
            <ion-button fill="clear" (click)="closeModal()" [attr.aria-label]="'core.close' | translate">
 | 
			
		||||
                <ion-icon name="fas-times" slot="icon-only" aria-hidden=true></ion-icon>
 | 
			
		||||
@ -10,7 +7,7 @@
 | 
			
		||||
        </ion-buttons>
 | 
			
		||||
    </ion-toolbar>
 | 
			
		||||
</ion-header>
 | 
			
		||||
<ion-content>
 | 
			
		||||
<ion-content [fullscreen]="true">
 | 
			
		||||
    <ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="doRefresh($event.target)">
 | 
			
		||||
        <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
 | 
			
		||||
    </ion-refresher>
 | 
			
		||||
 | 
			
		||||
@ -42,7 +42,6 @@ export class CoreCourseModuleMainActivityComponent extends CoreCourseModuleMainR
 | 
			
		||||
 | 
			
		||||
    // Data for context menu.
 | 
			
		||||
    syncIcon?: string; // Sync icon.
 | 
			
		||||
    hasOffline?: boolean; // If it has offline data to be synced.
 | 
			
		||||
    isOnline?: boolean; // If the app is online or not.
 | 
			
		||||
 | 
			
		||||
    protected syncObserver?: CoreEventObserver; // It will observe the sync auto event.
 | 
			
		||||
 | 
			
		||||
@ -29,6 +29,7 @@ import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { Translate } from '@singletons';
 | 
			
		||||
import { CoreEventObserver, CoreEvents } from '@singletons/events';
 | 
			
		||||
import { CoreLogger } from '@singletons/logger';
 | 
			
		||||
import { CoreCourseModuleSummaryComponent, CoreCourseModuleSummaryResult } from '../components/module-summary/module-summary';
 | 
			
		||||
import { CoreCourseContentsPage } from '../pages/contents/contents';
 | 
			
		||||
import { CoreCourse } from '../services/course';
 | 
			
		||||
import { CoreCourseHelper, CoreCourseModuleData } from '../services/course-helper';
 | 
			
		||||
@ -58,6 +59,7 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy,
 | 
			
		||||
    loaded = false; // If the component has been loaded.
 | 
			
		||||
    component?: string; // Component name.
 | 
			
		||||
    componentId?: number; // Component ID.
 | 
			
		||||
    hasOffline = false; // Resources don't have any data to sync.
 | 
			
		||||
    blog?: boolean; // If blog is available.
 | 
			
		||||
 | 
			
		||||
    // Data for context menu.
 | 
			
		||||
@ -253,16 +255,11 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Expand the description.
 | 
			
		||||
     *
 | 
			
		||||
     * @deprecated Use openModuleSummary instead.
 | 
			
		||||
     */
 | 
			
		||||
    expandDescription(): void {
 | 
			
		||||
        CoreTextUtils.viewText(Translate.instant('core.description'), this.description!, {
 | 
			
		||||
            component: this.component,
 | 
			
		||||
            componentId: this.module.id,
 | 
			
		||||
            filter: true,
 | 
			
		||||
            contextLevel: 'module',
 | 
			
		||||
            instanceId: this.module.id,
 | 
			
		||||
            courseId: this.courseId,
 | 
			
		||||
        });
 | 
			
		||||
        this.openModuleSummary();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -449,6 +446,48 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy,
 | 
			
		||||
        this.module = module;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Opens a module summary page.
 | 
			
		||||
     */
 | 
			
		||||
    async openModuleSummary(): Promise<void> {
 | 
			
		||||
        if (!this.module) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const data = await CoreDomUtils.openSideModal<CoreCourseModuleSummaryResult>({
 | 
			
		||||
            component: CoreCourseModuleSummaryComponent,
 | 
			
		||||
            componentProps: {
 | 
			
		||||
                moduleId: this.module.id,
 | 
			
		||||
                module: this.module,
 | 
			
		||||
                description: this.description,
 | 
			
		||||
                component: this.component,
 | 
			
		||||
                courseId: this.courseId,
 | 
			
		||||
                hasOffline: this.hasOffline,
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        if (data) {
 | 
			
		||||
            if (data.action == 'refresh') {
 | 
			
		||||
                const modal = await CoreDomUtils.showModalLoading();
 | 
			
		||||
 | 
			
		||||
                try {
 | 
			
		||||
                    await this.doRefresh();
 | 
			
		||||
                } finally {
 | 
			
		||||
                    modal.dismiss();
 | 
			
		||||
                }
 | 
			
		||||
            } else if(data.action == 'sync') {
 | 
			
		||||
                const modal = await CoreDomUtils.showModalLoading();
 | 
			
		||||
 | 
			
		||||
                try {
 | 
			
		||||
                    await this.doRefresh( undefined, undefined, true);
 | 
			
		||||
                } finally {
 | 
			
		||||
                    modal.dismiss();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Component being destroyed.
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
@ -27,6 +27,7 @@ import { CoreCourseModuleCompletionLegacyComponent } from './module-completion-l
 | 
			
		||||
import { CoreCourseModuleInfoComponent } from './module-info/module-info';
 | 
			
		||||
import { CoreCourseModuleManualCompletionComponent } from './module-manual-completion/module-manual-completion';
 | 
			
		||||
import { CoreCourseModuleNavigationComponent } from './module-navigation/module-navigation';
 | 
			
		||||
import { CoreCourseModuleSummaryComponent } from './module-summary/module-summary';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    declarations: [
 | 
			
		||||
@ -41,6 +42,7 @@ import { CoreCourseModuleNavigationComponent } from './module-navigation/module-
 | 
			
		||||
        CoreCourseTagAreaComponent,
 | 
			
		||||
        CoreCourseUnsupportedModuleComponent,
 | 
			
		||||
        CoreCourseModuleNavigationComponent,
 | 
			
		||||
        CoreCourseModuleSummaryComponent,
 | 
			
		||||
    ],
 | 
			
		||||
    imports: [
 | 
			
		||||
        CoreBlockComponentsModule,
 | 
			
		||||
@ -58,6 +60,7 @@ import { CoreCourseModuleNavigationComponent } from './module-navigation/module-
 | 
			
		||||
        CoreCourseTagAreaComponent,
 | 
			
		||||
        CoreCourseUnsupportedModuleComponent,
 | 
			
		||||
        CoreCourseModuleNavigationComponent,
 | 
			
		||||
        CoreCourseModuleSummaryComponent,
 | 
			
		||||
    ],
 | 
			
		||||
})
 | 
			
		||||
export class CoreCourseComponentsModule {}
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,82 @@
 | 
			
		||||
<ion-header class="no-title">
 | 
			
		||||
    <ion-toolbar>
 | 
			
		||||
        <ion-buttons slot="end">
 | 
			
		||||
            <ion-button fill="clear" (click)="closeModal()" [attr.aria-label]="'core.close' | translate">
 | 
			
		||||
                <ion-icon slot="icon-only" name="fas-times" aria-hidden="true"></ion-icon>
 | 
			
		||||
            </ion-button>
 | 
			
		||||
        </ion-buttons>
 | 
			
		||||
    </ion-toolbar>
 | 
			
		||||
</ion-header>
 | 
			
		||||
<ion-content [fullscreen]="true">
 | 
			
		||||
    <!-- Content. -->
 | 
			
		||||
    <core-loading [hideUntil]="loaded">
 | 
			
		||||
        <!-- Activity info. -->
 | 
			
		||||
        <ion-item class="ion-text-wrap" *ngIf="module">
 | 
			
		||||
            <ion-label>
 | 
			
		||||
                <h1>
 | 
			
		||||
                    <core-format-text [text]="module.name" contextLevel="module" [component]="component" [componentId]="componentId"
 | 
			
		||||
                        [contextInstanceId]="module.id" [courseId]="courseId">
 | 
			
		||||
                    </core-format-text>
 | 
			
		||||
                </h1>
 | 
			
		||||
            </ion-label>
 | 
			
		||||
            <ion-button fill="clear" [href]="externalUrl" core-link [showBrowserWarning]="false" color="dark"
 | 
			
		||||
                [attr.aria-label]="'core.openinbrowser' | translate" slot="end">
 | 
			
		||||
                <ion-icon name="fas-external-link-alt" slot="icon-only" aria-hidden="true"></ion-icon>
 | 
			
		||||
            </ion-button>
 | 
			
		||||
        </ion-item>
 | 
			
		||||
        <ion-item class="ion-text-wrap" *ngIf="module && description">
 | 
			
		||||
            <ion-label>
 | 
			
		||||
                <core-format-text [text]="description" [component]="component" [componentId]="componentId" contextLevel="module"
 | 
			
		||||
                    [contextInstanceId]="module.id" [courseId]="courseId" [maxHeight]="120">
 | 
			
		||||
                </core-format-text>
 | 
			
		||||
            </ion-label>
 | 
			
		||||
        </ion-item>
 | 
			
		||||
 | 
			
		||||
        <ion-item button class="ion-margin" *ngIf="prefetchText" class="ion-text-wrap">
 | 
			
		||||
            <ion-label>
 | 
			
		||||
                <p class="item-heading ion-text-wrap">{{ prefetchText }}</p>
 | 
			
		||||
                <p *ngIf="downloadTimeReadable">{{ downloadTimeReadable }}</p>
 | 
			
		||||
            </ion-label>
 | 
			
		||||
            <ion-button *ngIf="prefetchStatusIcon && prefetchStatusIcon != 'spinner'" (click)="prefetch()" color="primary" fill="outline"
 | 
			
		||||
                [attr.aria-label]="'core.download' | translate" slot="end">
 | 
			
		||||
                <ion-icon [name]="prefetchStatusIcon" slot="icon-only" aria-hidden="true">
 | 
			
		||||
                </ion-icon>
 | 
			
		||||
            </ion-button>
 | 
			
		||||
            <ion-spinner *ngIf="prefetchStatusIcon == 'spinner'" slot="end" aria-hidden="true"></ion-spinner>
 | 
			
		||||
        </ion-item>
 | 
			
		||||
 | 
			
		||||
        <ion-item button class="ion-margin" *ngIf="sizeReadable" class="ion-text-wrap">
 | 
			
		||||
            <ion-label>
 | 
			
		||||
                <p class="item-heading ion-text-wrap">{{ 'addon.storagemanager.totalspaceusage' | translate }}</p>
 | 
			
		||||
                <ion-badge color="light">{{ sizeReadable | coreBytesToSize }}</ion-badge>
 | 
			
		||||
            </ion-label>
 | 
			
		||||
            <ion-button *ngIf="!removeFilesLoading && size > 0" (click)="removeFiles()" color="danger" fill="outline"
 | 
			
		||||
                [attr.aria-label]="'core.clearstoreddata' | translate:{$a: sizeReadable}" slot="end">
 | 
			
		||||
                <ion-icon name="fas-trash" slot="icon-only" aria-hidden="true"></ion-icon>
 | 
			
		||||
            </ion-button>
 | 
			
		||||
            <ion-spinner *ngIf="removeFilesLoading" slot="end" aria-hidden="true"></ion-spinner>
 | 
			
		||||
        </ion-item>
 | 
			
		||||
 | 
			
		||||
        <ion-button *ngIf="blog" class="ion-margin" (click)="gotoBlog()" expand="block" fill="outline">
 | 
			
		||||
            <ion-icon name="far-newspaper" slot="start" aria-hidden="true"></ion-icon>
 | 
			
		||||
            <ion-label>
 | 
			
		||||
                {{ 'addon.blog.blog' | translate }}
 | 
			
		||||
            </ion-label>
 | 
			
		||||
        </ion-button>
 | 
			
		||||
    </core-loading>
 | 
			
		||||
</ion-content>
 | 
			
		||||
<ion-footer *ngIf="loaded && isOnline">
 | 
			
		||||
    <ion-button class="ion-margin" *ngIf="!hasOffline" (click)="refresh()" expand="block">
 | 
			
		||||
        <ion-icon name="fas-redo-alt" slot="start" aria-hidden="true"></ion-icon>
 | 
			
		||||
        <ion-label>
 | 
			
		||||
            {{ 'core.refresh' | translate }}
 | 
			
		||||
        </ion-label>
 | 
			
		||||
    </ion-button>
 | 
			
		||||
 | 
			
		||||
    <ion-button class="ion-margin" *ngIf="hasOffline" (click)="sync()" expand="block">
 | 
			
		||||
        <ion-icon name="fas-sync-alt" slot="start" aria-hidden="true"></ion-icon>
 | 
			
		||||
        <ion-label>
 | 
			
		||||
            {{ 'core.settings.synchronizenow' | translate }}
 | 
			
		||||
        </ion-label>
 | 
			
		||||
    </ion-button>
 | 
			
		||||
</ion-footer>
 | 
			
		||||
@ -0,0 +1,8 @@
 | 
			
		||||
 | 
			
		||||
:host ::ng-deep .collapsible-title ion-label {
 | 
			
		||||
    margin-top: 12px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
h1 {
 | 
			
		||||
    font-size: 20px;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,311 @@
 | 
			
		||||
// (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 { AddonBlog } from '@addons/blog/services/blog';
 | 
			
		||||
import { AddonBlogMainMenuHandlerService } from '@addons/blog/services/handlers/mainmenu';
 | 
			
		||||
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
 | 
			
		||||
import { Params } from '@angular/router';
 | 
			
		||||
import { CoreCourseHelper, CoreCourseModuleData } from '@features/course/services/course-helper';
 | 
			
		||||
import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate';
 | 
			
		||||
import { CoreApp } from '@services/app';
 | 
			
		||||
import { CoreFilepool } from '@services/filepool';
 | 
			
		||||
import { CoreNavigator } from '@services/navigator';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreDomUtils } from '@services/utils/dom';
 | 
			
		||||
import { CoreTextUtils } from '@services/utils/text';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { ModalController, Network, Translate, NgZone } from '@singletons';
 | 
			
		||||
import { CoreEventObserver, CoreEvents } from '@singletons/events';
 | 
			
		||||
import { Subscription } from 'rxjs';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component to display a module summary modal.
 | 
			
		||||
 */
 | 
			
		||||
@Component({
 | 
			
		||||
    selector: 'core-course-module-summary',
 | 
			
		||||
    templateUrl: 'module-summary.html',
 | 
			
		||||
    styleUrls: ['module-summary.scss'],
 | 
			
		||||
})
 | 
			
		||||
export class CoreCourseModuleSummaryComponent implements OnInit, OnDestroy {
 | 
			
		||||
 | 
			
		||||
    @Input() module?: CoreCourseModuleData; // The module of the component.
 | 
			
		||||
    @Input() courseId = 0; // Course ID the component belongs to.
 | 
			
		||||
    @Input() moduleId = 0; // Module ID the component belongs to.
 | 
			
		||||
    @Input() component = ''; // Component name.
 | 
			
		||||
    @Input() description = ''; // Module description.
 | 
			
		||||
    @Input() hasOffline = false; // If it has offline data to be synced.
 | 
			
		||||
 | 
			
		||||
    loaded = false; // If the component has been loaded.
 | 
			
		||||
    componentId?: number; // Component ID.
 | 
			
		||||
 | 
			
		||||
    // Data for context menu.
 | 
			
		||||
    externalUrl?: string; // External URL to open in browser.
 | 
			
		||||
 | 
			
		||||
    removeFilesLoading = false;
 | 
			
		||||
    prefetchStatusIcon?: string;
 | 
			
		||||
    prefetchStatus?: string;
 | 
			
		||||
    prefetchText?: string;
 | 
			
		||||
    sizeReadable?: string;
 | 
			
		||||
    downloadTimeReadable?: string; // Last download time in a readable format.
 | 
			
		||||
    size = 0;
 | 
			
		||||
 | 
			
		||||
    blog = false; // If blog is available.
 | 
			
		||||
 | 
			
		||||
    isOnline = false; // If the app is online or not.
 | 
			
		||||
 | 
			
		||||
    protected onlineSubscription: Subscription; // It will observe the status of the network connection.
 | 
			
		||||
 | 
			
		||||
    protected packageStatusObserver?: CoreEventObserver; // Observer of package status.
 | 
			
		||||
    protected fileStatusObserver?: CoreEventObserver; // Observer of file status.
 | 
			
		||||
    protected siteId: string;
 | 
			
		||||
    protected isDestroyed = false;
 | 
			
		||||
 | 
			
		||||
    constructor() {
 | 
			
		||||
        this.siteId = CoreSites.getCurrentSiteId();
 | 
			
		||||
        this.isOnline = CoreApp.isOnline();
 | 
			
		||||
 | 
			
		||||
        // Refresh online status when changes.
 | 
			
		||||
        this.onlineSubscription = Network.onChange().subscribe(() => {
 | 
			
		||||
            // Execute the callback in the Angular zone, so change detection doesn't stop working.
 | 
			
		||||
            NgZone.run(() => {
 | 
			
		||||
                this.isOnline = CoreApp.isOnline();
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async ngOnInit(): Promise<void> {
 | 
			
		||||
        if (!this.module) {
 | 
			
		||||
            this.closeModal();
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.fetchContent();
 | 
			
		||||
 | 
			
		||||
        if (this.component) {
 | 
			
		||||
            this.packageStatusObserver = CoreEvents.on(
 | 
			
		||||
                CoreEvents.PACKAGE_STATUS_CHANGED,
 | 
			
		||||
                (data) => {
 | 
			
		||||
                    if (data.componentId == module.id && data.component == this.component) {
 | 
			
		||||
                        this.getPackageStatus();
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                this.siteId,
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            // Debounce the update size function to prevent too many calls when downloading or deleting a whole activity.
 | 
			
		||||
            const debouncedUpdateSize = CoreUtils.debounce(async () => {
 | 
			
		||||
                if (!this.module) {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                const moduleSize = await CoreCourseModulePrefetchDelegate.getModuleStoredSize(this.module, this.courseId);
 | 
			
		||||
 | 
			
		||||
                this.sizeReadable = moduleSize > 0 ? CoreTextUtils.bytesToSize(moduleSize, 2) : '';
 | 
			
		||||
            }, 1000);
 | 
			
		||||
 | 
			
		||||
            this.fileStatusObserver = CoreEvents.on(
 | 
			
		||||
                CoreEvents.COMPONENT_FILE_ACTION,
 | 
			
		||||
                (data) => {
 | 
			
		||||
                    if (data.component != this.component || data.componentId != module.id) {
 | 
			
		||||
                        // The event doesn't belong to this component, ignore.
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (!CoreFilepool.isFileEventDownloadedOrDeleted(data)) {
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    // Update the module size.
 | 
			
		||||
                    debouncedUpdateSize();
 | 
			
		||||
                },
 | 
			
		||||
                this.siteId,
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Fetch content to populate the page.
 | 
			
		||||
     */
 | 
			
		||||
    protected async fetchContent(): Promise<void> {
 | 
			
		||||
        if (!this.module) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.componentId = this.module.id;
 | 
			
		||||
        this.externalUrl = this.module.url;
 | 
			
		||||
        this.courseId = this.courseId || this.module.course;
 | 
			
		||||
 | 
			
		||||
        this.blog = await AddonBlog.isPluginEnabled();
 | 
			
		||||
 | 
			
		||||
        await this.getPackageStatus();
 | 
			
		||||
 | 
			
		||||
        this.loaded = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Updage package status.
 | 
			
		||||
     *
 | 
			
		||||
     * @param refresh If prefetch info has to be refreshed.
 | 
			
		||||
     */
 | 
			
		||||
    async getPackageStatus(refresh = false): Promise<void> {
 | 
			
		||||
        if (!this.module) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const moduleInfo =
 | 
			
		||||
            await CoreCourseHelper.getModulePrefetchInfo(this.module, this.courseId, refresh, this.component);
 | 
			
		||||
 | 
			
		||||
        this.prefetchStatusIcon = moduleInfo.statusIcon;
 | 
			
		||||
        this.prefetchStatus = moduleInfo.status;
 | 
			
		||||
        this.downloadTimeReadable = '';
 | 
			
		||||
 | 
			
		||||
        if (moduleInfo.status != CoreConstants.NOT_DOWNLOADABLE) {
 | 
			
		||||
            // Module is downloadable, get the text to display to prefetch.
 | 
			
		||||
            if (moduleInfo.downloadTime && moduleInfo.downloadTime > 0) {
 | 
			
		||||
                this.prefetchText = Translate.instant('core.lastdownloaded');
 | 
			
		||||
                this.downloadTimeReadable = CoreTextUtils.ucFirst(moduleInfo.downloadTimeReadable);
 | 
			
		||||
            } else {
 | 
			
		||||
                // Module not downloaded, show a default text.
 | 
			
		||||
                this.prefetchText = Translate.instant('core.download');
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.sizeReadable = moduleInfo.sizeReadable;
 | 
			
		||||
        this.size = moduleInfo.size;
 | 
			
		||||
        if (moduleInfo.status == CoreConstants.DOWNLOADING) {
 | 
			
		||||
            // Set this to empty to prevent "remove file" option showing up while downloading.
 | 
			
		||||
            this.sizeReadable = '';
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Go to blog posts.
 | 
			
		||||
     */
 | 
			
		||||
    async gotoBlog(): Promise<void> {
 | 
			
		||||
        const params: Params = { cmId: this.moduleId };
 | 
			
		||||
 | 
			
		||||
        await CoreNavigator.navigateToSitePath(AddonBlogMainMenuHandlerService.PAGE_NAME, { params });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Prefetch the module.
 | 
			
		||||
     */
 | 
			
		||||
    async prefetch(): Promise<void> {
 | 
			
		||||
        if (!this.module) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const initialIcon = this.prefetchStatusIcon;
 | 
			
		||||
        this.prefetchStatusIcon = CoreConstants.ICON_DOWNLOADING; // Show spinner since this operation might take a while.
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            // We need to call getDownloadSize, the package might have been updated.
 | 
			
		||||
            const size = await CoreCourseModulePrefetchDelegate.getModuleDownloadSize(this.module, this.courseId, true);
 | 
			
		||||
 | 
			
		||||
            await CoreDomUtils.confirmDownloadSize(size);
 | 
			
		||||
 | 
			
		||||
            await CoreCourseModulePrefetchDelegate.prefetchModule(this.module, this.courseId, true);
 | 
			
		||||
 | 
			
		||||
            await this.getPackageStatus(true);
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            this.prefetchStatusIcon = initialIcon;
 | 
			
		||||
 | 
			
		||||
            if (!this.isDestroyed) {
 | 
			
		||||
                CoreDomUtils.showErrorModalDefault(error, 'core.errordownloading', true);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Confirm and remove downloaded files.
 | 
			
		||||
     */
 | 
			
		||||
    async removeFiles(): Promise<void> {
 | 
			
		||||
        if (!this.module) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this.prefetchStatus == CoreConstants.DOWNLOADING) {
 | 
			
		||||
            CoreDomUtils.showAlertTranslated(undefined, 'core.course.cannotdeletewhiledownloading');
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            await CoreDomUtils.showDeleteConfirm('addon.storagemanager.confirmdeletedatafrom', { name: this.module.name });
 | 
			
		||||
 | 
			
		||||
            this.removeFilesLoading = true;
 | 
			
		||||
 | 
			
		||||
            await CoreCourseHelper.removeModuleStoredData(this.module, this.courseId);
 | 
			
		||||
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            if (!this.isDestroyed &&error) {
 | 
			
		||||
                CoreDomUtils.showErrorModal(error);
 | 
			
		||||
            }
 | 
			
		||||
        } finally {
 | 
			
		||||
            this.removeFilesLoading = false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await this.getPackageStatus();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Refresh the data.
 | 
			
		||||
     */
 | 
			
		||||
    async refresh(): Promise<void> {
 | 
			
		||||
        if (!this.module) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ModalController.dismiss({ action: 'refresh' });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Sync the data.
 | 
			
		||||
     */
 | 
			
		||||
    async sync(): Promise<void> {
 | 
			
		||||
        if (!this.module) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ModalController.dismiss({ action: 'sync' });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Close the modal.
 | 
			
		||||
     */
 | 
			
		||||
    closeModal(): void {
 | 
			
		||||
        ModalController.dismiss();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    ngOnDestroy(): void {
 | 
			
		||||
        this.isDestroyed = true;
 | 
			
		||||
        this.packageStatusObserver?.off();
 | 
			
		||||
        this.fileStatusObserver?.off();
 | 
			
		||||
        this.onlineSubscription.unsubscribe();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type CoreCourseModuleSummaryResult =  {
 | 
			
		||||
    action: 'sync'|'refresh';
 | 
			
		||||
};
 | 
			
		||||
@ -297,6 +297,14 @@ button,
 | 
			
		||||
 | 
			
		||||
ion-button {
 | 
			
		||||
    margin: 4px 8px;
 | 
			
		||||
 | 
			
		||||
    ion-spinner[slot=start] {
 | 
			
		||||
        @include margin-horizontal(-0.3em, 0.3em);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ion-spinner[slot=end] {
 | 
			
		||||
        @include margin-horizontal(-0.3em, 0.3em);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ion-button.button-outline {
 | 
			
		||||
@ -1465,6 +1473,16 @@ ion-grid.core-no-grid > ion-row {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ion-header.no-title {
 | 
			
		||||
    --core-header-toolbar-border-width: 0;
 | 
			
		||||
    --core-header-toolbar-background: transparent;
 | 
			
		||||
 | 
			
		||||
    ion-toolbar .button.button-clear,
 | 
			
		||||
    ion-toolbar .button.button-solid {
 | 
			
		||||
        --background: var(--ion-background-color);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ion-header[collapsible] {
 | 
			
		||||
    @include core-transition(all, 500ms);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user