From 279071634b14f80c960f3898be67d2ac02d2ae6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Mon, 14 Feb 2022 10:54:02 +0100 Subject: [PATCH 01/12] MOBILE-3931 course: Change format component folder name to course-format --- src/core/features/course/components/components.module.ts | 2 +- .../course-format.html} | 0 .../{format/format.scss => course-format/course-format.scss} | 0 .../{format/format.ts => course-format/course-format.ts} | 4 ++-- src/core/features/course/pages/contents/contents.ts | 2 +- .../siteplugins/components/course-format/course-format.ts | 2 +- 6 files changed, 5 insertions(+), 5 deletions(-) rename src/core/features/course/components/{format/core-course-format.html => course-format/course-format.html} (100%) rename src/core/features/course/components/{format/format.scss => course-format/course-format.scss} (100%) rename src/core/features/course/components/{format/format.ts => course-format/course-format.ts} (99%) diff --git a/src/core/features/course/components/components.module.ts b/src/core/features/course/components/components.module.ts index 739dc216e..53f7d8563 100644 --- a/src/core/features/course/components/components.module.ts +++ b/src/core/features/course/components/components.module.ts @@ -16,7 +16,7 @@ import { NgModule } from '@angular/core'; import { CoreSharedModule } from '@/core/shared.module'; import { CoreBlockComponentsModule } from '@features/block/components/components.module'; -import { CoreCourseFormatComponent } from './format/format'; +import { CoreCourseFormatComponent } from './course-format/course-format'; import { CoreCourseModuleComponent } from './module/module'; import { CoreCourseModuleCompletionComponent } from './module-completion/module-completion'; import { CoreCourseModuleDescriptionComponent } from './module-description/module-description'; diff --git a/src/core/features/course/components/format/core-course-format.html b/src/core/features/course/components/course-format/course-format.html similarity index 100% rename from src/core/features/course/components/format/core-course-format.html rename to src/core/features/course/components/course-format/course-format.html diff --git a/src/core/features/course/components/format/format.scss b/src/core/features/course/components/course-format/course-format.scss similarity index 100% rename from src/core/features/course/components/format/format.scss rename to src/core/features/course/components/course-format/course-format.scss diff --git a/src/core/features/course/components/format/format.ts b/src/core/features/course/components/course-format/course-format.ts similarity index 99% rename from src/core/features/course/components/format/format.ts rename to src/core/features/course/components/course-format/course-format.ts index 2d5067cdc..5b6797f98 100644 --- a/src/core/features/course/components/format/format.ts +++ b/src/core/features/course/components/course-format/course-format.ts @@ -55,8 +55,8 @@ import { CoreCourseModuleDelegate } from '@features/course/services/module-deleg */ @Component({ selector: 'core-course-format', - templateUrl: 'core-course-format.html', - styleUrls: ['format.scss'], + templateUrl: 'course-format.html', + styleUrls: ['course-format.scss'], }) export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { diff --git a/src/core/features/course/pages/contents/contents.ts b/src/core/features/course/pages/contents/contents.ts index 77d752743..85d7bfce1 100644 --- a/src/core/features/course/pages/contents/contents.ts +++ b/src/core/features/course/pages/contents/contents.ts @@ -31,7 +31,7 @@ import { CoreCourseFormatDelegate } from '@features/course/services/format-deleg import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate'; import { CoreCourseOptionsMenuHandlerToDisplay } from '@features/course/services/course-options-delegate'; import { CoreCourseSync, CoreCourseSyncProvider } from '@features/course/services/sync'; -import { CoreCourseFormatComponent } from '../../components/format/format'; +import { CoreCourseFormatComponent } from '../../components/course-format/course-format'; import { CoreEvents, CoreEventObserver, diff --git a/src/core/features/siteplugins/components/course-format/course-format.ts b/src/core/features/siteplugins/components/course-format/course-format.ts index d2bc5c401..45eaf0266 100644 --- a/src/core/features/siteplugins/components/course-format/course-format.ts +++ b/src/core/features/siteplugins/components/course-format/course-format.ts @@ -15,7 +15,7 @@ import { Component, OnChanges, Input, ViewChild, Output, EventEmitter } from '@angular/core'; import { IonRefresher } from '@ionic/angular'; -import { CoreCourseFormatComponent } from '@features/course/components/format/format'; +import { CoreCourseFormatComponent } from '@features/course/components/course-format/course-format'; import { CoreCourseModuleCompletionData, CoreCourseSection } from '@features/course/services/course-helper'; import { CoreCourseFormatDelegate } from '@features/course/services/format-delegate'; import { CoreCourseAnyCourseData } from '@features/courses/services/courses'; From d224876f427c778130e4c954ea46669219f6e275 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Thu, 17 Feb 2022 15:55:10 +0100 Subject: [PATCH 02/12] MOBILE-3931 module: Add a new module summary page --- .../index/addon-mod-assign-index.html | 4 + .../components/index/index.html | 4 + .../index/addon-mod-book-index.html | 4 + .../index/addon-mod-chat-index.html | 4 + .../index/addon-mod-choice-index.html | 4 + .../index/addon-mod-data-index.html | 4 + .../index/addon-mod-feedback-index.html | 4 + .../index/addon-mod-folder-index.html | 4 + .../mod/forum/components/index/index.html | 4 + .../index/addon-mod-glossary-index.html | 4 + .../index/addon-mod-h5pactivity-index.html | 4 + .../index/addon-mod-imscp-index.html | 4 + .../index/addon-mod-lesson-index.html | 4 + .../components/index/addon-mod-lti-index.html | 4 + .../index/addon-mod-page-index.html | 4 + .../index/addon-mod-quiz-index.html | 4 + .../index/addon-mod-resource-index.html | 4 + .../index/addon-mod-scorm-index.html | 4 + .../index/addon-mod-survey-index.html | 4 + .../components/index/addon-mod-url-index.html | 4 + .../index/addon-mod-wiki-index.html | 4 + .../index/addon-mod-workshop-index.html | 4 + .../components/side-blocks/side-blocks.html | 7 +- .../course/classes/main-activity-component.ts | 1 - .../course/classes/main-resource-component.ts | 55 +++- .../course/components/components.module.ts | 3 + .../module-summary/module-summary.html | 82 +++++ .../module-summary/module-summary.scss | 8 + .../module-summary/module-summary.ts | 311 ++++++++++++++++++ src/theme/theme.base.scss | 18 + 30 files changed, 559 insertions(+), 14 deletions(-) create mode 100644 src/core/features/course/components/module-summary/module-summary.html create mode 100644 src/core/features/course/components/module-summary/module-summary.scss create mode 100644 src/core/features/course/components/module-summary/module-summary.ts diff --git a/src/addons/mod/assign/components/index/addon-mod-assign-index.html b/src/addons/mod/assign/components/index/addon-mod-assign-index.html index de9b9a462..73d0903e9 100644 --- a/src/addons/mod/assign/components/index/addon-mod-assign-index.html +++ b/src/addons/mod/assign/components/index/addon-mod-assign-index.html @@ -24,6 +24,10 @@ iconDescription="fas-archive" (action)="removeFiles($event)" iconAction="fas-trash" [closeOnClick]="false"> + + + + diff --git a/src/addons/mod/bigbluebuttonbn/components/index/index.html b/src/addons/mod/bigbluebuttonbn/components/index/index.html index 7b5f0aa7b..e10694728 100644 --- a/src/addons/mod/bigbluebuttonbn/components/index/index.html +++ b/src/addons/mod/bigbluebuttonbn/components/index/index.html @@ -17,6 +17,10 @@ [iconAction]="prefetchStatusIcon" [closeOnClick]="false"> + + + + diff --git a/src/addons/mod/book/components/index/addon-mod-book-index.html b/src/addons/mod/book/components/index/addon-mod-book-index.html index 0ebdcb21f..4e1084b7a 100644 --- a/src/addons/mod/book/components/index/addon-mod-book-index.html +++ b/src/addons/mod/book/components/index/addon-mod-book-index.html @@ -15,6 +15,10 @@ iconDescription="fas-archive" (action)="removeFiles($event)" iconAction="fas-trash" [closeOnClick]="false"> + + + + diff --git a/src/addons/mod/chat/components/index/addon-mod-chat-index.html b/src/addons/mod/chat/components/index/addon-mod-chat-index.html index d9106e2bf..5851b8e75 100644 --- a/src/addons/mod/chat/components/index/addon-mod-chat-index.html +++ b/src/addons/mod/chat/components/index/addon-mod-chat-index.html @@ -21,6 +21,10 @@ [iconAction]="prefetchStatusIcon" [closeOnClick]="false"> + + + + diff --git a/src/addons/mod/choice/components/index/addon-mod-choice-index.html b/src/addons/mod/choice/components/index/addon-mod-choice-index.html index 730623b79..4b32c2dbf 100644 --- a/src/addons/mod/choice/components/index/addon-mod-choice-index.html +++ b/src/addons/mod/choice/components/index/addon-mod-choice-index.html @@ -23,6 +23,10 @@ iconDescription="fas-archive" (action)="removeFiles($event)" iconAction="fas-trash" [closeOnClick]="false"> + + + + diff --git a/src/addons/mod/data/components/index/addon-mod-data-index.html b/src/addons/mod/data/components/index/addon-mod-data-index.html index c33a3a94a..b64adbce8 100644 --- a/src/addons/mod/data/components/index/addon-mod-data-index.html +++ b/src/addons/mod/data/components/index/addon-mod-data-index.html @@ -33,6 +33,10 @@ iconDescription="fas-archive" (action)="removeFiles($event)" iconAction="fas-trash" [closeOnClick]="false"> + + + + diff --git a/src/addons/mod/feedback/components/index/addon-mod-feedback-index.html b/src/addons/mod/feedback/components/index/addon-mod-feedback-index.html index f7bde4f52..bb3b4550e 100644 --- a/src/addons/mod/feedback/components/index/addon-mod-feedback-index.html +++ b/src/addons/mod/feedback/components/index/addon-mod-feedback-index.html @@ -23,6 +23,10 @@ iconDescription="fas-archive" (action)="removeFiles($event)" iconAction="fas-trash" [closeOnClick]="false"> + + + + diff --git a/src/addons/mod/folder/components/index/addon-mod-folder-index.html b/src/addons/mod/folder/components/index/addon-mod-folder-index.html index adceb5976..ea4d48cdf 100644 --- a/src/addons/mod/folder/components/index/addon-mod-folder-index.html +++ b/src/addons/mod/folder/components/index/addon-mod-folder-index.html @@ -20,6 +20,10 @@ iconDescription="fas-archive" (action)="removeFiles($event)" iconAction="fas-trash" [closeOnClick]="false"> + + + + diff --git a/src/addons/mod/forum/components/index/index.html b/src/addons/mod/forum/components/index/index.html index 11a307a79..3bd8b6ada 100644 --- a/src/addons/mod/forum/components/index/index.html +++ b/src/addons/mod/forum/components/index/index.html @@ -28,6 +28,10 @@ (action)="showSortOrderSelector()"> + + + + diff --git a/src/addons/mod/glossary/components/index/addon-mod-glossary-index.html b/src/addons/mod/glossary/components/index/addon-mod-glossary-index.html index a3437f70d..142f410eb 100644 --- a/src/addons/mod/glossary/components/index/addon-mod-glossary-index.html +++ b/src/addons/mod/glossary/components/index/addon-mod-glossary-index.html @@ -36,6 +36,10 @@ iconDescription="fas-archive" (action)="removeFiles($event)" iconAction="fas-trash" [closeOnClick]="false"> + + + + diff --git a/src/addons/mod/h5pactivity/components/index/addon-mod-h5pactivity-index.html b/src/addons/mod/h5pactivity/components/index/addon-mod-h5pactivity-index.html index 1338852c1..e7b6e7ea2 100644 --- a/src/addons/mod/h5pactivity/components/index/addon-mod-h5pactivity-index.html +++ b/src/addons/mod/h5pactivity/components/index/addon-mod-h5pactivity-index.html @@ -30,6 +30,10 @@ iconDescription="fas-archive" (action)="removeFiles($event)" iconAction="fas-trash" [closeOnClick]="false"> + + + + diff --git a/src/addons/mod/imscp/components/index/addon-mod-imscp-index.html b/src/addons/mod/imscp/components/index/addon-mod-imscp-index.html index 7a7a036c2..b6856d0db 100644 --- a/src/addons/mod/imscp/components/index/addon-mod-imscp-index.html +++ b/src/addons/mod/imscp/components/index/addon-mod-imscp-index.html @@ -23,6 +23,10 @@ iconDescription="fas-archive" (action)="removeFiles($event)" iconAction="fas-trash" [closeOnClick]="false"> + + + + diff --git a/src/addons/mod/lesson/components/index/addon-mod-lesson-index.html b/src/addons/mod/lesson/components/index/addon-mod-lesson-index.html index 1dd4b2270..dd57d8860 100644 --- a/src/addons/mod/lesson/components/index/addon-mod-lesson-index.html +++ b/src/addons/mod/lesson/components/index/addon-mod-lesson-index.html @@ -23,6 +23,10 @@ iconDescription="fas-archive" (action)="removeFiles($event)" iconAction="fas-trash" [closeOnClick]="false"> + + + + diff --git a/src/addons/mod/lti/components/index/addon-mod-lti-index.html b/src/addons/mod/lti/components/index/addon-mod-lti-index.html index 9430ef52b..9dfc934b2 100644 --- a/src/addons/mod/lti/components/index/addon-mod-lti-index.html +++ b/src/addons/mod/lti/components/index/addon-mod-lti-index.html @@ -14,6 +14,10 @@ (action)="doRefresh(null, $event)" [iconAction]="refreshIcon" [closeOnClick]="false"> + + + + diff --git a/src/addons/mod/page/components/index/addon-mod-page-index.html b/src/addons/mod/page/components/index/addon-mod-page-index.html index 1c8cab9ed..bd5f3a6ba 100644 --- a/src/addons/mod/page/components/index/addon-mod-page-index.html +++ b/src/addons/mod/page/components/index/addon-mod-page-index.html @@ -20,6 +20,10 @@ iconDescription="fas-archive" (action)="removeFiles($event)" iconAction="fas-trash" [closeOnClick]="false"> + + + + diff --git a/src/addons/mod/quiz/components/index/addon-mod-quiz-index.html b/src/addons/mod/quiz/components/index/addon-mod-quiz-index.html index dffa76b76..8060c0171 100644 --- a/src/addons/mod/quiz/components/index/addon-mod-quiz-index.html +++ b/src/addons/mod/quiz/components/index/addon-mod-quiz-index.html @@ -23,6 +23,10 @@ iconDescription="fas-archive" (action)="removeFiles($event)" iconAction="fas-trash" [closeOnClick]="false"> + + + + diff --git a/src/addons/mod/resource/components/index/addon-mod-resource-index.html b/src/addons/mod/resource/components/index/addon-mod-resource-index.html index bbad8ec56..78357a63f 100644 --- a/src/addons/mod/resource/components/index/addon-mod-resource-index.html +++ b/src/addons/mod/resource/components/index/addon-mod-resource-index.html @@ -15,6 +15,10 @@ iconDescription="fas-archive" (action)="removeFiles($event)" iconAction="fas-trash" [closeOnClick]="false"> + + + + diff --git a/src/addons/mod/scorm/components/index/addon-mod-scorm-index.html b/src/addons/mod/scorm/components/index/addon-mod-scorm-index.html index aeb744521..e5eea8e77 100644 --- a/src/addons/mod/scorm/components/index/addon-mod-scorm-index.html +++ b/src/addons/mod/scorm/components/index/addon-mod-scorm-index.html @@ -23,6 +23,10 @@ iconDescription="fas-archive" (action)="removeFiles($event)" iconAction="fas-trash" [closeOnClick]="false"> + + + + diff --git a/src/addons/mod/survey/components/index/addon-mod-survey-index.html b/src/addons/mod/survey/components/index/addon-mod-survey-index.html index 99a46962c..8f73a5c47 100644 --- a/src/addons/mod/survey/components/index/addon-mod-survey-index.html +++ b/src/addons/mod/survey/components/index/addon-mod-survey-index.html @@ -24,6 +24,10 @@ iconDescription="fas-archive" (action)="removeFiles($event)" iconAction="fas-trash" [closeOnClick]="false"> + + + + diff --git a/src/addons/mod/url/components/index/addon-mod-url-index.html b/src/addons/mod/url/components/index/addon-mod-url-index.html index 24f4579bd..47d00e1af 100644 --- a/src/addons/mod/url/components/index/addon-mod-url-index.html +++ b/src/addons/mod/url/components/index/addon-mod-url-index.html @@ -10,6 +10,10 @@ + + + + diff --git a/src/addons/mod/wiki/components/index/addon-mod-wiki-index.html b/src/addons/mod/wiki/components/index/addon-mod-wiki-index.html index 7db05d66b..c689ec193 100644 --- a/src/addons/mod/wiki/components/index/addon-mod-wiki-index.html +++ b/src/addons/mod/wiki/components/index/addon-mod-wiki-index.html @@ -42,6 +42,10 @@ iconDescription="fas-archive" (action)="removeFiles($event)" iconAction="fas-trash" [closeOnClick]="false"> + + + + diff --git a/src/addons/mod/workshop/components/index/addon-mod-workshop-index.html b/src/addons/mod/workshop/components/index/addon-mod-workshop-index.html index a5a979a6f..ec5719b0d 100644 --- a/src/addons/mod/workshop/components/index/addon-mod-workshop-index.html +++ b/src/addons/mod/workshop/components/index/addon-mod-workshop-index.html @@ -24,6 +24,10 @@ iconDescription="fas-archive" (action)="removeFiles($event)" iconAction="fas-trash" [closeOnClick]="false"> + + + + diff --git a/src/core/features/block/components/side-blocks/side-blocks.html b/src/core/features/block/components/side-blocks/side-blocks.html index 198cb21d2..91fee4e1e 100644 --- a/src/core/features/block/components/side-blocks/side-blocks.html +++ b/src/core/features/block/components/side-blocks/side-blocks.html @@ -1,8 +1,5 @@ - + - -

{{ 'core.block.blocks' | translate }}

-
@@ -10,7 +7,7 @@
- + diff --git a/src/core/features/course/classes/main-activity-component.ts b/src/core/features/course/classes/main-activity-component.ts index 1c2621036..e89d53476 100644 --- a/src/core/features/course/classes/main-activity-component.ts +++ b/src/core/features/course/classes/main-activity-component.ts @@ -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. diff --git a/src/core/features/course/classes/main-resource-component.ts b/src/core/features/course/classes/main-resource-component.ts index 1cf1b4a0b..cc1f72c67 100644 --- a/src/core/features/course/classes/main-resource-component.ts +++ b/src/core/features/course/classes/main-resource-component.ts @@ -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 { + if (!this.module) { + return; + } + + const data = await CoreDomUtils.openSideModal({ + 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. */ diff --git a/src/core/features/course/components/components.module.ts b/src/core/features/course/components/components.module.ts index 53f7d8563..5eee268bf 100644 --- a/src/core/features/course/components/components.module.ts +++ b/src/core/features/course/components/components.module.ts @@ -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 {} diff --git a/src/core/features/course/components/module-summary/module-summary.html b/src/core/features/course/components/module-summary/module-summary.html new file mode 100644 index 000000000..26d20c8b6 --- /dev/null +++ b/src/core/features/course/components/module-summary/module-summary.html @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + +

+ + +

+
+ + + +
+ + + + + + + + + +

{{ prefetchText }}

+

{{ downloadTimeReadable }}

+
+ + + + +
+ + + +

{{ 'addon.storagemanager.totalspaceusage' | translate }}

+ {{ sizeReadable | coreBytesToSize }} +
+ + + + +
+ + + + + {{ 'addon.blog.blog' | translate }} + + +
+
+ + + + + {{ 'core.refresh' | translate }} + + + + + + + {{ 'core.settings.synchronizenow' | translate }} + + + diff --git a/src/core/features/course/components/module-summary/module-summary.scss b/src/core/features/course/components/module-summary/module-summary.scss new file mode 100644 index 000000000..fb9664764 --- /dev/null +++ b/src/core/features/course/components/module-summary/module-summary.scss @@ -0,0 +1,8 @@ + +:host ::ng-deep .collapsible-title ion-label { + margin-top: 12px; +} + +h1 { + font-size: 20px; +} diff --git a/src/core/features/course/components/module-summary/module-summary.ts b/src/core/features/course/components/module-summary/module-summary.ts new file mode 100644 index 000000000..58d159945 --- /dev/null +++ b/src/core/features/course/components/module-summary/module-summary.ts @@ -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 { + 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 { + 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 { + 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 { + const params: Params = { cmId: this.moduleId }; + + await CoreNavigator.navigateToSitePath(AddonBlogMainMenuHandlerService.PAGE_NAME, { params }); + } + + /** + * Prefetch the module. + */ + async prefetch(): Promise { + 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 { + 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 { + if (!this.module) { + return; + } + + ModalController.dismiss({ action: 'refresh' }); + } + + /** + * Sync the data. + */ + async sync(): Promise { + 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'; +}; diff --git a/src/theme/theme.base.scss b/src/theme/theme.base.scss index f11236c58..a2f2df66f 100644 --- a/src/theme/theme.base.scss +++ b/src/theme/theme.base.scss @@ -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); From 344ee6d57ecc65b7a521639512e3da8ffa1f7195 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Mon, 14 Feb 2022 17:07:46 +0100 Subject: [PATCH 03/12] MOBILE-3931 module: Remove unnecessary variables and menus from modules --- scripts/langindex.json | 1 - .../index/addon-mod-assign-index.html | 25 ---- .../mod/assign/components/index/index.ts | 136 +++++++----------- .../components/index/index.html | 18 --- .../bigbluebuttonbn/components/index/index.ts | 19 ++- .../index/addon-mod-book-index.html | 16 --- src/addons/mod/book/components/index/index.ts | 14 +- .../index/addon-mod-chat-index.html | 22 --- src/addons/mod/chat/components/index/index.ts | 32 ++--- .../index/addon-mod-choice-index.html | 24 ---- .../mod/choice/components/index/index.ts | 60 ++++---- .../index/addon-mod-data-index.html | 22 --- src/addons/mod/data/components/index/index.ts | 10 +- .../index/addon-mod-feedback-index.html | 24 ---- .../mod/feedback/components/index/index.ts | 5 +- .../index/addon-mod-folder-index.html | 21 --- .../mod/folder/components/index/index.ts | 22 +-- .../mod/forum/components/index/index.html | 23 --- .../mod/forum/components/index/index.ts | 10 +- src/addons/mod/forum/lang.json | 1 - .../index/addon-mod-glossary-index.html | 22 --- .../mod/glossary/components/index/index.ts | 54 ++++--- .../index/addon-mod-h5pactivity-index.html | 25 +--- .../mod/h5pactivity/components/index/index.ts | 81 +++++------ .../index/addon-mod-imscp-index.html | 20 --- .../mod/imscp/components/index/index.ts | 50 +++---- .../index/addon-mod-lesson-index.html | 24 ---- .../mod/lesson/components/index/index.ts | 115 +++++++-------- .../components/index/addon-mod-lti-index.html | 17 +-- src/addons/mod/lti/components/index/index.ts | 15 +- .../index/addon-mod-page-index.html | 21 --- src/addons/mod/page/components/index/index.ts | 29 ++-- .../index/addon-mod-quiz-index.html | 26 +--- src/addons/mod/quiz/components/index/index.ts | 135 ++++++++--------- .../index/addon-mod-resource-index.html | 21 +-- .../mod/resource/components/index/index.ts | 114 ++++++++------- .../index/addon-mod-scorm-index.html | 26 +--- .../mod/scorm/components/index/index.ts | 50 +++---- .../index/addon-mod-survey-index.html | 27 +--- .../mod/survey/components/index/index.ts | 45 +++--- .../components/index/addon-mod-url-index.html | 11 -- .../index/addon-mod-wiki-index.html | 22 --- src/addons/mod/wiki/components/index/index.ts | 23 ++- .../index/addon-mod-workshop-index.html | 25 ---- .../mod/workshop/components/index/index.ts | 82 +++++------ .../course/classes/main-activity-component.ts | 53 +------ .../course/classes/main-resource-component.ts | 125 ++++++---------- .../features/course/services/course-helper.ts | 2 + 48 files changed, 534 insertions(+), 1231 deletions(-) diff --git a/scripts/langindex.json b/scripts/langindex.json index 3bdd8c444..07000f48f 100644 --- a/scripts/langindex.json +++ b/scripts/langindex.json @@ -649,7 +649,6 @@ "addon.mod_forum.posttomygroups": "forum", "addon.mod_forum.privatereply": "forum", "addon.mod_forum.re": "forum", - "addon.mod_forum.refreshdiscussions": "local_moodlemobileapp", "addon.mod_forum.refreshposts": "local_moodlemobileapp", "addon.mod_forum.removefromfavourites": "forum", "addon.mod_forum.reply": "forum", diff --git a/src/addons/mod/assign/components/index/addon-mod-assign-index.html b/src/addons/mod/assign/components/index/addon-mod-assign-index.html index 73d0903e9..69b0d2d25 100644 --- a/src/addons/mod/assign/components/index/addon-mod-assign-index.html +++ b/src/addons/mod/assign/components/index/addon-mod-assign-index.html @@ -1,30 +1,5 @@ - - - - - - - - - - - - - - - - - diff --git a/src/addons/mod/assign/components/index/index.ts b/src/addons/mod/assign/components/index/index.ts index 28827e331..c78728728 100644 --- a/src/addons/mod/assign/components/index/index.ts +++ b/src/addons/mod/assign/components/index/index.ts @@ -23,7 +23,6 @@ import { CoreGroupInfo, CoreGroups } from '@services/groups'; import { CoreNavigator } from '@services/navigator'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreTextUtils } from '@services/utils/text'; import { CoreTimeUtils } from '@services/utils/time'; import { CoreUtils } from '@services/utils/utils'; import { Translate } from '@singletons'; @@ -161,104 +160,75 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo } /** - * Expand the description. + * @inheritdoc */ - expandDescription(ev?: Event): void { - ev?.preventDefault(); - ev?.stopPropagation(); - - if (this.assign && (this.description || this.assign.introattachments)) { - CoreTextUtils.viewText(Translate.instant('core.description'), this.description || '', { - component: this.component, - componentId: this.module.id, - files: this.assign.introattachments, - filter: true, - contextLevel: 'module', - instanceId: this.module.id, - courseId: this.courseId, - }); - } - } - - /** - * Get assignment data. - * - * @param refresh If it's refreshing content. - * @param sync If it should try to sync. - * @param showErrors If show errors to the user of hide them. - * @return Promise resolved when done. - */ - protected async fetchContent(refresh = false, sync = false, showErrors = false): Promise { + protected async fetchContent(refresh?: boolean, sync = false, showErrors = false): Promise { // Get assignment data. - try { - this.assign = await AddonModAssign.getAssignment(this.courseId, this.module.id); + this.assign = await AddonModAssign.getAssignment(this.courseId, this.module.id); - this.dataRetrieved.emit(this.assign); - this.description = this.assign.intro; + this.dataRetrieved.emit(this.assign); + this.description = this.assign.intro; - if (sync) { - // Try to synchronize the assign. - await CoreUtils.ignoreErrors(this.syncActivity(showErrors)); - } + if (sync) { + // Try to synchronize the assign. + await CoreUtils.ignoreErrors(this.syncActivity(showErrors)); + } - // Check if there's any offline data for this assign. - this.hasOffline = await AddonModAssignOffline.hasAssignOfflineData(this.assign.id); + // Check if there's any offline data for this assign. + this.hasOffline = await AddonModAssignOffline.hasAssignOfflineData(this.assign.id); - // Get assignment submissions. - const submissions = await AddonModAssign.getSubmissions(this.assign.id, { cmId: this.module.id }); - const time = CoreTimeUtils.timestamp(); + // Get assignment submissions. + const submissions = await AddonModAssign.getSubmissions(this.assign.id, { cmId: this.module.id }); + const time = CoreTimeUtils.timestamp(); - this.canViewAllSubmissions = submissions.canviewsubmissions; + this.canViewAllSubmissions = submissions.canviewsubmissions; - if (submissions.canviewsubmissions) { + if (submissions.canviewsubmissions) { - // Calculate the messages to display about time remaining and late submissions. - if (this.assign.duedate > 0) { - if (this.assign.duedate - time <= 0) { - this.timeRemaining = Translate.instant('addon.mod_assign.assignmentisdue'); - } else { - this.timeRemaining = CoreTimeUtils.formatDuration(this.assign.duedate - time, 3); - - if (this.assign.cutoffdate) { - if (this.assign.cutoffdate > time) { - this.lateSubmissions = Translate.instant( - 'addon.mod_assign.latesubmissionsaccepted', - { $a: CoreTimeUtils.userDate(this.assign.cutoffdate * 1000) }, - ); - } else { - this.lateSubmissions = Translate.instant('addon.mod_assign.nomoresubmissionsaccepted'); - } - } else { - this.lateSubmissions = ''; - } - } + // Calculate the messages to display about time remaining and late submissions. + if (this.assign.duedate > 0) { + if (this.assign.duedate - time <= 0) { + this.timeRemaining = Translate.instant('addon.mod_assign.assignmentisdue'); } else { - this.timeRemaining = ''; - this.lateSubmissions = ''; + this.timeRemaining = CoreTimeUtils.formatDuration(this.assign.duedate - time, 3); + + if (this.assign.cutoffdate) { + if (this.assign.cutoffdate > time) { + this.lateSubmissions = Translate.instant( + 'addon.mod_assign.latesubmissionsaccepted', + { $a: CoreTimeUtils.userDate(this.assign.cutoffdate * 1000) }, + ); + } else { + this.lateSubmissions = Translate.instant('addon.mod_assign.nomoresubmissionsaccepted'); + } + } else { + this.lateSubmissions = ''; + } } - - // Check if groupmode is enabled to avoid showing wrong numbers. - this.groupInfo = await CoreGroups.getActivityGroupInfo(this.assign.cmid, false); - - await this.setGroup(CoreGroups.validateGroupId(this.group, this.groupInfo)); - - return; + } else { + this.timeRemaining = ''; + this.lateSubmissions = ''; } - try { - // Check if the user can view their own submission. - await AddonModAssign.getSubmissionStatus(this.assign.id, { cmId: this.module.id }); - this.canViewOwnSubmission = true; - } catch (error) { - this.canViewOwnSubmission = false; + // Check if groupmode is enabled to avoid showing wrong numbers. + this.groupInfo = await CoreGroups.getActivityGroupInfo(this.assign.cmid, false); - if (error.errorcode !== 'nopermission') { - throw error; - } + await this.setGroup(CoreGroups.validateGroupId(this.group, this.groupInfo)); + + return; + } + + try { + // Check if the user can view their own submission. + await AddonModAssign.getSubmissionStatus(this.assign.id, { cmId: this.module.id }); + this.canViewOwnSubmission = true; + } catch (error) { + this.canViewOwnSubmission = false; + + if (error.errorcode !== 'nopermission') { + throw error; } - } finally { - this.fillContextMenu(refresh); } } diff --git a/src/addons/mod/bigbluebuttonbn/components/index/index.html b/src/addons/mod/bigbluebuttonbn/components/index/index.html index e10694728..e46bb4a56 100644 --- a/src/addons/mod/bigbluebuttonbn/components/index/index.html +++ b/src/addons/mod/bigbluebuttonbn/components/index/index.html @@ -1,23 +1,5 @@ - - - - - - - - - - - - - diff --git a/src/addons/mod/bigbluebuttonbn/components/index/index.ts b/src/addons/mod/bigbluebuttonbn/components/index/index.ts index 16ff70143..8d85abf94 100644 --- a/src/addons/mod/bigbluebuttonbn/components/index/index.ts +++ b/src/addons/mod/bigbluebuttonbn/components/index/index.ts @@ -71,21 +71,18 @@ export class AddonModBBBIndexComponent extends CoreCourseModuleMainActivityCompo /** * @inheritdoc */ - protected async fetchContent(refresh: boolean = false): Promise { - try { - this.bbb = await AddonModBBB.getBBB(this.courseId, this.module.id); + protected async fetchContent(): Promise { + this.bbb = await AddonModBBB.getBBB(this.courseId, this.module.id); - this.description = this.bbb.intro; - this.dataRetrieved.emit(this.bbb); + this.description = this.bbb.intro; + this.dataRetrieved.emit(this.bbb); - this.groupInfo = await CoreGroups.getActivityGroupInfo(this.module.id, false); + this.groupInfo = await CoreGroups.getActivityGroupInfo(this.module.id, false); - this.groupId = CoreGroups.validateGroupId(this.groupId, this.groupInfo); + this.groupId = CoreGroups.validateGroupId(this.groupId, this.groupInfo); + + await this.fetchMeetingInfo(); - await this.fetchMeetingInfo(); - } finally { - this.fillContextMenu(refresh); - } } /** diff --git a/src/addons/mod/book/components/index/addon-mod-book-index.html b/src/addons/mod/book/components/index/addon-mod-book-index.html index 4e1084b7a..c673defcf 100644 --- a/src/addons/mod/book/components/index/addon-mod-book-index.html +++ b/src/addons/mod/book/components/index/addon-mod-book-index.html @@ -1,21 +1,5 @@ - - - - - - - - - - diff --git a/src/addons/mod/book/components/index/index.ts b/src/addons/mod/book/components/index/index.ts index 4ef609091..b8d7f4bea 100644 --- a/src/addons/mod/book/components/index/index.ts +++ b/src/addons/mod/book/components/index/index.ts @@ -52,15 +52,11 @@ export class AddonModBookIndexComponent extends CoreCourseModuleMainResourceComp /** * @inheritdoc */ - protected async fetchContent(refresh?: boolean): Promise { - try { - await Promise.all([ - this.loadBook(), - this.loadTOC(), - ]); - } finally { - this.fillContextMenu(refresh); - } + protected async fetchContent(): Promise { + await Promise.all([ + this.loadBook(), + this.loadTOC(), + ]); } /** diff --git a/src/addons/mod/chat/components/index/addon-mod-chat-index.html b/src/addons/mod/chat/components/index/addon-mod-chat-index.html index 5851b8e75..43db1e541 100644 --- a/src/addons/mod/chat/components/index/addon-mod-chat-index.html +++ b/src/addons/mod/chat/components/index/addon-mod-chat-index.html @@ -1,27 +1,5 @@ - - - - - - - - - - - - - - - diff --git a/src/addons/mod/chat/components/index/index.ts b/src/addons/mod/chat/components/index/index.ts index fe6a7dd65..159f4e462 100644 --- a/src/addons/mod/chat/components/index/index.ts +++ b/src/addons/mod/chat/components/index/index.ts @@ -70,27 +70,23 @@ export class AddonModChatIndexComponent extends CoreCourseModuleMainActivityComp /** * @inheritdoc */ - protected async fetchContent(refresh: boolean = false): Promise { - try { - this.chat = await AddonModChat.getChat(this.courseId, this.module.id); + protected async fetchContent(): Promise { + this.chat = await AddonModChat.getChat(this.courseId, this.module.id); - this.description = this.chat.intro; - const now = CoreTimeUtils.timestamp(); - const span = (this.chat.chattime || 0) - now; + this.description = this.chat.intro; + const now = CoreTimeUtils.timestamp(); + const span = (this.chat.chattime || 0) - now; - if (this.chat.chattime && this.chat.schedule && span > 0) { - this.chatInfo = { - date: CoreTimeUtils.userDate(this.chat.chattime * 1000), - fromnow: CoreTimeUtils.formatTime(span), - }; - } else { - this.chatInfo = undefined; - } - - this.dataRetrieved.emit(this.chat); - } finally { - this.fillContextMenu(refresh); + if (this.chat.chattime && this.chat.schedule && span > 0) { + this.chatInfo = { + date: CoreTimeUtils.userDate(this.chat.chattime * 1000), + fromnow: CoreTimeUtils.formatTime(span), + }; + } else { + this.chatInfo = undefined; } + + this.dataRetrieved.emit(this.chat); } /** diff --git a/src/addons/mod/choice/components/index/addon-mod-choice-index.html b/src/addons/mod/choice/components/index/addon-mod-choice-index.html index 4b32c2dbf..15cdfbec4 100644 --- a/src/addons/mod/choice/components/index/addon-mod-choice-index.html +++ b/src/addons/mod/choice/components/index/addon-mod-choice-index.html @@ -1,29 +1,5 @@ - - - - - - - - - - - - - - - - - diff --git a/src/addons/mod/choice/components/index/index.ts b/src/addons/mod/choice/components/index/index.ts index 171ebf8fa..2597bde19 100644 --- a/src/addons/mod/choice/components/index/index.ts +++ b/src/addons/mod/choice/components/index/index.ts @@ -132,43 +132,39 @@ export class AddonModChoiceIndexComponent extends CoreCourseModuleMainActivityCo /** * @inheritdoc */ - protected async fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise { + protected async fetchContent(refresh?: boolean, sync = false, showErrors = false): Promise { this.now = Date.now(); - try { - this.choice = await AddonModChoice.getChoice(this.courseId, this.module.id); + this.choice = await AddonModChoice.getChoice(this.courseId, this.module.id); - if (sync) { - // Try to synchronize the choice. - const updated = await this.syncActivity(showErrors); + if (sync) { + // Try to synchronize the choice. + const updated = await this.syncActivity(showErrors); - if (updated) { - // Responses were sent, update the choice. - this.choice = await AddonModChoice.getChoice(this.courseId, this.module.id); - } + if (updated) { + // Responses were sent, update the choice. + this.choice = await AddonModChoice.getChoice(this.courseId, this.module.id); } - - this.choice.timeopen = (this.choice.timeopen || 0) * 1000; - this.choice.timeclose = (this.choice.timeclose || 0) * 1000; - this.openTimeReadable = CoreTimeUtils.userDate(this.choice.timeopen); - this.closeTimeReadable = CoreTimeUtils.userDate(this.choice.timeclose); - - this.description = this.choice.intro; - this.choiceNotOpenYet = !!this.choice.timeopen && this.choice.timeopen > this.now; - this.choiceClosed = !!this.choice.timeclose && this.choice.timeclose <= this.now; - - this.dataRetrieved.emit(this.choice); - - // Check if there are responses stored in offline. - this.hasOffline = await AddonModChoiceOffline.hasResponse(this.choice.id); - - // We need fetchOptions to finish before calling fetchResults because it needs hasAnsweredOnline variable. - await this.fetchOptions(this.choice); - - await this.fetchResults(this.choice); - } finally { - this.fillContextMenu(refresh); } + + this.choice.timeopen = (this.choice.timeopen || 0) * 1000; + this.choice.timeclose = (this.choice.timeclose || 0) * 1000; + this.openTimeReadable = CoreTimeUtils.userDate(this.choice.timeopen); + this.closeTimeReadable = CoreTimeUtils.userDate(this.choice.timeclose); + + this.description = this.choice.intro; + this.choiceNotOpenYet = !!this.choice.timeopen && this.choice.timeopen > this.now; + this.choiceClosed = !!this.choice.timeclose && this.choice.timeclose <= this.now; + + this.dataRetrieved.emit(this.choice); + + // Check if there are responses stored in offline. + this.hasOffline = await AddonModChoiceOffline.hasResponse(this.choice.id); + + // We need fetchOptions to finish before calling fetchResults because it needs hasAnsweredOnline variable. + await this.fetchOptions(this.choice); + + await this.fetchResults(this.choice); } /** @@ -433,7 +429,7 @@ export class AddonModChoiceIndexComponent extends CoreCourseModuleMainActivityCo * @return Promise resolved when done. */ protected async dataUpdated(online: boolean): Promise { - if (!online || !this.isPrefetched) { + if (!online || !this.isPrefetched()) { // Not downloaded, just refresh the data. return this.refreshContent(false); } diff --git a/src/addons/mod/data/components/index/addon-mod-data-index.html b/src/addons/mod/data/components/index/addon-mod-data-index.html index b64adbce8..a80142873 100644 --- a/src/addons/mod/data/components/index/addon-mod-data-index.html +++ b/src/addons/mod/data/components/index/addon-mod-data-index.html @@ -4,34 +4,12 @@ - - - - - - - - - - - - - - diff --git a/src/addons/mod/data/components/index/index.ts b/src/addons/mod/data/components/index/index.ts index 3386cbd82..543cceeda 100644 --- a/src/addons/mod/data/components/index/index.ts +++ b/src/addons/mod/data/components/index/index.ts @@ -202,14 +202,9 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp } /** - * Download data contents. - * - * @param refresh If it's refreshing content. - * @param sync If it should try to sync. - * @param showErrors If show errors to the user of hide them. - * @return Promise resolved when done. + * @inheritdoc */ - protected async fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise { + protected async fetchContent(refresh?: boolean, sync = false, showErrors = false): Promise { let canAdd = false; let canSearch = false; @@ -270,7 +265,6 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp } finally { this.canAdd = canAdd; this.canSearch = canSearch; - this.fillContextMenu(refresh); } } diff --git a/src/addons/mod/feedback/components/index/addon-mod-feedback-index.html b/src/addons/mod/feedback/components/index/addon-mod-feedback-index.html index bb3b4550e..eb54799b8 100644 --- a/src/addons/mod/feedback/components/index/addon-mod-feedback-index.html +++ b/src/addons/mod/feedback/components/index/addon-mod-feedback-index.html @@ -1,29 +1,5 @@ - - - - - - - - - - - - - - - - - diff --git a/src/addons/mod/feedback/components/index/index.ts b/src/addons/mod/feedback/components/index/index.ts index 3f3f94027..255b0e6ce 100644 --- a/src/addons/mod/feedback/components/index/index.ts +++ b/src/addons/mod/feedback/components/index/index.ts @@ -172,7 +172,7 @@ export class AddonModFeedbackIndexComponent extends CoreCourseModuleMainActivity /** * @inheritdoc */ - protected async fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise { + protected async fetchContent(refresh?: boolean, sync = false, showErrors = false): Promise { try { this.feedback = await AddonModFeedback.getFeedback(this.courseId, this.module.id); @@ -201,9 +201,6 @@ export class AddonModFeedbackIndexComponent extends CoreCourseModuleMainActivity await this.fetchFeedbackOverviewData(); } finally { - // Now fill the context menu. - this.fillContextMenu(refresh); - if (this.feedback) { // Check if there are responses stored in offline. this.hasOffline = await AddonModFeedbackOffline.hasFeedbackOfflineData(this.feedback.id); diff --git a/src/addons/mod/folder/components/index/addon-mod-folder-index.html b/src/addons/mod/folder/components/index/addon-mod-folder-index.html index ea4d48cdf..e50f4f51b 100644 --- a/src/addons/mod/folder/components/index/addon-mod-folder-index.html +++ b/src/addons/mod/folder/components/index/addon-mod-folder-index.html @@ -1,26 +1,5 @@ - - - - - - - - - - - - - - - diff --git a/src/addons/mod/folder/components/index/index.ts b/src/addons/mod/folder/components/index/index.ts index 73cdb6271..562a2824e 100644 --- a/src/addons/mod/folder/components/index/index.ts +++ b/src/addons/mod/folder/components/index/index.ts @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { CoreConstants } from '@/core/constants'; import { Component, Input, OnInit, Optional } from '@angular/core'; import { Params } from '@angular/router'; import { CoreCourseModuleMainResourceComponent } from '@features/course/classes/main-resource-component'; @@ -57,7 +56,6 @@ export class AddonModFolderIndexComponent extends CoreCourseModuleMainResourceCo this.contents = this.subfolder; this.loaded = true; - this.refreshIcon = CoreConstants.ICON_REFRESH; return; } @@ -73,7 +71,6 @@ export class AddonModFolderIndexComponent extends CoreCourseModuleMainResourceCo } } finally { this.loaded = true; - this.refreshIcon = CoreConstants.ICON_REFRESH; } } @@ -87,24 +84,17 @@ export class AddonModFolderIndexComponent extends CoreCourseModuleMainResourceCo } /** - * Download folder contents. - * - * @param refresh Whether we're refreshing data. - * @return Promise resolved when done. + * @inheritdoc */ protected async fetchContent(refresh = false): Promise { - try { - this.folderInstance = await AddonModFolder.getFolder(this.courseId, this.module.id); + this.folderInstance = await AddonModFolder.getFolder(this.courseId, this.module.id); - const contents = await CoreCourse.getModuleContents(this.module, undefined, undefined, false, refresh); + const contents = await CoreCourse.getModuleContents(this.module, undefined, undefined, false, refresh); - this.dataRetrieved.emit(this.folderInstance || this.module); + this.dataRetrieved.emit(this.folderInstance || this.module); - this.description = this.folderInstance ? this.folderInstance.intro : this.module.description; - this.contents = AddonModFolderHelper.formatContents(contents); - } finally { - this.fillContextMenu(refresh); - } + this.description = this.folderInstance ? this.folderInstance.intro : this.module.description; + this.contents = AddonModFolderHelper.formatContents(contents); } /** diff --git a/src/addons/mod/forum/components/index/index.html b/src/addons/mod/forum/components/index/index.html index 3bd8b6ada..b16a7b9d4 100644 --- a/src/addons/mod/forum/components/index/index.html +++ b/src/addons/mod/forum/components/index/index.html @@ -1,29 +1,6 @@ - - - - - - - - - - - - - - diff --git a/src/addons/mod/forum/components/index/index.ts b/src/addons/mod/forum/components/index/index.ts index 3c8337d41..40fda05ae 100644 --- a/src/addons/mod/forum/components/index/index.ts +++ b/src/addons/mod/forum/components/index/index.ts @@ -296,13 +296,9 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom } /** - * Download the component contents. - * - * @param refresh Whether we're refreshing data. - * @param sync If the refresh needs syncing. - * @param showErrors Wether to show errors to the user or hide them. + * @inheritdoc */ - protected async fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise { + protected async fetchContent(refresh = false, sync = false, showErrors = false): Promise { this.fetchFailed = false; try { @@ -329,8 +325,6 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom this.fetchFailed = true; // Set to prevent infinite calls with infinite-loading. throw error; // Pass the error to the parent catch. - } finally { - this.fillContextMenu(refresh); } } diff --git a/src/addons/mod/forum/lang.json b/src/addons/mod/forum/lang.json index 11f77ca53..86e48abf7 100644 --- a/src/addons/mod/forum/lang.json +++ b/src/addons/mod/forum/lang.json @@ -50,7 +50,6 @@ "posttomygroups": "Post a copy to all groups", "privatereply": "Reply privately", "re": "Re:", - "refreshdiscussions": "Refresh discussions", "refreshposts": "Refresh posts", "removefromfavourites": "Unstar this discussion", "reply": "Reply", diff --git a/src/addons/mod/glossary/components/index/addon-mod-glossary-index.html b/src/addons/mod/glossary/components/index/addon-mod-glossary-index.html index 142f410eb..2341c36e7 100644 --- a/src/addons/mod/glossary/components/index/addon-mod-glossary-index.html +++ b/src/addons/mod/glossary/components/index/addon-mod-glossary-index.html @@ -10,31 +10,9 @@ - - - - - - - - - - - - - - diff --git a/src/addons/mod/glossary/components/index/index.ts b/src/addons/mod/glossary/components/index/index.ts index 7cf42ded3..cbcc5aa89 100644 --- a/src/addons/mod/glossary/components/index/index.ts +++ b/src/addons/mod/glossary/components/index/index.ts @@ -177,39 +177,35 @@ export class AddonModGlossaryIndexComponent extends CoreCourseModuleMainActivity /** * @inheritdoc */ - protected async fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise { + protected async fetchContent(refresh = false, sync = false, showErrors = false): Promise { const entries = await this.promisedEntries; - try { - await entries.getSource().loadGlossary(); + await entries.getSource().loadGlossary(); - if (!this.glossary) { - return; - } - - this.description = this.glossary.intro || this.description; - this.canAdd = !!this.glossary.canaddentry || false; - - this.dataRetrieved.emit(this.glossary); - - if (!entries.getSource().fetchMode) { - this.switchMode('letter_all'); - } - - if (sync) { - // Try to synchronize the glossary. - await this.syncActivity(showErrors); - } - - const [hasOfflineRatings] = await Promise.all([ - CoreRatingOffline.hasRatings('mod_glossary', 'entry', ContextLevel.MODULE, this.glossary.coursemodule), - refresh ? entries.reload() : entries.load(), - ]); - - this.hasOfflineRatings = hasOfflineRatings; - } finally { - this.fillContextMenu(refresh); + if (!this.glossary) { + return; } + + this.description = this.glossary.intro || this.description; + this.canAdd = !!this.glossary.canaddentry || false; + + this.dataRetrieved.emit(this.glossary); + + if (!entries.getSource().fetchMode) { + this.switchMode('letter_all'); + } + + if (sync) { + // Try to synchronize the glossary. + await this.syncActivity(showErrors); + } + + const [hasOfflineRatings] = await Promise.all([ + CoreRatingOffline.hasRatings('mod_glossary', 'entry', ContextLevel.MODULE, this.glossary.coursemodule), + refresh ? entries.reload() : entries.load(), + ]); + + this.hasOfflineRatings = hasOfflineRatings; } /** diff --git a/src/addons/mod/h5pactivity/components/index/addon-mod-h5pactivity-index.html b/src/addons/mod/h5pactivity/components/index/addon-mod-h5pactivity-index.html index e7b6e7ea2..fc7dfb9ed 100644 --- a/src/addons/mod/h5pactivity/components/index/addon-mod-h5pactivity-index.html +++ b/src/addons/mod/h5pactivity/components/index/addon-mod-h5pactivity-index.html @@ -3,31 +3,10 @@ + iconAction="fas-chart-bar"> - - - - - - - - - - - - - - + (action)="viewAllAttempts()" iconAction="fas-chart-bar"> diff --git a/src/addons/mod/h5pactivity/components/index/index.ts b/src/addons/mod/h5pactivity/components/index/index.ts index f8cb4d015..834a623a7 100644 --- a/src/addons/mod/h5pactivity/components/index/index.ts +++ b/src/addons/mod/h5pactivity/components/index/index.ts @@ -112,51 +112,47 @@ export class AddonModH5PActivityIndexComponent extends CoreCourseModuleMainActiv /** * @inheritdoc */ - protected async fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise { - try { - this.h5pActivity = await AddonModH5PActivity.getH5PActivity(this.courseId, this.module.id, { - siteId: this.siteId, - }); + protected async fetchContent(refresh?: boolean, sync = false, showErrors = false): Promise { + this.h5pActivity = await AddonModH5PActivity.getH5PActivity(this.courseId, this.module.id, { + siteId: this.siteId, + }); - this.dataRetrieved.emit(this.h5pActivity); - this.description = this.h5pActivity.intro; - this.displayOptions = CoreH5PHelper.decodeDisplayOptions(this.h5pActivity.displayoptions); + this.dataRetrieved.emit(this.h5pActivity); + this.description = this.h5pActivity.intro; + this.displayOptions = CoreH5PHelper.decodeDisplayOptions(this.h5pActivity.displayoptions); - if (sync) { - await this.syncActivity(showErrors); - } + if (sync) { + await this.syncActivity(showErrors); + } - await Promise.all([ - this.checkHasOffline(), - this.fetchAccessInfo(), - this.fetchDeployedFileData(), - ]); + await Promise.all([ + this.checkHasOffline(), + this.fetchAccessInfo(), + this.fetchDeployedFileData(), + ]); - this.trackComponent = this.accessInfo?.cansubmit ? AddonModH5PActivityProvider.TRACK_COMPONENT : ''; - this.canViewAllAttempts = !!this.h5pActivity.enabletracking && !!this.accessInfo?.canreviewattempts && + this.trackComponent = this.accessInfo?.cansubmit ? AddonModH5PActivityProvider.TRACK_COMPONENT : ''; + this.canViewAllAttempts = !!this.h5pActivity.enabletracking && !!this.accessInfo?.canreviewattempts && AddonModH5PActivity.canGetUsersAttemptsInSite(); - if (this.h5pActivity.package && this.h5pActivity.package[0]) { - // The online player should use the original file, not the trusted one. - this.onlinePlayerUrl = CoreH5P.h5pPlayer.calculateOnlinePlayerUrl( - this.site.getURL(), - this.h5pActivity.package[0].fileurl, - this.displayOptions, - this.trackComponent, - ); - } + if (this.h5pActivity.package && this.h5pActivity.package[0]) { + // The online player should use the original file, not the trusted one. + this.onlinePlayerUrl = CoreH5P.h5pPlayer.calculateOnlinePlayerUrl( + this.site.getURL(), + this.h5pActivity.package[0].fileurl, + this.displayOptions, + this.trackComponent, + ); + } - if (!this.siteCanDownload || this.state == CoreConstants.DOWNLOADED) { - // Cannot download the file or already downloaded, play the package directly. - this.play(); + if (!this.siteCanDownload || this.state == CoreConstants.DOWNLOADED) { + // Cannot download the file or already downloaded, play the package directly. + this.play(); - } else if ((this.state == CoreConstants.NOT_DOWNLOADED || this.state == CoreConstants.OUTDATED) && CoreApp.isOnline() && + } else if ((this.state == CoreConstants.NOT_DOWNLOADED || this.state == CoreConstants.OUTDATED) && CoreApp.isOnline() && this.deployedFile?.filesize && CoreFilepool.shouldDownload(this.deployedFile.filesize)) { - // Package is small, download it automatically. Don't block this function for this. - this.downloadAutomatically(); - } - } finally { - this.fillContextMenu(refresh); + // Package is small, download it automatically. Don't block this function for this. + this.downloadAutomatically(); } } @@ -529,19 +525,6 @@ export class AddonModH5PActivityIndexComponent extends CoreCourseModuleMainActiv this.checkHasOffline(); } - /** - * @inheritdoc - */ - async gotoBlog(): Promise { - this.isOpeningPage = true; - - try { - await super.gotoBlog(); - } finally { - this.isOpeningPage = false; - } - } - /** * Component destroyed. */ diff --git a/src/addons/mod/imscp/components/index/addon-mod-imscp-index.html b/src/addons/mod/imscp/components/index/addon-mod-imscp-index.html index b6856d0db..a8d3096cf 100644 --- a/src/addons/mod/imscp/components/index/addon-mod-imscp-index.html +++ b/src/addons/mod/imscp/components/index/addon-mod-imscp-index.html @@ -3,26 +3,6 @@ - - - - - - - - - - - - - - diff --git a/src/addons/mod/imscp/components/index/index.ts b/src/addons/mod/imscp/components/index/index.ts index 0b6ae75a5..78df65bac 100644 --- a/src/addons/mod/imscp/components/index/index.ts +++ b/src/addons/mod/imscp/components/index/index.ts @@ -39,6 +39,7 @@ export class AddonModImscpIndexComponent extends CoreCourseModuleMainResourceCom protected items: AddonModImscpTocItem[] = []; protected currentHref?: string; + protected displayDescription = false; constructor(@Optional() courseContentsPage?: CoreCourseContentsPage) { super('AddonModImscpIndexComponent', courseContentsPage); @@ -70,42 +71,33 @@ export class AddonModImscpIndexComponent extends CoreCourseModuleMainResourceCom } /** - * Download imscp contents. - * - * @param refresh Whether we're refreshing data. - * @return Promise resolved when done. + * @inheritdoc */ protected async fetchContent(refresh = false): Promise { - try { - const downloadResult = await this.downloadResourceIfNeeded(refresh); + const downloadResult = await this.downloadResourceIfNeeded(refresh); - const imscp = await AddonModImscp.getImscp(this.courseId, this.module.id); - this.description = imscp.intro; - this.dataRetrieved.emit(imscp); + const imscp = await AddonModImscp.getImscp(this.courseId, this.module.id); + this.description = imscp.intro; + this.dataRetrieved.emit(imscp); - // Get contents. No need to refresh, it has been done in downloadResourceIfNeeded. - const contents = await CoreCourse.getModuleContents(this.module); + // Get contents. No need to refresh, it has been done in downloadResourceIfNeeded. + const contents = await CoreCourse.getModuleContents(this.module); - this.items = AddonModImscp.createItemList(contents); + this.items = AddonModImscp.createItemList(contents); - if (this.items.length && this.currentHref === undefined) { - this.currentHref = this.items[0].href; - } - - try { - await this.loadItemHref(this.currentHref); - } catch (error) { - CoreDomUtils.showErrorModalDefault(error, 'addon.mod_imscp.deploymenterror', true); - - throw new CoreSilentError(error); - } - - this.warning = downloadResult.failed ? this.getErrorDownloadingSomeFilesMessage(downloadResult.error!) : ''; - - } finally { - // Pass false because downloadResourceIfNeeded already invalidates and refresh data if refresh=true. - this.fillContextMenu(false); + if (this.items.length && this.currentHref === undefined) { + this.currentHref = this.items[0].href; } + + try { + await this.loadItemHref(this.currentHref); + } catch (error) { + CoreDomUtils.showErrorModalDefault(error, 'addon.mod_imscp.deploymenterror', true); + + throw new CoreSilentError(error); + } + + this.warning = downloadResult.failed ? this.getErrorDownloadingSomeFilesMessage(downloadResult.error!) : ''; } /** diff --git a/src/addons/mod/lesson/components/index/addon-mod-lesson-index.html b/src/addons/mod/lesson/components/index/addon-mod-lesson-index.html index dd57d8860..350ed2e21 100644 --- a/src/addons/mod/lesson/components/index/addon-mod-lesson-index.html +++ b/src/addons/mod/lesson/components/index/addon-mod-lesson-index.html @@ -1,29 +1,5 @@ - - - - - - - - - - - - - - - - - diff --git a/src/addons/mod/lesson/components/index/index.ts b/src/addons/mod/lesson/components/index/index.ts index 522d769fa..8c3a34a4a 100644 --- a/src/addons/mod/lesson/components/index/index.ts +++ b/src/addons/mod/lesson/components/index/index.ts @@ -136,85 +136,76 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo } /** - * Get the lesson data. - * - * @param refresh If it's refreshing content. - * @param sync If it should try to sync. - * @param showErrors If show errors to the user of hide them. - * @return Promise resolved when done. + * @inheritdoc */ - protected async fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise { - try { - let lessonReady = true; - this.askPassword = false; + protected async fetchContent(refresh?: boolean, sync = false, showErrors = false): Promise { + let lessonReady = true; + this.askPassword = false; - this.lesson = await AddonModLesson.getLesson(this.courseId, this.module.id); + this.lesson = await AddonModLesson.getLesson(this.courseId, this.module.id); - this.dataRetrieved.emit(this.lesson); - this.description = this.lesson.intro; // Show description only if intro is present. + this.dataRetrieved.emit(this.lesson); + this.description = this.lesson.intro; // Show description only if intro is present. - if (sync) { - // Try to synchronize the lesson. - await this.syncActivity(showErrors); - } + if (sync) { + // Try to synchronize the lesson. + await this.syncActivity(showErrors); + } - this.accessInfo = await AddonModLesson.getAccessInformation(this.lesson.id, { cmId: this.module.id }); - this.canManage = this.accessInfo.canmanage; - this.canViewReports = this.accessInfo.canviewreports; - this.preventReasons = []; - const promises: Promise[] = []; + this.accessInfo = await AddonModLesson.getAccessInformation(this.lesson.id, { cmId: this.module.id }); + this.canManage = this.accessInfo.canmanage; + this.canViewReports = this.accessInfo.canviewreports; + this.preventReasons = []; + const promises: Promise[] = []; - if (AddonModLesson.isLessonOffline(this.lesson)) { - // Handle status. - this.setStatusListener(); + if (AddonModLesson.isLessonOffline(this.lesson)) { + // Handle status. + this.setStatusListener(); - promises.push(this.loadOfflineData()); - } + promises.push(this.loadOfflineData()); + } - if (this.accessInfo.preventaccessreasons.length) { - let preventReason = AddonModLesson.getPreventAccessReason(this.accessInfo, false); - const askPassword = preventReason?.reason == 'passwordprotectedlesson'; + if (this.accessInfo.preventaccessreasons.length) { + let preventReason = AddonModLesson.getPreventAccessReason(this.accessInfo, false); + const askPassword = preventReason?.reason == 'passwordprotectedlesson'; - if (askPassword) { - try { - // The lesson requires a password. Check if there is one in memory or DB. - const password = this.password ? - this.password : - await AddonModLesson.getStoredPassword(this.lesson.id); + if (askPassword) { + try { + // The lesson requires a password. Check if there is one in memory or DB. + const password = this.password ? + this.password : + await AddonModLesson.getStoredPassword(this.lesson.id); - await this.validatePassword(password); + await this.validatePassword(password); - // Now that we have the password, get the access reason again ignoring the password. - preventReason = AddonModLesson.getPreventAccessReason(this.accessInfo, true); - if (preventReason) { - this.preventReasons = [preventReason]; - } - } catch { - // No password or the validation failed. Show password form. - this.askPassword = true; - this.preventReasons = [preventReason!]; - lessonReady = false; + // Now that we have the password, get the access reason again ignoring the password. + preventReason = AddonModLesson.getPreventAccessReason(this.accessInfo, true); + if (preventReason) { + this.preventReasons = [preventReason]; } - } else { - // Lesson cannot be started. + } catch { + // No password or the validation failed. Show password form. + this.askPassword = true; this.preventReasons = [preventReason!]; lessonReady = false; } + } else { + // Lesson cannot be started. + this.preventReasons = [preventReason!]; + lessonReady = false; } + } - if (this.selectedTab == 1 && this.canViewReports) { - // Only fetch the report data if the tab is selected. - promises.push(this.fetchReportData()); - } + if (this.selectedTab == 1 && this.canViewReports) { + // Only fetch the report data if the tab is selected. + promises.push(this.fetchReportData()); + } - await Promise.all(promises); + await Promise.all(promises); - if (lessonReady) { - // Lesson can be started, don't ask the password and don't show prevent messages. - this.lessonReady(); - } - } finally { - this.fillContextMenu(refresh); + if (lessonReady) { + // Lesson can be started, don't ask the password and don't show prevent messages. + this.lessonReady(); } } @@ -633,8 +624,6 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo } this.loaded = false; - this.refreshIcon = CoreConstants.ICON_LOADING; - this.syncIcon = CoreConstants.ICON_LOADING; try { await this.validatePassword( password); @@ -652,8 +641,6 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo CoreDomUtils.showErrorModal(error); } finally { this.loaded = true; - this.refreshIcon = CoreConstants.ICON_REFRESH; - this.syncIcon = CoreConstants.ICON_SYNC; CoreForms.triggerFormSubmittedEvent(this.formElement, true, this.siteId); } diff --git a/src/addons/mod/lti/components/index/addon-mod-lti-index.html b/src/addons/mod/lti/components/index/addon-mod-lti-index.html index 9dfc934b2..9892dd3ae 100644 --- a/src/addons/mod/lti/components/index/addon-mod-lti-index.html +++ b/src/addons/mod/lti/components/index/addon-mod-lti-index.html @@ -1,20 +1,5 @@ - - - - - - - - - - - @@ -24,7 +9,7 @@ - diff --git a/src/addons/mod/lti/components/index/index.ts b/src/addons/mod/lti/components/index/index.ts index f18244c01..333f0bb48 100644 --- a/src/addons/mod/lti/components/index/index.ts +++ b/src/addons/mod/lti/components/index/index.ts @@ -31,6 +31,7 @@ export class AddonModLtiIndexComponent extends CoreCourseModuleMainActivityCompo component = AddonModLtiProvider.COMPONENT; moduleName = 'lti'; + displayDescription = false; lti?: AddonModLtiLti; // The LTI object. @@ -55,15 +56,13 @@ export class AddonModLtiIndexComponent extends CoreCourseModuleMainActivityCompo /** * @inheritdoc */ - protected async fetchContent(refresh: boolean = false): Promise { - try { - this.lti = await AddonModLti.getLti(this.courseId, this.module.id); + protected async fetchContent(): Promise { + this.lti = await AddonModLti.getLti(this.courseId, this.module.id); - this.description = this.lti.intro; - this.dataRetrieved.emit(this.lti); - } finally { - this.fillContextMenu(refresh); - } + this.description = this.lti.intro; + + this.displayDescription = this.lti && !!this.lti.showdescriptionlaunch; + this.dataRetrieved.emit(this.lti); } /** diff --git a/src/addons/mod/page/components/index/addon-mod-page-index.html b/src/addons/mod/page/components/index/addon-mod-page-index.html index bd5f3a6ba..e17fa3571 100644 --- a/src/addons/mod/page/components/index/addon-mod-page-index.html +++ b/src/addons/mod/page/components/index/addon-mod-page-index.html @@ -1,26 +1,5 @@ - - - - - - - - - - - - - - - diff --git a/src/addons/mod/page/components/index/index.ts b/src/addons/mod/page/components/index/index.ts index dfba12b29..bbf8ac151 100644 --- a/src/addons/mod/page/components/index/index.ts +++ b/src/addons/mod/page/components/index/index.ts @@ -71,29 +71,22 @@ export class AddonModPageIndexComponent extends CoreCourseModuleMainResourceComp } /** - * Download page contents. - * - * @param refresh Whether we're refreshing data. - * @return Promise resolved when done. + * @inheritdoc */ protected async fetchContent(refresh?: boolean): Promise { - try { - // Download the resource if it needs to be downloaded. - const downloadResult = await this.downloadResourceIfNeeded(refresh); + // Download the resource if it needs to be downloaded. + const downloadResult = await this.downloadResourceIfNeeded(refresh); - // Get contents. No need to refresh, it has been done in downloadResourceIfNeeded. - const contents = await CoreCourse.getModuleContents(this.module); + // Get contents. No need to refresh, it has been done in downloadResourceIfNeeded. + const contents = await CoreCourse.getModuleContents(this.module); - const results = await Promise.all([ - this.loadPageData(), - AddonModPageHelper.getPageHtml(contents, this.module.id), - ]); + const results = await Promise.all([ + this.loadPageData(), + AddonModPageHelper.getPageHtml(contents, this.module.id), + ]); - this.contents = results[1]; - this.warning = downloadResult?.failed ? this.getErrorDownloadingSomeFilesMessage(downloadResult.error!) : ''; - } finally { - this.fillContextMenu(refresh); - } + this.contents = results[1]; + this.warning = downloadResult?.failed ? this.getErrorDownloadingSomeFilesMessage(downloadResult.error!) : ''; } /** diff --git a/src/addons/mod/quiz/components/index/addon-mod-quiz-index.html b/src/addons/mod/quiz/components/index/addon-mod-quiz-index.html index 8060c0171..4f6ec82f8 100644 --- a/src/addons/mod/quiz/components/index/addon-mod-quiz-index.html +++ b/src/addons/mod/quiz/components/index/addon-mod-quiz-index.html @@ -1,29 +1,5 @@ - - - - - - - - - - - - - - - - - @@ -214,7 +190,7 @@ {{ 'core.openinbrowser' | translate }} diff --git a/src/addons/mod/quiz/components/index/index.ts b/src/addons/mod/quiz/components/index/index.ts index 92bdfd881..1f03d876e 100644 --- a/src/addons/mod/quiz/components/index/index.ts +++ b/src/addons/mod/quiz/components/index/index.ts @@ -180,82 +180,73 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp } /** - * Get the quiz data. - * - * @param refresh If it's refreshing content. - * @param sync If it should try to sync. - * @param showErrors If show errors to the user of hide them. - * @return Promise resolved when done. + * @inheritdoc */ - protected async fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise { - try { - // First get the quiz instance. - const quiz = await AddonModQuiz.getQuiz(this.courseId, this.module.id); + protected async fetchContent(refresh?: boolean, sync = false, showErrors = false): Promise { + // First get the quiz instance. + const quiz = await AddonModQuiz.getQuiz(this.courseId, this.module.id); - this.gradeMethodReadable = AddonModQuiz.getQuizGradeMethod(quiz.grademethod); - this.now = Date.now(); - this.dataRetrieved.emit(quiz); - this.description = quiz.intro || this.description; - this.candidateQuiz = quiz; + this.gradeMethodReadable = AddonModQuiz.getQuizGradeMethod(quiz.grademethod); + this.now = Date.now(); + this.dataRetrieved.emit(quiz); + this.description = quiz.intro || this.description; + this.candidateQuiz = quiz; - // Try to get warnings from automatic sync. - const warnings = await AddonModQuizSync.getSyncWarnings(quiz.id); + // Try to get warnings from automatic sync. + const warnings = await AddonModQuizSync.getSyncWarnings(quiz.id); - if (warnings?.length) { - // Show warnings and delete them so they aren't shown again. - CoreDomUtils.showErrorModal(CoreTextUtils.buildMessage(warnings)); + if (warnings?.length) { + // Show warnings and delete them so they aren't shown again. + CoreDomUtils.showErrorModal(CoreTextUtils.buildMessage(warnings)); - await AddonModQuizSync.setSyncWarnings(quiz.id, []); - } - - if (AddonModQuiz.isQuizOffline(quiz)) { - if (sync) { - // Try to sync the quiz. - try { - await this.syncActivity(showErrors); - } catch { - // Ignore errors, keep getting data even if sync fails. - this.autoReview = undefined; - } - } - } else { - this.autoReview = undefined; - this.showStatusSpinner = false; - } - - if (AddonModQuiz.isQuizOffline(quiz)) { - // Handle status. - this.setStatusListener(); - - // Get last synchronization time and check if sync button should be seen. - this.syncTime = await AddonModQuizSync.getReadableSyncTime(quiz.id); - this.hasOffline = await AddonModQuizSync.hasDataToSync(quiz.id); - } - - // Get quiz access info. - this.quizAccessInfo = await AddonModQuiz.getQuizAccessInformation(quiz.id, { cmId: this.module.id }); - - this.showReviewColumn = this.quizAccessInfo.canreviewmyattempts; - this.accessRules = this.quizAccessInfo.accessrules; - this.unsupportedRules = AddonModQuiz.getUnsupportedRules(this.quizAccessInfo.activerulenames); - - if (quiz.preferredbehaviour) { - this.behaviourSupported = CoreQuestionBehaviourDelegate.isBehaviourSupported(quiz.preferredbehaviour); - } - - // Get question types in the quiz. - const types = await AddonModQuiz.getQuizRequiredQtypes(quiz.id, { cmId: this.module.id }); - - this.unsupportedQuestions = AddonModQuiz.getUnsupportedQuestions(types); - this.hasSupportedQuestions = !!types.find((type) => type != 'random' && this.unsupportedQuestions.indexOf(type) == -1); - - await this.getAttempts(quiz); - - // Quiz is ready to be shown, move it to the variable that is displayed. - this.quiz = quiz; - } finally { - this.fillContextMenu(refresh); + await AddonModQuizSync.setSyncWarnings(quiz.id, []); } + + if (AddonModQuiz.isQuizOffline(quiz)) { + if (sync) { + // Try to sync the quiz. + try { + await this.syncActivity(showErrors); + } catch { + // Ignore errors, keep getting data even if sync fails. + this.autoReview = undefined; + } + } + } else { + this.autoReview = undefined; + this.showStatusSpinner = false; + } + + if (AddonModQuiz.isQuizOffline(quiz)) { + // Handle status. + this.setStatusListener(); + + // Get last synchronization time and check if sync button should be seen. + this.syncTime = await AddonModQuizSync.getReadableSyncTime(quiz.id); + this.hasOffline = await AddonModQuizSync.hasDataToSync(quiz.id); + } + + // Get quiz access info. + this.quizAccessInfo = await AddonModQuiz.getQuizAccessInformation(quiz.id, { cmId: this.module.id }); + + this.showReviewColumn = this.quizAccessInfo.canreviewmyattempts; + this.accessRules = this.quizAccessInfo.accessrules; + this.unsupportedRules = AddonModQuiz.getUnsupportedRules(this.quizAccessInfo.activerulenames); + + if (quiz.preferredbehaviour) { + this.behaviourSupported = CoreQuestionBehaviourDelegate.isBehaviourSupported(quiz.preferredbehaviour); + } + + // Get question types in the quiz. + const types = await AddonModQuiz.getQuizRequiredQtypes(quiz.id, { cmId: this.module.id }); + + this.unsupportedQuestions = AddonModQuiz.getUnsupportedQuestions(types); + this.hasSupportedQuestions = !!types.find((type) => type != 'random' && this.unsupportedQuestions.indexOf(type) == -1); + + await this.getAttempts(quiz); + + // Quiz is ready to be shown, move it to the variable that is displayed. + this.quiz = quiz; } /** @@ -465,16 +456,12 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp // Refresh data. this.loaded = false; - this.refreshIcon = CoreConstants.ICON_LOADING; - this.syncIcon = CoreConstants.ICON_LOADING; this.content?.scrollToTop(); await promise; await CoreUtils.ignoreErrors(this.refreshContent(true)); this.loaded = true; - this.refreshIcon = CoreConstants.ICON_REFRESH; - this.syncIcon = CoreConstants.ICON_SYNC; } /** diff --git a/src/addons/mod/resource/components/index/addon-mod-resource-index.html b/src/addons/mod/resource/components/index/addon-mod-resource-index.html index 78357a63f..c209b8031 100644 --- a/src/addons/mod/resource/components/index/addon-mod-resource-index.html +++ b/src/addons/mod/resource/components/index/addon-mod-resource-index.html @@ -1,21 +1,5 @@ - - - - - - - - - - @@ -25,9 +9,8 @@ - + diff --git a/src/addons/mod/resource/components/index/index.ts b/src/addons/mod/resource/components/index/index.ts index 897711e19..203b2349e 100644 --- a/src/addons/mod/resource/components/index/index.ts +++ b/src/addons/mod/resource/components/index/index.ts @@ -116,76 +116,74 @@ export class AddonModResourceIndexComponent extends CoreCourseModuleMainResource throw new CoreError(Translate.instant('core.filenotfound')); } - let hasCalledDownloadResource = false; - // Get the resource instance to get the latest name/description and to know if it's embedded. const resource = await AddonModResource.getResourceData(this.courseId, this.module.id); this.description = resource.intro || ''; const options: AddonModResourceCustomData = resource.displayoptions ? CoreTextUtils.unserialize(resource.displayoptions) : {}; - try { - this.displayDescription = options.printintro === undefined || !!options.printintro; - this.dataRetrieved.emit(resource); + this.displayDescription = options.printintro === undefined || !!options.printintro; + this.dataRetrieved.emit(resource); - if (AddonModResourceHelper.isDisplayedInIframe(this.module)) { - hasCalledDownloadResource = true; + if (AddonModResourceHelper.isDisplayedInIframe(this.module)) { - const downloadResult = await this.downloadResourceIfNeeded(refresh, true); - const src = await AddonModResourceHelper.getIframeSrc(this.module); - this.mode = 'iframe'; + const downloadResult = await this.downloadResourceIfNeeded(refresh, true); + const src = await AddonModResourceHelper.getIframeSrc(this.module); + this.mode = 'iframe'; - if (this.src && src.toString() == this.src.toString()) { - // Re-loading same page. - // Set it to empty and then re-set the src in the next digest so it detects it has changed. - this.src = ''; - setTimeout(() => { - this.src = src; - }); - } else { + if (this.src && src.toString() == this.src.toString()) { + // Re-loading same page. + // Set it to empty and then re-set the src in the next digest so it detects it has changed. + this.src = ''; + setTimeout(() => { this.src = src; - } - - this.warning = downloadResult.failed - ? this.getErrorDownloadingSomeFilesMessage(downloadResult.error!) - : ''; - - return; - } - - if (resource && 'display' in resource && AddonModResourceHelper.isDisplayedEmbedded(this.module, resource.display)) { - this.mode = 'embedded'; - this.warning = ''; - - this.contentText = await AddonModResourceHelper.getEmbeddedHtml(this.module); - this.mode = this.contentText.length > 0 ? 'embedded' : 'external'; + }); } else { - this.mode = 'external'; - this.warning = ''; - let mimetype: string; - - if (this.isIOS) { - this.shouldOpenInBrowser = CoreFileHelper.shouldOpenInBrowser(contents[0]); - } - - if ('contentsinfo' in this.module && this.module.contentsinfo) { - mimetype = this.module.contentsinfo.mimetypes[0]; - this.readableSize = CoreTextUtils.bytesToSize(this.module.contentsinfo.filessize, 1); - this.timemodified = this.module.contentsinfo.lastmodified * 1000; - } else { - mimetype = await CoreUtils.getMimeTypeFromUrl(CoreFileHelper.getFileUrl(contents[0])); - this.readableSize = CoreTextUtils.bytesToSize(contents[0].filesize, 1); - this.timemodified = contents[0].timemodified * 1000; - } - - this.timecreated = contents[0].timecreated * 1000; - this.isExternalFile = !!contents[0].isexternalfile; - this.type = CoreMimetypeUtils.getMimetypeDescription(mimetype); - this.isStreamedFile = CoreMimetypeUtils.isStreamedMimetype(mimetype); + this.src = src; } - } finally { - // Pass false in some cases because downloadResourceIfNeeded already invalidates and refresh data if refresh=true. - this.fillContextMenu(hasCalledDownloadResource ? false : refresh); + + // Never show description on iframe. + this.displayDescription = false; + + this.warning = downloadResult.failed + ? this.getErrorDownloadingSomeFilesMessage(downloadResult.error!) + : ''; + + return; + } + + if (resource && 'display' in resource && AddonModResourceHelper.isDisplayedEmbedded(this.module, resource.display)) { + this.mode = 'embedded'; + this.warning = ''; + + this.contentText = await AddonModResourceHelper.getEmbeddedHtml(this.module); + this.mode = this.contentText.length > 0 ? 'embedded' : 'external'; + } else { + this.mode = 'external'; + this.warning = ''; + let mimetype: string; + + // Always show description on external. + this.displayDescription = true; + + if (this.isIOS) { + this.shouldOpenInBrowser = CoreFileHelper.shouldOpenInBrowser(contents[0]); + } + + if ('contentsinfo' in this.module && this.module.contentsinfo) { + mimetype = this.module.contentsinfo.mimetypes[0]; + this.readableSize = CoreTextUtils.bytesToSize(this.module.contentsinfo.filessize, 1); + this.timemodified = this.module.contentsinfo.lastmodified * 1000; + } else { + mimetype = await CoreUtils.getMimeTypeFromUrl(CoreFileHelper.getFileUrl(contents[0])); + this.readableSize = CoreTextUtils.bytesToSize(contents[0].filesize, 1); + this.timemodified = contents[0].timemodified * 1000; + } + + this.timecreated = contents[0].timecreated * 1000; + this.isExternalFile = !!contents[0].isexternalfile; + this.type = CoreMimetypeUtils.getMimetypeDescription(mimetype); + this.isStreamedFile = CoreMimetypeUtils.isStreamedMimetype(mimetype); } } diff --git a/src/addons/mod/scorm/components/index/addon-mod-scorm-index.html b/src/addons/mod/scorm/components/index/addon-mod-scorm-index.html index e5eea8e77..0fa5c17a4 100644 --- a/src/addons/mod/scorm/components/index/addon-mod-scorm-index.html +++ b/src/addons/mod/scorm/components/index/addon-mod-scorm-index.html @@ -1,29 +1,5 @@ - - - - - - - - - - - - - - - - - @@ -176,7 +152,7 @@

{{ errorMessage | translate }}

- + {{ 'core.openinbrowser' | translate }} diff --git a/src/addons/mod/scorm/components/index/index.ts b/src/addons/mod/scorm/components/index/index.ts index 44985f6d1..88dd7b2c8 100644 --- a/src/addons/mod/scorm/components/index/index.ts +++ b/src/addons/mod/scorm/components/index/index.ts @@ -175,43 +175,39 @@ export class AddonModScormIndexComponent extends CoreCourseModuleMainActivityCom /** * @inheritdoc */ - protected async fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise { - try { - // Get the SCORM instance. - this.scorm = await AddonModScorm.getScorm(this.courseId, this.module.id, { moduleUrl: this.module.url }); + protected async fetchContent(refresh?: boolean, sync = false, showErrors = false): Promise { + // Get the SCORM instance. + this.scorm = await AddonModScorm.getScorm(this.courseId, this.module.id, { moduleUrl: this.module.url }); - this.dataRetrieved.emit(this.scorm); - this.description = this.scorm.intro || this.description; - this.errorMessage = AddonModScorm.isScormUnsupported(this.scorm); + this.dataRetrieved.emit(this.scorm); + this.description = this.scorm.intro || this.description; + this.errorMessage = AddonModScorm.isScormUnsupported(this.scorm); - if (this.scorm.warningMessage) { - return; // SCORM is closed or not open yet, we can't get more data. - } + if (this.scorm.warningMessage) { + return; // SCORM is closed or not open yet, we can't get more data. + } - if (sync) { - // Try to synchronize the SCORM. - await CoreUtils.ignoreErrors(this.syncActivity(showErrors)); - } + if (sync) { + // Try to synchronize the SCORM. + await CoreUtils.ignoreErrors(this.syncActivity(showErrors)); + } - const [syncTime, accessInfo] = await Promise.all([ - AddonModScormSync.getReadableSyncTime(this.scorm.id), - AddonModScorm.getAccessInformation(this.scorm.id, { cmId: this.module.id }), - this.fetchAttemptData(this.scorm), - ]); + const [syncTime, accessInfo] = await Promise.all([ + AddonModScormSync.getReadableSyncTime(this.scorm.id), + AddonModScorm.getAccessInformation(this.scorm.id, { cmId: this.module.id }), + this.fetchAttemptData(this.scorm), + ]); - this.syncTime = syncTime; - this.accessInfo = accessInfo; + this.syncTime = syncTime; + this.accessInfo = accessInfo; - // Check whether to launch the SCORM immediately. - if (this.skip === undefined) { - this.skip = !this.hasOffline && !this.errorMessage && + // Check whether to launch the SCORM immediately. + if (this.skip === undefined) { + this.skip = !this.hasOffline && !this.errorMessage && (!this.scorm.lastattemptlock || this.attemptsLeft > 0) && this.accessInfo.canskipview && !this.accessInfo.canviewreport && this.scorm.skipview! >= AddonModScormProvider.SKIPVIEW_FIRST && (this.scorm.skipview == AddonModScormProvider.SKIPVIEW_ALWAYS || this.lastAttempt == 0); - } - } finally { - this.fillContextMenu(refresh); } } diff --git a/src/addons/mod/survey/components/index/addon-mod-survey-index.html b/src/addons/mod/survey/components/index/addon-mod-survey-index.html index 8f73a5c47..dedf029c9 100644 --- a/src/addons/mod/survey/components/index/addon-mod-survey-index.html +++ b/src/addons/mod/survey/components/index/addon-mod-survey-index.html @@ -1,30 +1,5 @@ - - - - - - - - - - - - - - - - - @@ -41,7 +16,7 @@

{{ 'addon.mod_survey.surveycompletednograph' | translate }}

- + {{ 'addon.mod_survey.results' | translate }} diff --git a/src/addons/mod/survey/components/index/index.ts b/src/addons/mod/survey/components/index/index.ts index 0be63e920..eca06619b 100644 --- a/src/addons/mod/survey/components/index/index.ts +++ b/src/addons/mod/survey/components/index/index.ts @@ -115,39 +115,30 @@ export class AddonModSurveyIndexComponent extends CoreCourseModuleMainActivityCo } /** - * Download survey contents. - * - * @param refresh If it's refreshing content. - * @param sync If it should try to sync. - * @param showErrors If show errors to the user of hide them. - * @return Promise resolved when done. + * @inheritdoc */ - protected async fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise { - try { - this.survey = await AddonModSurvey.getSurvey(this.courseId, this.module.id); + protected async fetchContent(refresh?: boolean, sync = false, showErrors = false): Promise { + this.survey = await AddonModSurvey.getSurvey(this.courseId, this.module.id); - this.description = this.survey.intro; - this.dataRetrieved.emit(this.survey); + this.description = this.survey.intro; + this.dataRetrieved.emit(this.survey); - if (sync) { - // Try to synchronize the survey. - const answersSent = await this.syncActivity(showErrors); - if (answersSent) { - // Answers were sent, update the survey. - this.survey = await AddonModSurvey.getSurvey(this.courseId, this.module.id); - } + if (sync) { + // Try to synchronize the survey. + const answersSent = await this.syncActivity(showErrors); + if (answersSent) { + // Answers were sent, update the survey. + this.survey = await AddonModSurvey.getSurvey(this.courseId, this.module.id); } + } - // Check if there are answers stored in offline. - this.hasOffline = this.survey.surveydone - ? false - : await AddonModSurveyOffline.hasAnswers(this.survey.id); + // Check if there are answers stored in offline. + this.hasOffline = this.survey.surveydone + ? false + : await AddonModSurveyOffline.hasAnswers(this.survey.id); - if (!this.survey.surveydone && !this.hasOffline) { - await this.fetchQuestions(); - } - } finally { - this.fillContextMenu(refresh); + if (!this.survey.surveydone && !this.hasOffline) { + await this.fetchQuestions(); } } diff --git a/src/addons/mod/url/components/index/addon-mod-url-index.html b/src/addons/mod/url/components/index/addon-mod-url-index.html index 47d00e1af..8b531ded4 100644 --- a/src/addons/mod/url/components/index/addon-mod-url-index.html +++ b/src/addons/mod/url/components/index/addon-mod-url-index.html @@ -1,16 +1,5 @@ - - - - - - - diff --git a/src/addons/mod/wiki/components/index/addon-mod-wiki-index.html b/src/addons/mod/wiki/components/index/addon-mod-wiki-index.html index c689ec193..de251b747 100644 --- a/src/addons/mod/wiki/components/index/addon-mod-wiki-index.html +++ b/src/addons/mod/wiki/components/index/addon-mod-wiki-index.html @@ -13,34 +13,12 @@ - - - - - - - - - - - - - - diff --git a/src/addons/mod/wiki/components/index/index.ts b/src/addons/mod/wiki/components/index/index.ts index 4f5a4709f..a9b8780de 100644 --- a/src/addons/mod/wiki/components/index/index.ts +++ b/src/addons/mod/wiki/components/index/index.ts @@ -21,14 +21,16 @@ import { CoreCourse } from '@features/course/services/course'; import { CoreTag, CoreTagItem } from '@features/tag/services/tag'; import { CoreUser } from '@features/user/services/user'; import { IonContent } from '@ionic/angular'; +import { CoreApp } from '@services/app'; import { CoreGroup, CoreGroups } from '@services/groups'; 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 { Translate } from '@singletons'; +import { Network, Translate, NgZone } from '@singletons'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; +import { Subscription } from 'rxjs'; import { Md5 } from 'ts-md5'; import { AddonModWikiPageDBRecord } from '../../services/database/wiki'; import { AddonModWikiModuleHandlerService } from '../../services/handlers/module'; @@ -76,6 +78,8 @@ export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComp moduleName = 'wiki'; groupWiki = false; + isOnline = false; + wiki?: AddonModWikiWiki; // The wiki instance. isMainPage = false; // Whether the user is viewing wiki's main page (just entered the wiki). canEdit = false; // Whether user can edit the page. @@ -104,12 +108,23 @@ export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComp protected ignoreManualSyncEvent = false; // Whether manual sync event should be ignored. protected currentUserId?: number; // Current user ID. protected currentPath!: string; + protected onlineSubscription: Subscription; // It will observe the status of the network connection. constructor( protected content?: IonContent, @Optional() courseContentsPage?: CoreCourseContentsPage, ) { super('AddonModLessonIndexComponent', content, courseContentsPage); + + 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(); + }); + }); } /** @@ -212,7 +227,7 @@ export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComp /** * @inheritdoc */ - protected async fetchContent(refresh = false, sync = false, showErrors = false): Promise { + protected async fetchContent(refresh?: boolean, sync = false, showErrors = false): Promise { try { // Get the wiki instance. this.wiki = await AddonModWiki.getWiki(this.courseId, this.module.id); @@ -240,7 +255,6 @@ export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComp } this.description = this.wiki.intro || this.module.description; - this.externalUrl = this.module.url; this.componentId = this.module.id; await this.fetchSubwikis(this.wiki.id); @@ -278,8 +292,6 @@ export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComp } throw error; - } finally { - this.fillContextMenu(refresh); } } @@ -835,6 +847,7 @@ export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComp this.manualSyncObserver?.off(); this.newPageObserver?.off(); + this.onlineSubscription.unsubscribe(); if (this.wiki) { AddonModWiki.wikiPageClosed(this.wiki.id, this.currentPath); } diff --git a/src/addons/mod/workshop/components/index/addon-mod-workshop-index.html b/src/addons/mod/workshop/components/index/addon-mod-workshop-index.html index ec5719b0d..9ff979bf8 100644 --- a/src/addons/mod/workshop/components/index/addon-mod-workshop-index.html +++ b/src/addons/mod/workshop/components/index/addon-mod-workshop-index.html @@ -1,30 +1,5 @@ - - - - - - - - - - - - - - - - - diff --git a/src/addons/mod/workshop/components/index/index.ts b/src/addons/mod/workshop/components/index/index.ts index f3cd51e4d..1376c7433 100644 --- a/src/addons/mod/workshop/components/index/index.ts +++ b/src/addons/mod/workshop/components/index/index.ts @@ -216,55 +216,45 @@ export class AddonModWorkshopIndexComponent extends CoreCourseModuleMainActivity } /** - * Download feedback contents. - * - * @param refresh If it's refreshing content. - * @param sync If it should try to sync. - * @param showErrors If show errors to the user of hide them. - * @return Promise resolved when done. + * @inheritdoc */ - protected async fetchContent(refresh = false, sync = false, showErrors = false): Promise { - try { - this.workshop = await AddonModWorkshop.getWorkshop(this.courseId, this.module.id); + protected async fetchContent(refresh?: boolean, sync = false, showErrors = false): Promise { + this.workshop = await AddonModWorkshop.getWorkshop(this.courseId, this.module.id); - this.description = this.workshop.intro; - this.dataRetrieved.emit(this.workshop); + this.description = this.workshop.intro; + this.dataRetrieved.emit(this.workshop); - if (sync) { - // Try to synchronize the feedback. - await this.syncActivity(showErrors); - } - - // Check if there are answers stored in offline. - this.access = await AddonModWorkshop.getWorkshopAccessInformation(this.workshop.id, { cmId: this.module.id }); - - if (this.access.canviewallsubmissions) { - this.groupInfo = await CoreGroups.getActivityGroupInfo(this.workshop.coursemodule); - this.group = CoreGroups.validateGroupId(this.group, this.groupInfo); - } - - this.phases = await AddonModWorkshop.getUserPlanPhases(this.workshop.id, { cmId: this.module.id }); - - this.phases[this.workshop.phase].tasks.forEach((task) => { - if (!task.link && (task.code == 'examples' || task.code == 'prepareexamples')) { - // Add links to manage examples. - task.link = this.externalUrl!; - } - }); - - // Check if there are info stored in offline. - this.hasOffline = await AddonModWorkshopOffline.hasWorkshopOfflineData(this.workshop.id); - if (this.hasOffline) { - this.offlineSubmissions = await AddonModWorkshopOffline.getSubmissions(this.workshop.id); - } else { - this.offlineSubmissions = []; - } - - await this.setPhaseInfo(); - - } finally { - this.fillContextMenu(refresh); + if (sync) { + // Try to synchronize the feedback. + await this.syncActivity(showErrors); } + + // Check if there are answers stored in offline. + this.access = await AddonModWorkshop.getWorkshopAccessInformation(this.workshop.id, { cmId: this.module.id }); + + if (this.access.canviewallsubmissions) { + this.groupInfo = await CoreGroups.getActivityGroupInfo(this.workshop.coursemodule); + this.group = CoreGroups.validateGroupId(this.group, this.groupInfo); + } + + this.phases = await AddonModWorkshop.getUserPlanPhases(this.workshop.id, { cmId: this.module.id }); + + this.phases[this.workshop.phase].tasks.forEach((task) => { + if (!task.link && (task.code == 'examples' || task.code == 'prepareexamples')) { + // Add links to manage examples. + task.link = this.module.url || ''; + } + }); + + // Check if there are info stored in offline. + this.hasOffline = await AddonModWorkshopOffline.hasWorkshopOfflineData(this.workshop.id); + if (this.hasOffline) { + this.offlineSubmissions = await AddonModWorkshopOffline.getSubmissions(this.workshop.id); + } else { + this.offlineSubmissions = []; + } + + await this.setPhaseInfo(); } /** @@ -394,7 +384,7 @@ export class AddonModWorkshopIndexComponent extends CoreCourseModuleMainActivity componentProps: { phases: CoreUtils.objectToArray(this.phases), workshopPhase: this.workshop!.phase, - externalUrl: this.externalUrl, + externalUrl: this.module.url, showSubmit: this.showSubmit, }, }); diff --git a/src/core/features/course/classes/main-activity-component.ts b/src/core/features/course/classes/main-activity-component.ts index e89d53476..d0214e996 100644 --- a/src/core/features/course/classes/main-activity-component.ts +++ b/src/core/features/course/classes/main-activity-component.ts @@ -17,15 +17,11 @@ import { IonContent } from '@ionic/angular'; import { CoreCourseModuleMainResourceComponent } from './main-resource-component'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; -import { Network, NgZone } from '@singletons'; -import { Subscription } from 'rxjs'; -import { CoreApp } from '@services/app'; import { CoreCourse } from '../services/course'; import { CoreUtils } from '@services/utils/utils'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreWSExternalWarning } from '@services/ws'; import { CoreCourseContentsPage } from '../pages/contents/contents'; -import { CoreConstants } from '@/core/constants'; import { CoreSites } from '@services/sites'; /** @@ -40,12 +36,7 @@ export class CoreCourseModuleMainActivityComponent extends CoreCourseModuleMainR moduleName?: string; // Raw module name to be translated. It will be translated on init. - // Data for context menu. - syncIcon?: string; // Sync icon. - isOnline?: boolean; // If the app is online or not. - protected syncObserver?: CoreEventObserver; // It will observe the sync auto event. - protected onlineSubscription: Subscription; // It will observe the status of the network connection. protected syncEventName?: string; // Auto sync event name. constructor( @@ -54,14 +45,6 @@ export class CoreCourseModuleMainActivityComponent extends CoreCourseModuleMainR courseContentsPage?: CoreCourseContentsPage, ) { super(loggerName, courseContentsPage); - - // 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(); - }); - }); } /** @@ -71,7 +54,6 @@ export class CoreCourseModuleMainActivityComponent extends CoreCourseModuleMainR await super.ngOnInit(); this.hasOffline = false; - this.syncIcon = CoreConstants.ICON_LOADING; this.moduleName = CoreCourse.translateModuleName(this.moduleName || ''); if (this.syncEventName) { @@ -118,20 +100,12 @@ export class CoreCourseModuleMainActivityComponent extends CoreCourseModuleMainR return; } - this.refreshIcon = CoreConstants.ICON_LOADING; - this.syncIcon = CoreConstants.ICON_LOADING; + await CoreUtils.ignoreErrors(Promise.all([ + this.invalidateContent(), + this.showCompletion ? CoreCourse.invalidateModule(this.module.id) : undefined, + ])); - try { - await CoreUtils.ignoreErrors(Promise.all([ - this.invalidateContent(), - this.showCompletion ? CoreCourse.invalidateModule(this.module.id) : undefined, - ])); - - await this.loadContent(true, sync, showErrors); - } finally { - this.refreshIcon = CoreConstants.ICON_REFRESH; - this.syncIcon = CoreConstants.ICON_SYNC; - } + await this.loadContent(true, sync, showErrors); } /** @@ -142,17 +116,10 @@ export class CoreCourseModuleMainActivityComponent extends CoreCourseModuleMainR * @return Resolved when done. */ protected async showLoadingAndFetch(sync: boolean = false, showErrors: boolean = false): Promise { - this.refreshIcon = CoreConstants.ICON_LOADING; - this.syncIcon = CoreConstants.ICON_LOADING; this.loaded = false; this.content?.scrollToTop(); - try { - await this.loadContent(false, sync, showErrors); - } finally { - this.refreshIcon = CoreConstants.ICON_REFRESH; - this.syncIcon = CoreConstants.ICON_REFRESH; - } + await this.loadContent(false, sync, showErrors); } /** @@ -163,8 +130,6 @@ export class CoreCourseModuleMainActivityComponent extends CoreCourseModuleMainR * @return Resolved when done. */ protected showLoadingAndRefresh(sync: boolean = false, showErrors: boolean = false): Promise { - this.refreshIcon = CoreConstants.ICON_LOADING; - this.syncIcon = CoreConstants.ICON_LOADING; this.loaded = false; this.content?.scrollToTop(); @@ -193,8 +158,6 @@ export class CoreCourseModuleMainActivityComponent extends CoreCourseModuleMainR * @return Promise resolved when done. */ protected async loadContent(refresh?: boolean, sync: boolean = false, showErrors: boolean = false): Promise { - this.isOnline = CoreApp.isOnline(); - if (!this.module) { // This can happen if course format changes from single activity to weekly/topics. return; @@ -215,8 +178,6 @@ export class CoreCourseModuleMainActivityComponent extends CoreCourseModuleMainR CoreDomUtils.showErrorModalDefault(error, this.fetchContentDefaultError, true); } finally { this.loaded = true; - this.refreshIcon = CoreConstants.ICON_REFRESH; - this.syncIcon = CoreConstants.ICON_REFRESH; } } @@ -269,8 +230,6 @@ export class CoreCourseModuleMainActivityComponent extends CoreCourseModuleMainR */ ngOnDestroy(): void { super.ngOnDestroy(); - - this.onlineSubscription?.unsubscribe(); this.syncObserver?.off(); } diff --git a/src/core/features/course/classes/main-resource-component.ts b/src/core/features/course/classes/main-resource-component.ts index cc1f72c67..d59380505 100644 --- a/src/core/features/course/classes/main-resource-component.ts +++ b/src/core/features/course/classes/main-resource-component.ts @@ -13,14 +13,10 @@ // 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 { OnInit, OnDestroy, Input, Output, EventEmitter, Component, Optional, Inject } from '@angular/core'; -import { Params } from '@angular/router'; import { CoreAnyError } from '@classes/errors/error'; import { IonRefresher } from '@ionic/angular'; import { CoreApp } from '@services/app'; -import { CoreNavigator } from '@services/navigator'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; @@ -60,20 +56,11 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, 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. - externalUrl?: string; // External URL to open in browser. description?: string; // Module description. - refreshIcon = CoreConstants.ICON_LOADING; // Refresh icon, normally spinner or refresh. - prefetchStatusIcon?: string; // Used when calling fillContextMenu. - prefetchStatus?: string; // Used when calling fillContextMenu. - prefetchText?: string; // Used when calling fillContextMenu. - size?: string; // Used when calling fillContextMenu. - downloadTimeReadable?: string; // Last download time in a readable format. Used when calling fillContextMenu. - isDestroyed = false; // Whether the component is destroyed, used when calling fillContextMenu. - contextMenuStatusObserver?: CoreEventObserver; // Observer of package status, used when calling fillContextMenu. - contextFileStatusObserver?: CoreEventObserver; // Observer of file status, used when calling fillContextMenu. + prefetchStatus?: string; + downloadTimeReadable?: string; // Last download time in a readable format. + isDestroyed = false; // Whether the component is destroyed. protected fetchContentDefaultError = 'core.course.errorgetmodule'; // Default error to show when loading contents. protected isCurrentView = false; // Whether the component is in the current view. @@ -84,6 +71,8 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, protected logger: CoreLogger; protected debouncedUpdateModule?: () => void; // Update the module after a certain time. protected showCompletion = false; // Whether to show completion inside the activity. + protected displayDescription = true; // Wether to show Module description on module page, and not on summary or the contrary. + protected packageStatusObserver?: CoreEventObserver; // Observer of package status. constructor( @Optional() @Inject('') loggerName: string = 'CoreCourseModuleMainResourceComponent', @@ -93,13 +82,12 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, } /** - * Component being initialized. + * @inheritdoc */ async ngOnInit(): Promise { this.siteId = CoreSites.getCurrentSiteId(); this.description = this.module.description; this.componentId = this.module.id; - this.externalUrl = this.module.url; this.courseId = this.courseId || this.module.course; this.showCompletion = !!CoreSites.getRequiredCurrentSite().isVersionGreaterEqualThan('3.11'); @@ -119,14 +107,22 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, }, 10000); } - this.blog = await AddonBlog.isPluginEnabled(); + this.packageStatusObserver = CoreEvents.on( + CoreEvents.PACKAGE_STATUS_CHANGED, + (data) => { + if (data.componentId == module.id && data.component == this.component) { + this.getPackageStatus(); + } + }, + this.siteId, + ); } /** * Refresh the data. * * @param refresher Refresher. - * @param done Function to call when done. + * @param done Function to call when done. Never used. * @param showErrors If show errors to the user of hide them. * @return Promise resolved when done. */ @@ -145,7 +141,6 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, await CoreUtils.ignoreErrors(this.refreshContent(true, showErrors)); refresher?.complete(); - done && done(); } /** @@ -162,22 +157,16 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, return; } - this.refreshIcon = CoreConstants.ICON_LOADING; + await CoreUtils.ignoreErrors(Promise.all([ + this.invalidateContent(), + this.showCompletion ? CoreCourse.invalidateModule(this.module.id) : undefined, + ])); - try { - await CoreUtils.ignoreErrors(Promise.all([ - this.invalidateContent(), - this.showCompletion ? CoreCourse.invalidateModule(this.module.id) : undefined, - ])); - - if (this.showCompletion) { - this.fetchModule(); - } - - await this.loadContent(true); - } finally { - this.refreshIcon = CoreConstants.ICON_REFRESH; + if (this.showCompletion) { + this.fetchModule(); } + + await this.loadContent(true); } /** @@ -214,6 +203,7 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, try { await this.fetchContent(refresh); + await this.getPackageStatus(refresh); } catch (error) { if (!refresh && !CoreSites.getCurrentSite()?.isOfflineDisabled() && this.isNotFoundError(error)) { // Module not found, retry without using cache. @@ -223,7 +213,6 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, CoreDomUtils.showErrorModalDefault(error, this.fetchContentDefaultError, true); } finally { this.loaded = true; - this.refreshIcon = CoreConstants.ICON_REFRESH; } } @@ -238,11 +227,20 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, } /** - * Fill the context menu options + * Updage package status. + * + * @param refresh If prefetch info has to be refreshed. */ - protected fillContextMenu(refresh: boolean = false): void { - // All data obtained, now fill the context menu. - CoreCourseHelper.fillContextMenu(this, this.module, this.courseId, refresh, this.component); + async getPackageStatus(refresh = false): Promise { + if (!this.module) { + return; + } + + const moduleInfo = + await CoreCourseHelper.getModulePrefetchInfo(this.module, this.courseId, refresh, this.component); + + this.downloadTimeReadable = CoreTextUtils.ucFirst(moduleInfo.downloadTimeReadable); + this.prefetchStatus = moduleInfo.status; } /** @@ -253,48 +251,6 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, return this.prefetchStatus != CoreConstants.NOT_DOWNLOADABLE && this.prefetchStatus != CoreConstants.NOT_DOWNLOADED; } - /** - * Expand the description. - * - * @deprecated Use openModuleSummary instead. - */ - expandDescription(): void { - this.openModuleSummary(); - } - - /** - * Go to blog posts. - */ - async gotoBlog(): Promise { - const params: Params = { cmId: this.module.id }; - - await CoreNavigator.navigateToSitePath(AddonBlogMainMenuHandlerService.PAGE_NAME, { params }); - } - - /** - * Prefetch the module. - * - * @param done Function to call when done. - */ - prefetch(done?: () => void): void { - CoreCourseHelper.contextMenuPrefetch(this, this.module, this.courseId, done); - } - - /** - * Confirm and remove downloaded files. - * - * @param done Function to call when done. - */ - removeFiles(done?: () => void): void { - if (this.prefetchStatus == CoreConstants.DOWNLOADING) { - CoreDomUtils.showAlertTranslated(undefined, 'core.course.cannotdeletewhiledownloading'); - - return; - } - - CoreCourseHelper.confirmAndRemoveFiles(this.module, this.courseId, done); - } - /** * Get message about an error occurred while downloading files. * @@ -459,7 +415,7 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, componentProps: { moduleId: this.module.id, module: this.module, - description: this.description, + description: !this.displayDescription ? this.description : '', component: this.component, courseId: this.courseId, hasOffline: this.hasOffline, @@ -493,10 +449,9 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, */ ngOnDestroy(): void { this.isDestroyed = true; - this.contextMenuStatusObserver?.off(); - this.contextFileStatusObserver?.off(); this.statusObserver?.off(); this.completionObserver?.off(); + this.packageStatusObserver?.off(); } /** diff --git a/src/core/features/course/services/course-helper.ts b/src/core/features/course/services/course-helper.ts index 93c8b0ca6..39e917109 100644 --- a/src/core/features/course/services/course-helper.ts +++ b/src/core/features/course/services/course-helper.ts @@ -493,6 +493,7 @@ export class CoreCourseHelperProvider { * @param courseId Course ID the module belongs to. * @param done Function to call when done. It will close the context menu. * @return Promise resolved when done. + * @deprecated since 4.0 */ async confirmAndRemoveFiles(module: CoreCourseModuleData, courseId: number, done?: () => void): Promise { let modal: CoreIonLoadingElement | undefined; @@ -580,6 +581,7 @@ export class CoreCourseHelperProvider { * @param courseId Course ID the module belongs to. * @param done Function to call when done. It will close the context menu. * @return Promise resolved when done. + * @deprecated since 4.0 */ async contextMenuPrefetch( instance: ComponentWithContextMenu, From 16cee9df14d3e78b3f8340cca72bf21df858a2b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Tue, 15 Feb 2022 17:12:37 +0100 Subject: [PATCH 04/12] MOBILE-3931 module: Merge duplicated prefetch statuses --- .../mod/lesson/components/index/index.ts | 8 +- .../index/addon-mod-resource-index.html | 2 +- .../mod/resource/components/index/index.ts | 4 +- .../course/classes/main-resource-component.ts | 43 +++---- .../module-navigation/module-navigation.ts | 2 +- .../features/course/services/course-helper.ts | 112 +++++++++--------- src/theme/theme.light.scss | 3 + 7 files changed, 82 insertions(+), 92 deletions(-) diff --git a/src/addons/mod/lesson/components/index/index.ts b/src/addons/mod/lesson/components/index/index.ts index 8c3a34a4a..c449a9fe9 100644 --- a/src/addons/mod/lesson/components/index/index.ts +++ b/src/addons/mod/lesson/components/index/index.ts @@ -558,13 +558,9 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo } /** - * Displays some data based on the current status. - * - * @param status The current status. - * @param previousStatus The previous status. If not defined, there is no previous status. + * @inheritdoc */ - // eslint-disable-next-line @typescript-eslint/no-unused-vars - protected showStatus(status: string, previousStatus?: string): void { + protected showStatus(status: string): void { this.showSpinner = status == CoreConstants.DOWNLOADING; } diff --git a/src/addons/mod/resource/components/index/addon-mod-resource-index.html b/src/addons/mod/resource/components/index/addon-mod-resource-index.html index c209b8031..a299d9a31 100644 --- a/src/addons/mod/resource/components/index/addon-mod-resource-index.html +++ b/src/addons/mod/resource/components/index/addon-mod-resource-index.html @@ -63,7 +63,7 @@

{{ 'core.lastdownloaded' | translate }}

{{ downloadTimeReadable }}

- + diff --git a/src/addons/mod/resource/components/index/index.ts b/src/addons/mod/resource/components/index/index.ts index 203b2349e..6c26ec16e 100644 --- a/src/addons/mod/resource/components/index/index.ts +++ b/src/addons/mod/resource/components/index/index.ts @@ -125,6 +125,8 @@ export class AddonModResourceIndexComponent extends CoreCourseModuleMainResource this.displayDescription = options.printintro === undefined || !!options.printintro; this.dataRetrieved.emit(resource); + this.setStatusListener(); + if (AddonModResourceHelper.isDisplayedInIframe(this.module)) { const downloadResult = await this.downloadResourceIfNeeded(refresh, true); @@ -202,7 +204,7 @@ export class AddonModResourceIndexComponent extends CoreCourseModuleMainResource downloadable = await AddonModResourceHelper.isMainFileDownloadable(this.module); if (downloadable) { - if (this.prefetchStatus === CoreConstants.OUTDATED && !this.isOnline) { + if (this.currentStatus === CoreConstants.OUTDATED && !this.isOnline) { // Warn the user that the file isn't updated. const alert = await CoreDomUtils.showAlert( undefined, diff --git a/src/core/features/course/classes/main-resource-component.ts b/src/core/features/course/classes/main-resource-component.ts index d59380505..d94f853dd 100644 --- a/src/core/features/course/classes/main-resource-component.ts +++ b/src/core/features/course/classes/main-resource-component.ts @@ -58,21 +58,20 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, hasOffline = false; // Resources don't have any data to sync. description?: string; // Module description. - prefetchStatus?: string; - downloadTimeReadable?: string; // Last download time in a readable format. isDestroyed = false; // Whether the component is destroyed. protected fetchContentDefaultError = 'core.course.errorgetmodule'; // Default error to show when loading contents. protected isCurrentView = false; // Whether the component is in the current view. protected siteId?: string; // Current Site ID. protected statusObserver?: CoreEventObserver; // Observer of package status. Only if setStatusListener is called. - protected currentStatus?: string; // The current status of the module. Only if setStatusListener is called. + currentStatus?: string; // The current status of the module. Only if setStatusListener is called. + downloadTimeReadable?: string; // Last download time in a readable format. Only if setStatusListener is called. + protected completionObserver?: CoreEventObserver; protected logger: CoreLogger; protected debouncedUpdateModule?: () => void; // Update the module after a certain time. protected showCompletion = false; // Whether to show completion inside the activity. protected displayDescription = true; // Wether to show Module description on module page, and not on summary or the contrary. - protected packageStatusObserver?: CoreEventObserver; // Observer of package status. constructor( @Optional() @Inject('') loggerName: string = 'CoreCourseModuleMainResourceComponent', @@ -106,16 +105,6 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, this.fetchModule(); }, 10000); } - - this.packageStatusObserver = CoreEvents.on( - CoreEvents.PACKAGE_STATUS_CHANGED, - (data) => { - if (data.componentId == module.id && data.component == this.component) { - this.getPackageStatus(); - } - }, - this.siteId, - ); } /** @@ -203,7 +192,6 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, try { await this.fetchContent(refresh); - await this.getPackageStatus(refresh); } catch (error) { if (!refresh && !CoreSites.getCurrentSite()?.isOfflineDisabled() && this.isNotFoundError(error)) { // Module not found, retry without using cache. @@ -227,28 +215,25 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, } /** - * Updage package status. - * - * @param refresh If prefetch info has to be refreshed. + * Updage package last downloaded. */ - async getPackageStatus(refresh = false): Promise { + protected async getPackageLastDownloaded(): Promise { if (!this.module) { return; } - const moduleInfo = - await CoreCourseHelper.getModulePrefetchInfo(this.module, this.courseId, refresh, this.component); + const lastDownloaded = + await CoreCourseHelper.getModulePackageLastDownloaded(this.module, this.component); - this.downloadTimeReadable = CoreTextUtils.ucFirst(moduleInfo.downloadTimeReadable); - this.prefetchStatus = moduleInfo.status; + this.downloadTimeReadable = lastDownloaded.downloadTimeReadable; } /** - * Check if the module is prefetched or being prefetched. To make it faster, just use the data calculated by fillContextMenu. - * This means that you need to call fillContextMenu to make this work. + * Check if the module is prefetched or being prefetched. + * To make it faster, just use the data calculated by setStatusListener. */ protected isPrefetched(): boolean { - return this.prefetchStatus != CoreConstants.NOT_DOWNLOADABLE && this.prefetchStatus != CoreConstants.NOT_DOWNLOADED; + return this.currentStatus != CoreConstants.NOT_DOWNLOADABLE && this.currentStatus != CoreConstants.NOT_DOWNLOADED; } /** @@ -308,6 +293,8 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, const previousStatus = this.currentStatus; this.currentStatus = data.status; + this.getPackageLastDownloaded(); + this.showStatus(this.currentStatus, previousStatus); }, this.siteId); } else if (!refresh) { @@ -322,6 +309,9 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, const status = await CoreCourseModulePrefetchDelegate.getModuleStatus(this.module, this.courseId, undefined, refresh); this.currentStatus = status; + + this.getPackageLastDownloaded(); + this.showStatus(status); } @@ -451,7 +441,6 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, this.isDestroyed = true; this.statusObserver?.off(); this.completionObserver?.off(); - this.packageStatusObserver?.off(); } /** diff --git a/src/core/features/course/components/module-navigation/module-navigation.ts b/src/core/features/course/components/module-navigation/module-navigation.ts index 2b3573738..3a566c701 100644 --- a/src/core/features/course/components/module-navigation/module-navigation.ts +++ b/src/core/features/course/components/module-navigation/module-navigation.ts @@ -110,7 +110,7 @@ export class CoreCourseModuleNavigationComponent implements OnInit, OnDestroy { return; } // Set a minimum height value. - this.initialHeight = this.initialHeight || 56; + this.initialHeight = this.initialHeight || 48; this.previousHeight = this.initialHeight; this.content = this.element.closest('ion-content'); diff --git a/src/core/features/course/services/course-helper.ts b/src/core/features/course/services/course-helper.ts index 39e917109..77aab7baa 100644 --- a/src/core/features/course/services/course-helper.ts +++ b/src/core/features/course/services/course-helper.ts @@ -75,36 +75,19 @@ import { CoreStatusWithWarningsWSResponse } from '@services/ws'; /** * Prefetch info of a module. */ -export type CoreCourseModulePrefetchInfo = { - /** - * Downloaded size. - */ - size: number; +export type CoreCourseModulePrefetchInfo = CoreCourseModulePackageLastDownloaded & { + size: number; // Downloaded size. + sizeReadable: string; // Downloadable size in a readable format. + status: string; // Module status. + statusIcon?: string; // Icon's name of the module status. +}; - /** - * Downloadable size in a readable format. - */ - sizeReadable: string; - - /** - * Module status. - */ - status: string; - - /** - * Icon's name of the module status. - */ - statusIcon?: string; - - /** - * Time when the module was last downloaded. - */ - downloadTime: number; - - /** - * Download time in a readable format. - */ - downloadTimeReadable: string; +/** + * Prefetch info of a module. + */ +export type CoreCourseModulePackageLastDownloaded = { + downloadTime: number; // Time when the module was last downloaded. + downloadTimeReadable: string; // Download time in a readable format. }; /** @@ -1484,12 +1467,9 @@ export class CoreCourseHelperProvider { async getModulePrefetchInfo( module: CoreCourseModuleData, courseId: number, - invalidateCache?: boolean, - component?: string, + invalidateCache = false, + component = '', ): Promise { - - const siteId = CoreSites.getCurrentSiteId(); - if (invalidateCache) { // Currently, some modules pass invalidateCache=false because they already invalidate data in downloadResourceIfNeeded. // If this function is changed to do more actions if invalidateCache=true, please review those modules. @@ -1501,7 +1481,7 @@ export class CoreCourseHelperProvider { const results = await Promise.all([ CoreCourseModulePrefetchDelegate.getModuleStoredSize(module, courseId), CoreCourseModulePrefetchDelegate.getModuleStatus(module, courseId), - CoreUtils.ignoreErrors(CoreFilepool.getPackageData(siteId, component || '', module.id)), + this.getModulePackageLastDownloaded(module, component), ]); // Treat stored size. @@ -1528,33 +1508,53 @@ export class CoreCourseHelperProvider { break; } - // Treat download time. - if (!results[2] || !results[2].downloadTime || !CoreFileHelper.isStateDownloaded(results[2].status || '')) { - // Not downloaded. - return { - size, - sizeReadable, - status, - statusIcon, - downloadTime: 0, - downloadTimeReadable: '', - }; - } - - const now = CoreTimeUtils.timestamp(); - const downloadTime = results[2].downloadTime; - let downloadTimeReadable = ''; - if (now - results[2].downloadTime < 7 * 86400) { - downloadTimeReadable = moment(results[2].downloadTime * 1000).fromNow(); - } else { - downloadTimeReadable = moment(results[2].downloadTime * 1000).calendar(); - } + const packageData = results[2]; return { size, sizeReadable, status, statusIcon, + downloadTime: packageData.downloadTime, + downloadTimeReadable: packageData.downloadTimeReadable, + }; + } + + /** + * Get prefetch info for a module. + * + * @param module Module to get the info from. + * @param component Component of the module. + * @return Promise resolved with the info. + */ + async getModulePackageLastDownloaded( + module: CoreCourseModuleData, + component = '', + ): Promise { + const siteId = CoreSites.getCurrentSiteId(); + const packageData = await CoreUtils.ignoreErrors(CoreFilepool.getPackageData(siteId, component, module.id)); + + // Treat download time. + if (!packageData || !packageData.downloadTime || !CoreFileHelper.isStateDownloaded(packageData.status || '')) { + // Not downloaded. + return { + downloadTime: 0, + downloadTimeReadable: '', + }; + } + + const now = CoreTimeUtils.timestamp(); + const downloadTime = packageData.downloadTime; + let downloadTimeReadable = ''; + if (now - downloadTime < 7 * 86400) { + downloadTimeReadable = moment(downloadTime * 1000).fromNow(); + } else { + downloadTimeReadable = moment(downloadTime * 1000).calendar(); + } + + downloadTimeReadable = CoreTextUtils.ucFirst(downloadTimeReadable); + + return { downloadTime, downloadTimeReadable, }; diff --git a/src/theme/theme.light.scss b/src/theme/theme.light.scss index 6f2e2b837..edcdad2f9 100644 --- a/src/theme/theme.light.scss +++ b/src/theme/theme.light.scss @@ -233,6 +233,9 @@ } --core-loading-spinner: var(--brand); + ion-loading { + --spinner-color: var(--core-loading-spinner); + } ion-spinner, ion-refresher { --ion-color-base: var(--core-loading-spinner); --ion-color-primary: var(--core-loading-spinner); From 6c782e3b3eaaaeb9e3f222746f9212db45c87164 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Wed, 16 Feb 2022 15:32:09 +0100 Subject: [PATCH 05/12] MOBILE-3931 module: Adapt module site plugins code --- .../course/classes/main-resource-component.ts | 15 +- .../module-summary/module-summary.html | 14 +- .../module-summary/module-summary.ts | 21 ++- .../features/course/services/course-helper.ts | 139 +---------------- .../course/services/module-delegate.ts | 4 +- .../core-siteplugins-module-index.html | 26 +--- .../components/module-index/module-index.ts | 140 +++++++++++++----- 7 files changed, 145 insertions(+), 214 deletions(-) diff --git a/src/core/features/course/classes/main-resource-component.ts b/src/core/features/course/classes/main-resource-component.ts index d94f853dd..edbcd1680 100644 --- a/src/core/features/course/classes/main-resource-component.ts +++ b/src/core/features/course/classes/main-resource-component.ts @@ -58,7 +58,6 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, hasOffline = false; // Resources don't have any data to sync. description?: string; // Module description. - isDestroyed = false; // Whether the component is destroyed. protected fetchContentDefaultError = 'core.course.errorgetmodule'; // Default error to show when loading contents. protected isCurrentView = false; // Whether the component is in the current view. @@ -72,6 +71,7 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, protected debouncedUpdateModule?: () => void; // Update the module after a certain time. protected showCompletion = false; // Whether to show completion inside the activity. protected displayDescription = true; // Wether to show Module description on module page, and not on summary or the contrary. + protected isDestroyed = false; // Whether the component is destroyed. constructor( @Optional() @Inject('') loggerName: string = 'CoreCourseModuleMainResourceComponent', @@ -111,11 +111,10 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, * Refresh the data. * * @param refresher Refresher. - * @param done Function to call when done. Never used. * @param showErrors If show errors to the user of hide them. * @return Promise resolved when done. */ - async doRefresh(refresher?: IonRefresher | null, done?: () => void, showErrors: boolean = false): Promise { + async doRefresh(refresher?: IonRefresher | null, showErrors = false): Promise { if (!this.loaded || !this.module) { // Module can be undefined if course format changes from single activity to weekly/topics. return; @@ -405,10 +404,14 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, componentProps: { moduleId: this.module.id, module: this.module, - description: !this.displayDescription ? this.description : '', + description: this.description, component: this.component, courseId: this.courseId, hasOffline: this.hasOffline, + displayOptions: { + // Show description on summary if not shown on the page. + displayDescription: !this.displayDescription, + }, }, }); @@ -421,11 +424,11 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, } finally { modal.dismiss(); } - } else if(data.action == 'sync') { + } else if (data.action == 'sync') { const modal = await CoreDomUtils.showModalLoading(); try { - await this.doRefresh( undefined, undefined, true); + await this.doRefresh( undefined, true); } finally { modal.dismiss(); } diff --git a/src/core/features/course/components/module-summary/module-summary.html b/src/core/features/course/components/module-summary/module-summary.html index 26d20c8b6..7830b0baf 100644 --- a/src/core/features/course/components/module-summary/module-summary.html +++ b/src/core/features/course/components/module-summary/module-summary.html @@ -19,12 +19,12 @@
- + - + @@ -32,7 +32,7 @@ - +

{{ prefetchText }}

{{ downloadTimeReadable }}

@@ -45,7 +45,7 @@
- +

{{ 'addon.storagemanager.totalspaceusage' | translate }}

{{ sizeReadable | coreBytesToSize }} @@ -57,7 +57,7 @@
- + {{ 'addon.blog.blog' | translate }} @@ -65,7 +65,7 @@
- + diff --git a/src/core/features/course/components/module-summary/module-summary.ts b/src/core/features/course/components/module-summary/module-summary.ts index 58d159945..3475ca44f 100644 --- a/src/core/features/course/components/module-summary/module-summary.ts +++ b/src/core/features/course/components/module-summary/module-summary.ts @@ -46,6 +46,7 @@ export class CoreCourseModuleSummaryComponent implements OnInit, OnDestroy { @Input() component = ''; // Component name. @Input() description = ''; // Module description. @Input() hasOffline = false; // If it has offline data to be synced. + @Input() displayOptions: CoreCourseModuleSummaryDisplayOptions = {}; loaded = false; // If the component has been loaded. componentId?: number; // Component ID. @@ -95,6 +96,15 @@ export class CoreCourseModuleSummaryComponent implements OnInit, OnDestroy { return; } + this.displayOptions = Object.assign({ + displayOpenInBrowser: true, + displayDescription: true, + displayRefresh: true, + displayPrefetch: true, + displaySize: true, + displayBlog: true, + }, this.displayOptions); + this.fetchContent(); if (this.component) { @@ -306,6 +316,15 @@ export class CoreCourseModuleSummaryComponent implements OnInit, OnDestroy { } -export type CoreCourseModuleSummaryResult = { +export type CoreCourseModuleSummaryResult = { action: 'sync'|'refresh'; }; + +export type CoreCourseModuleSummaryDisplayOptions = { + displayOpenInBrowser?: boolean; + displayDescription?: boolean; + displayRefresh?: boolean; + displayPrefetch?: boolean; + displaySize?: boolean; + displayBlog?: boolean; +}; diff --git a/src/core/features/course/services/course-helper.ts b/src/core/features/course/services/course-helper.ts index 77aab7baa..4c807d5e1 100644 --- a/src/core/features/course/services/course-helper.ts +++ b/src/core/features/course/services/course-helper.ts @@ -64,7 +64,6 @@ import { CoreFile } from '@services/file'; import { CoreUrlUtils } from '@services/utils/url'; import { CoreTextUtils } from '@services/utils/text'; import { CoreTimeUtils } from '@services/utils/time'; -import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreFilterHelper } from '@features/filter/services/filter-helper'; import { CoreNetworkError } from '@classes/errors/network-error'; import { CoreSiteHome } from '@features/sitehome/services/sitehome'; @@ -474,23 +473,18 @@ export class CoreCourseHelperProvider { * * @param module Module to remove the files. * @param courseId Course ID the module belongs to. - * @param done Function to call when done. It will close the context menu. * @return Promise resolved when done. * @deprecated since 4.0 */ - async confirmAndRemoveFiles(module: CoreCourseModuleData, courseId: number, done?: () => void): Promise { + async confirmAndRemoveFiles(module: CoreCourseModuleData, courseId: number): Promise { let modal: CoreIonLoadingElement | undefined; try { - await CoreDomUtils.showDeleteConfirm('addon.storagemanager.confirmdeletedatafrom', { name: module.name }); modal = await CoreDomUtils.showModalLoading(); await this.removeModuleStoredData(module, courseId); - - done && done(); - } catch (error) { if (error) { CoreDomUtils.showErrorModal(error); @@ -555,45 +549,6 @@ export class CoreCourseHelperProvider { await CoreDomUtils.confirmDownloadSize(sizeSum, undefined, undefined, undefined, undefined, alwaysConfirm); } - /** - * Helper function to prefetch a module, showing a confirmation modal if the size is big. - * This function is meant to be called from a context menu option. It will also modify some data like the prefetch icon. - * - * @param instance The component instance that has the context menu. - * @param module Module to be prefetched - * @param courseId Course ID the module belongs to. - * @param done Function to call when done. It will close the context menu. - * @return Promise resolved when done. - * @deprecated since 4.0 - */ - async contextMenuPrefetch( - instance: ComponentWithContextMenu, - module: CoreCourseModuleData, - courseId: number, - done?: () => void, - ): Promise { - const initialIcon = instance.prefetchStatusIcon; - instance.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(module, courseId, true); - - await CoreDomUtils.confirmDownloadSize(size); - - await CoreCourseModulePrefetchDelegate.prefetchModule(module, courseId, true); - - // Success, close menu. - done && done(); - } catch (error) { - instance.prefetchStatusIcon = initialIcon; - - if (!instance.isDestroyed) { - CoreDomUtils.showErrorModalDefault(error, 'core.errordownloading', true); - } - } - } - /** * Check whether a course is accessed using guest access. * @@ -1030,87 +985,6 @@ export class CoreCourseHelperProvider { await CoreFilepool.downloadOrPrefetchFiles(siteId, files, false, false, component, componentId); } - /** - * Fill the Context Menu for a certain module. - * - * @param instance The component instance that has the context menu. - * @param module Module to be prefetched - * @param courseId Course ID the module belongs to. - * @param invalidateCache Invalidates the cache first. - * @param component Component of the module. - * @return Promise resolved when done. - */ - async fillContextMenu( - instance: ComponentWithContextMenu, - module: CoreCourseModuleData, - courseId: number, - invalidateCache?: boolean, - component?: string, - ): Promise { - const siteId = CoreSites.getCurrentSiteId(); - - const moduleInfo = await this.getModulePrefetchInfo(module, courseId, invalidateCache, component); - - instance.size = moduleInfo.sizeReadable; - instance.prefetchStatusIcon = moduleInfo.statusIcon; - instance.prefetchStatus = moduleInfo.status; - instance.downloadTimeReadable = CoreTextUtils.ucFirst(moduleInfo.downloadTimeReadable); - - if (moduleInfo.status != CoreConstants.NOT_DOWNLOADABLE) { - // Module is downloadable, get the text to display to prefetch. - if (moduleInfo.downloadTime && moduleInfo.downloadTime > 0) { - instance.prefetchText = Translate.instant('core.lastdownloaded') + ': ' + moduleInfo.downloadTimeReadable; - } else { - // Module not downloaded, show a default text. - instance.prefetchText = Translate.instant('core.download'); - } - } - - if (moduleInfo.status == CoreConstants.DOWNLOADING) { - // Set this to empty to prevent "remove file" option showing up while downloading. - instance.size = ''; - } - - if (!instance.contextMenuStatusObserver && component) { - instance.contextMenuStatusObserver = CoreEvents.on( - CoreEvents.PACKAGE_STATUS_CHANGED, - (data) => { - if (data.componentId == module.id && data.component == component) { - this.fillContextMenu(instance, module, courseId, false, component); - } - }, - siteId, - ); - } - - if (!instance.contextFileStatusObserver && component) { - // Debounce the update size function to prevent too many calls when downloading or deleting a whole activity. - const debouncedUpdateSize = CoreUtils.debounce(async () => { - const moduleSize = await CoreCourseModulePrefetchDelegate.getModuleStoredSize(module, courseId); - - instance.size = moduleSize > 0 ? CoreTextUtils.bytesToSize(moduleSize, 2) : ''; - }, 1000); - - instance.contextFileStatusObserver = CoreEvents.on( - CoreEvents.COMPONENT_FILE_ACTION, - (data) => { - if (data.component != 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(); - }, - siteId, - ); - } - } - /** * Get a course. It will first check the user courses, and fallback to another WS if not enrolled. * @@ -2225,14 +2099,3 @@ export type CoreCourseOpenModuleOptions = { sectionId?: number; // Section the module belongs to. modNavOptions?: CoreNavigationOptions; // Navigation options to open the module, including params to pass to the module. }; - -type ComponentWithContextMenu = { - prefetchStatusIcon?: string; - isDestroyed?: boolean; - size?: string; - prefetchStatus?: string; - prefetchText?: string; - downloadTimeReadable?: string; - contextMenuStatusObserver?: CoreEventObserver; - contextFileStatusObserver?: CoreEventObserver; -}; diff --git a/src/core/features/course/services/module-delegate.ts b/src/core/features/course/services/module-delegate.ts index d32c1be3e..6c9a73055 100644 --- a/src/core/features/course/services/module-delegate.ts +++ b/src/core/features/course/services/module-delegate.ts @@ -202,10 +202,10 @@ export interface CoreCourseModuleMainComponent { * Refresh the data. * * @param refresher Refresher. - * @param done Function to call when done. + * @param showErrors If show errors to the user of hide them. * @return Promise resolved when done. */ - doRefresh(refresher?: IonRefresher, done?: () => void): Promise; + doRefresh(refresher?: IonRefresher | null, showErrors?: boolean): Promise; } /** diff --git a/src/core/features/siteplugins/components/module-index/core-siteplugins-module-index.html b/src/core/features/siteplugins/components/module-index/core-siteplugins-module-index.html index 89dfe24d8..d4f915a10 100644 --- a/src/core/features/siteplugins/components/module-index/core-siteplugins-module-index.html +++ b/src/core/features/siteplugins/components/module-index/core-siteplugins-module-index.html @@ -1,28 +1,8 @@ - - - - - - - - - - - - + + + ; initResult?: CoreSitePluginsContent | null; preSets?: CoreSiteWSPreSets; - - // Data for context menu. - externalUrl?: string; description?: string; - refreshIcon?: string; + + /** + * @deprecated since 4.0, use module.url instead. + */ + externalUrl?: string; + /** + * @deprecated since 4.0. It won't be populated anymore. + */ + refreshIcon = CoreConstants.ICON_REFRESH; + /** + * @deprecated since 4.0.. It won't be populated anymore. + */ prefetchStatus?: string; + /** + * @deprecated since 4.0. It won't be populated anymore. + */ prefetchStatusIcon?: string; + /** + * @deprecated since 4.0. It won't be populated anymore. + */ prefetchText?: string; + /** + * @deprecated since 4.0. It won't be populated anymore. + */ size?: string; - contextMenuStatusObserver?: CoreEventObserver; - contextFileStatusObserver?: CoreEventObserver; + displayOpenInBrowser = true; displayDescription = true; displayRefresh = true; displayPrefetch = true; displaySize = true; + ptrEnabled = true; isDestroyed = false; @@ -80,8 +100,6 @@ export class CoreSitePluginsModuleIndexComponent implements OnInit, OnDestroy, C * Component being initialized. */ ngOnInit(): void { - this.refreshIcon = CoreConstants.ICON_LOADING; - if (!this.module) { return; } @@ -122,71 +140,119 @@ export class CoreSitePluginsModuleIndexComponent implements OnInit, OnDestroy, C * Refresh the data. * * @param refresher Refresher. - * @param done Function to call when done. * @return Promise resolved when done. */ - async doRefresh(refresher?: IonRefresher | null, done?: () => void): Promise { - if (this.content) { - this.refreshIcon = CoreConstants.ICON_LOADING; - } - + async doRefresh(refresher?: IonRefresher | null): Promise { try { await this.content?.refreshContent(false); } finally { refresher?.complete(); - done && done(); } } /** * Function called when the data of the site plugin content is loaded. */ + // eslint-disable-next-line @typescript-eslint/no-unused-vars contentLoaded(refresh: boolean): void { - this.refreshIcon = CoreConstants.ICON_REFRESH; - - // Check if there is a prefetch handler for this type of module. - if (CoreCourseModulePrefetchDelegate.getPrefetchHandlerFor(this.module.modname)) { - CoreCourseHelper.fillContextMenu(this, this.module, this.courseId, refresh, this.component); - } + return; } /** * Function called when starting to load the data of the site plugin content. */ contentLoading(): void { - this.refreshIcon = CoreConstants.ICON_LOADING; + return; } /** * Expand the description. + * + * @deprecated since 4.0 */ expandDescription(): void { - if (!this.description) { + this.openModuleSummary(); + } + + /** + * Opens a module summary page. + */ + async openModuleSummary(): Promise { + if (!this.module) { return; } - 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, + const data = await CoreDomUtils.openSideModal({ + component: CoreCourseModuleSummaryComponent, + componentProps: { + moduleId: this.module.id, + module: this.module, + description: this.description, + component: this.component, + courseId: this.courseId, + displayOptions: { + displayOpenInBrowser: this.displayOpenInBrowser, + displayDescription: this.displayDescription, + displayRefresh: this.displayRefresh, + displayPrefetch: this.displayPrefetch, + displaySize: this.displaySize, + displayBlog: false, + }, + }, }); + + if (data && data.action == 'refresh') { + const modal = await CoreDomUtils.showModalLoading(); + + try { + await this.doRefresh(); + } finally { + modal.dismiss(); + } + } } /** * Prefetch the module. + * + * @deprecated since 4.0 */ - prefetch(): void { - CoreCourseHelper.contextMenuPrefetch(this, this.module, this.courseId); + async prefetch(): Promise { + 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); + } catch (error) { + if (!this.isDestroyed) { + CoreDomUtils.showErrorModalDefault(error, 'core.errordownloading', true); + } + } } /** * Confirm and remove downloaded files. + * + * @deprecated since 4.0 */ - removeFiles(): void { - CoreCourseHelper.confirmAndRemoveFiles(this.module, this.courseId); + async removeFiles(): Promise { + let modal: CoreIonLoadingElement | undefined; + + try { + await CoreDomUtils.showDeleteConfirm('addon.storagemanager.confirmdeletedatafrom', { name: this.module.name }); + + modal = await CoreDomUtils.showModalLoading(); + + await CoreCourseHelper.removeModuleStoredData(this.module, this.courseId); + } catch (error) { + if (error) { + CoreDomUtils.showErrorModal(error); + } + } finally { + modal?.dismiss(); + } } /** From 6dcd37234fddedc26670a08d549f1cda9928eedc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Wed, 16 Feb 2022 17:02:18 +0100 Subject: [PATCH 06/12] MOBILE-3931 module: Remove duplicated context menu items --- src/addons/mod/forum/components/index/index.html | 6 ------ .../components/index/addon-mod-glossary-index.html | 6 ------ .../course/pages/module-preview/module-preview.html | 9 ++++----- 3 files changed, 4 insertions(+), 17 deletions(-) diff --git a/src/addons/mod/forum/components/index/index.html b/src/addons/mod/forum/components/index/index.html index b16a7b9d4..f6d887a7f 100644 --- a/src/addons/mod/forum/components/index/index.html +++ b/src/addons/mod/forum/components/index/index.html @@ -1,11 +1,5 @@ - - - - - diff --git a/src/addons/mod/glossary/components/index/addon-mod-glossary-index.html b/src/addons/mod/glossary/components/index/addon-mod-glossary-index.html index 2341c36e7..05414fc94 100644 --- a/src/addons/mod/glossary/components/index/addon-mod-glossary-index.html +++ b/src/addons/mod/glossary/components/index/addon-mod-glossary-index.html @@ -9,12 +9,6 @@ - - - - - diff --git a/src/core/features/course/pages/module-preview/module-preview.html b/src/core/features/course/pages/module-preview/module-preview.html index 3fceb9d64..eb42c4b54 100644 --- a/src/core/features/course/pages/module-preview/module-preview.html +++ b/src/core/features/course/pages/module-preview/module-preview.html @@ -11,11 +11,10 @@ - - - - + + +
From 71788e83bf8f43c7c37779d7f734219e012d413d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Wed, 16 Feb 2022 21:21:33 +0100 Subject: [PATCH 07/12] MOBILE-3931 grades: Fix navigate to gradeId --- src/core/features/grades/grades.module.ts | 4 +-- .../features/grades/pages/course/course.html | 36 +------------------ .../grades/pages/course/course.page.ts | 34 +++++++++++++++--- .../features/grades/services/grades-helper.ts | 12 ++++--- src/core/features/grades/services/grades.ts | 2 +- .../grades/services/handlers/overview-link.ts | 2 +- .../features/grades/services/handlers/user.ts | 2 +- 7 files changed, 42 insertions(+), 50 deletions(-) diff --git a/src/core/features/grades/grades.module.ts b/src/core/features/grades/grades.module.ts index 6669a9161..8935c6ee8 100644 --- a/src/core/features/grades/grades.module.ts +++ b/src/core/features/grades/grades.module.ts @@ -22,7 +22,7 @@ import { CoreMainMenuTabRoutingModule } from '@features/mainmenu/mainmenu-tab-ro import { CoreUserDelegate } from '@features/user/services/user-delegate'; import { PARTICIPANTS_PAGE_NAME } from '@features/user/user.module'; import { CoreGradesProvider } from './services/grades'; -import { CoreGradesHelperProvider } from './services/grades-helper'; +import { CoreGradesHelperProvider, GRADES_PAGE_NAME } from './services/grades-helper'; import { CoreGradesCourseOptionHandler } from './services/handlers/course-option'; import { CoreGradesOverviewLinkHandler } from './services/handlers/overview-link'; import { CoreGradesUserHandler } from './services/handlers/user'; @@ -33,8 +33,6 @@ export const CORE_GRADES_SERVICES: Type[] = [ CoreGradesHelperProvider, ]; -export const GRADES_PAGE_NAME = 'grades'; - const mainMenuChildrenRoutes: Routes = [ { path: GRADES_PAGE_NAME, diff --git a/src/core/features/grades/pages/course/course.html b/src/core/features/grades/pages/course/course.html index c484de9f0..6cbbae4e4 100644 --- a/src/core/features/grades/pages/course/course.html +++ b/src/core/features/grades/pages/course/course.html @@ -31,7 +31,7 @@ [attr.tabindex]="row.expandable && showSummary && 0" [attr.aria-expanded]="row.expanded" [attr.aria-label]="rowAriaLabel(row)" [attr.aria-controls]="row.detailsid" (ariaButtonClick)="row.expandable && showSummary && toggleRow(row)" [class]="row.rowclass" - [class.core-grades-grade-clickable]="row.expandable && showSummary"> + [class.core-grades-grade-clickable]="row.expandable && showSummary" [id]="'grade-'+row.id"> @@ -71,40 +71,6 @@ - - - - - - - -

- - -

-
-
- - - - - - - - -

- - -

-
-
-

{{ 'core.grades.weight' | translate}}

diff --git a/src/core/features/grades/pages/course/course.page.ts b/src/core/features/grades/pages/course/course.page.ts index fe22fc527..22fadfa71 100644 --- a/src/core/features/grades/pages/course/course.page.ts +++ b/src/core/features/grades/pages/course/course.page.ts @@ -13,8 +13,8 @@ // limitations under the License. import { ActivatedRoute } from '@angular/router'; -import { AfterViewInit, Component, ElementRef, OnDestroy } from '@angular/core'; -import { IonRefresher } from '@ionic/angular'; +import { AfterViewInit, Component, ElementRef, OnDestroy, Optional } from '@angular/core'; +import { IonContent, IonRefresher } from '@ionic/angular'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreGrades } from '@features/grades/services/grades'; @@ -44,6 +44,7 @@ export class CoreGradesCoursePage implements AfterViewInit, OnDestroy { courseId!: number; userId!: number; + gradeId?: number; expandLabel!: string; collapseLabel!: string; title?: string; @@ -53,10 +54,16 @@ export class CoreGradesCoursePage implements AfterViewInit, OnDestroy { totalColumnsSpan?: number; withinSplitView?: boolean; - constructor(protected route: ActivatedRoute, protected element: ElementRef) { + constructor( + protected route: ActivatedRoute, + protected element: ElementRef, + @Optional() protected content?: IonContent, + ) { try { this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId', { route }); this.userId = CoreNavigator.getRouteNumberParam('userId', { route }) ?? CoreSites.getCurrentSiteUserId(); + this.gradeId = CoreNavigator.getRouteNumberParam('gradeId', { route }); + this.expandLabel = Translate.instant('core.expand'); this.collapseLabel = Translate.instant('core.collapse'); @@ -116,13 +123,14 @@ export class CoreGradesCoursePage implements AfterViewInit, OnDestroy { * Toggle whether a row is expanded or collapsed. * * @param row Row. + * @param expand If defined, force expand or collapse. */ - toggleRow(row: CoreGradesFormattedTableRow): void { + toggleRow(row: CoreGradesFormattedTableRow, expand?: boolean): void { if (!this.rows || !this.columns) { return; } - row.expanded = !row.expanded; + row.expanded = expand ?? !row.expanded; let colspan: number = this.columns.length + (row.colspan ?? 0) - 1; for (let i = this.rows.indexOf(row) - 1; i >= 0; i--) { @@ -155,6 +163,22 @@ export class CoreGradesCoursePage implements AfterViewInit, OnDestroy { private async fetchInitialGrades(): Promise { try { await this.fetchGrades(); + + if (this.gradeId && this.rows) { + const row = this.rows.find((row) => row.id == this.gradeId); + + if (row) { + this.toggleRow(row, true); + await CoreUtils.nextTick(); + + CoreDomUtils.scrollToElementBySelector( + this.element.nativeElement, + this.content, + '#grade-' + row.id, + ); + this.gradeId = undefined; + } + } } catch (error) { CoreDomUtils.showErrorModalDefault(error, 'Error loading course'); diff --git a/src/core/features/grades/services/grades-helper.ts b/src/core/features/grades/services/grades-helper.ts index c343c2c71..942f46895 100644 --- a/src/core/features/grades/services/grades-helper.ts +++ b/src/core/features/grades/services/grades-helper.ts @@ -35,7 +35,8 @@ import { CoreNavigator } from '@services/navigator'; import { makeSingleton, Translate } from '@singletons'; import { CoreError } from '@classes/errors/error'; import { CoreCourseHelper } from '@features/course/services/course-helper'; -import { GRADES_PAGE_NAME } from '../grades.module'; + +export const GRADES_PAGE_NAME = 'grades'; /** * Service that provides some features regarding grades information. @@ -497,9 +498,12 @@ export class CoreGradesHelperProvider { const gradeId = item.id; await CoreUtils.ignoreErrors( - CoreNavigator.navigateToSitePath(`/${GRADES_PAGE_NAME}/${courseId}/${gradeId}`, { siteId }), + CoreNavigator.navigateToSitePath( + `/${GRADES_PAGE_NAME}/${courseId}`, + { params: { gradeId }, siteId }, + ), ); - } catch (error) { + } catch { try { // Cannot get grade items or there's no need to. if (userId && userId != currentUserId) { @@ -519,7 +523,7 @@ export class CoreGradesHelperProvider { // Open the course with the grades tab selected. await CoreCourseHelper.getAndOpenCourse(courseId, { selectedTab: 'CoreGrades' }, siteId); - } catch (error) { + } catch { // Cannot get course for some reason, just open the grades page. await CoreNavigator.navigateToSitePath(`/${GRADES_PAGE_NAME}/${courseId}`, { siteId }); } diff --git a/src/core/features/grades/services/grades.ts b/src/core/features/grades/services/grades.ts index 80b588e8a..c7aa1aa7c 100644 --- a/src/core/features/grades/services/grades.ts +++ b/src/core/features/grades/services/grades.ts @@ -199,7 +199,7 @@ export class CoreGradesProvider { const table = await site.read('gradereport_user_get_grades_table', params, preSets); if (!table?.tables?.[0]) { - throw new CoreError('Coudln\'t get course grades table'); + throw new CoreError('Couldn\'t get course grades table'); } return table.tables[0]; diff --git a/src/core/features/grades/services/handlers/overview-link.ts b/src/core/features/grades/services/handlers/overview-link.ts index 241e2fc19..2205e29ea 100644 --- a/src/core/features/grades/services/handlers/overview-link.ts +++ b/src/core/features/grades/services/handlers/overview-link.ts @@ -15,10 +15,10 @@ import { Injectable } from '@angular/core'; import { CoreContentLinksHandlerBase } from '@features/contentlinks/classes/base-handler'; import { CoreContentLinksAction } from '@features/contentlinks/services/contentlinks-delegate'; -import { GRADES_PAGE_NAME } from '@features/grades/grades.module'; import { CoreNavigator } from '@services/navigator'; import { makeSingleton } from '@singletons'; import { CoreGrades } from '../grades'; +import { GRADES_PAGE_NAME } from '../grades-helper'; /** * Handler to treat links to overview courses grades. diff --git a/src/core/features/grades/services/handlers/user.ts b/src/core/features/grades/services/handlers/user.ts index 945972f4e..ade60adf5 100644 --- a/src/core/features/grades/services/handlers/user.ts +++ b/src/core/features/grades/services/handlers/user.ts @@ -14,7 +14,6 @@ import { Injectable } from '@angular/core'; import { COURSE_PAGE_NAME } from '@features/course/course.module'; -import { GRADES_PAGE_NAME } from '@features/grades/grades.module'; import { CoreGrades } from '@features/grades/services/grades'; import { CoreUserProfile } from '@features/user/services/user'; @@ -29,6 +28,7 @@ import { CoreNavigator } from '@services/navigator'; import { CoreSites } from '@services/sites'; import { CoreUtils } from '@services/utils/utils'; import { makeSingleton } from '@singletons'; +import { GRADES_PAGE_NAME } from '../grades-helper'; /** * Profile grades handler. From 6a862730e2b71220eb42c6ae7762a04c00011fd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Wed, 16 Feb 2022 21:30:05 +0100 Subject: [PATCH 08/12] MOBILE-3931 module: Add grades and course to module summary --- scripts/langindex.json | 1 + .../module-summary/module-summary.html | 112 +++++++++++++++++- .../module-summary/module-summary.ts | 69 ++++++++++- .../services/handlers/default-format.ts | 10 +- src/core/features/grades/lang.json | 1 + .../features/grades/services/grades-helper.ts | 32 +++++ .../components/module-index/module-index.ts | 7 +- .../siteplugins/services/siteplugins.ts | 1 + 8 files changed, 221 insertions(+), 12 deletions(-) diff --git a/scripts/langindex.json b/scripts/langindex.json index 07000f48f..59d395a92 100644 --- a/scripts/langindex.json +++ b/scripts/langindex.json @@ -1741,6 +1741,7 @@ "core.grades.fail": "grades", "core.grades.feedback": "grades", "core.grades.grade": "grades", + "core.grades.gradebook": "grades", "core.grades.gradeitem": "grades", "core.grades.gradepass": "grades", "core.grades.grades": "grades", diff --git a/src/core/features/course/components/module-summary/module-summary.html b/src/core/features/course/components/module-summary/module-summary.html index 7830b0baf..e77abac35 100644 --- a/src/core/features/course/components/module-summary/module-summary.html +++ b/src/core/features/course/components/module-summary/module-summary.html @@ -24,6 +24,17 @@
+ + + +

{{ 'core.course' | translate}}

+

+ + +

+
+
+ - +

{{ prefetchText }}

{{ downloadTimeReadable }}

@@ -45,7 +56,7 @@
- +

{{ 'addon.storagemanager.totalspaceusage' | translate }}

{{ sizeReadable | coreBytesToSize }} @@ -57,7 +68,102 @@
- + + + + +

{{ 'core.grades.gradebook' | translate }}

+
+
+ + + + +

+ + +

+

+ {{ 'core.grades.grade' | translate}} +

+

+
+ + + + + +
+
+ + +

{{ 'core.grades.weight' | translate}}

+

+
+
+ + +

{{ 'core.grades.range' | translate}}

+

+
+
+ + + +

{{ 'core.grades.percentage' | translate}}

+

+
+
+ + + +

{{ 'core.grades.lettergrade' | translate}}

+

+
+
+ + + +

{{ 'core.grades.rank' | translate}}

+

+
+
+ + + +

{{ 'core.grades.average' | translate}}

+

+
+
+ + + +

{{ 'core.grades.feedback' | translate}}

+

+ + +

+
+
+ + + +

{{ 'core.grades.contributiontocoursetotal' | translate}}

+

+
+
+
+
+
+
+ + {{ 'addon.blog.blog' | translate }} diff --git a/src/core/features/course/components/module-summary/module-summary.ts b/src/core/features/course/components/module-summary/module-summary.ts index 3475ca44f..f66609555 100644 --- a/src/core/features/course/components/module-summary/module-summary.ts +++ b/src/core/features/course/components/module-summary/module-summary.ts @@ -17,8 +17,12 @@ 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 { CoreCourse } from '@features/course/services/course'; import { CoreCourseHelper, CoreCourseModuleData } from '@features/course/services/course-helper'; +import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate'; import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate'; +import { CoreCourses, CoreEnrolledCourseData } from '@features/courses/services/courses'; +import { CoreGradesFormattedRow, CoreGradesFormattedTableRow, CoreGradesHelper } from '@features/grades/services/grades-helper'; import { CoreApp } from '@services/app'; import { CoreFilepool } from '@services/filepool'; import { CoreNavigator } from '@services/navigator'; @@ -61,13 +65,12 @@ export class CoreCourseModuleSummaryComponent implements OnInit, OnDestroy { sizeReadable?: string; downloadTimeReadable?: string; // Last download time in a readable format. size = 0; - + grades?: CoreGradesFormattedRow[]; blog = false; // If blog is available. - isOnline = false; // If the app is online or not. + course?: CoreEnrolledCourseData; 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; @@ -103,8 +106,15 @@ export class CoreCourseModuleSummaryComponent implements OnInit, OnDestroy { displayPrefetch: true, displaySize: true, displayBlog: true, + displayGrades: true, }, this.displayOptions); + this.displayOptions.displayGrades = this.displayOptions.displayGrades && + CoreCourseModuleDelegate.supportsFeature(this.module.modname, CoreConstants.FEATURE_GRADE_HAS_GRADE, true); + + this.displayOptions.displayDescription = this.displayOptions.displayDescription && + CoreCourseModuleDelegate.supportsFeature(this.module.modname, CoreConstants.FEATURE_SHOW_DESCRIPTION, true); + this.fetchContent(); if (this.component) { @@ -164,7 +174,11 @@ export class CoreCourseModuleSummaryComponent implements OnInit, OnDestroy { this.blog = await AddonBlog.isPluginEnabled(); - await this.getPackageStatus(); + await Promise.all([ + this.getPackageStatus(), + this.fetchGrades(), + this.fetchCourse(), + ]); this.loaded = true; } @@ -174,7 +188,7 @@ export class CoreCourseModuleSummaryComponent implements OnInit, OnDestroy { * * @param refresh If prefetch info has to be refreshed. */ - async getPackageStatus(refresh = false): Promise { + protected async getPackageStatus(refresh = false): Promise { if (!this.module) { return; } @@ -214,6 +228,50 @@ export class CoreCourseModuleSummaryComponent implements OnInit, OnDestroy { await CoreNavigator.navigateToSitePath(AddonBlogMainMenuHandlerService.PAGE_NAME, { params }); } + /** + * Fetch grade module info. + */ + protected async fetchGrades(): Promise { + if (!this.displayOptions.displayGrades) { + return; + } + + this.grades = await CoreGradesHelper.getModuleGrades(this.courseId, this.moduleId); + } + + /** + * Toggle grades expand. + * + * @param grade Row to expand. + */ + toggleGrade(grade: CoreGradesFormattedTableRow): void { + grade.expanded = !grade.expanded; + } + + /** + * Fetch course. + */ + protected async fetchCourse(): Promise { + this.course = await CoreCourses.getUserCourse(this.courseId, true); + } + + /** + * Open course. + */ + openCourse(): void { + if (!this.course) { + return; + } + + CoreCourse.openCourse( + this.course, + { + replace: true, + animationDirection: 'back', + }, + ); + } + /** * Prefetch the module. */ @@ -327,4 +385,5 @@ export type CoreCourseModuleSummaryDisplayOptions = { displayPrefetch?: boolean; displaySize?: boolean; displayBlog?: boolean; + displayGrades?: boolean; }; diff --git a/src/core/features/course/services/handlers/default-format.ts b/src/core/features/course/services/handlers/default-format.ts index 38c68d3df..5a4833f15 100644 --- a/src/core/features/course/services/handlers/default-format.ts +++ b/src/core/features/course/services/handlers/default-format.ts @@ -130,9 +130,13 @@ export class CoreCourseFormatDefaultHandler implements CoreCourseFormatHandler { navOptions.params = navOptions.params || {}; Object.assign(navOptions.params, { course: course }); - // Don't return the .push promise, we don't want to display a loading modal during the page transition. - const currentTab = CoreNavigator.getCurrentMainMenuTab(); - const routeDepth = CoreNavigator.getRouteDepth(`/main/${currentTab}/course/${course.id}`); + // When replace is true, disable route depth. + let routeDepth = 0; + if (!navOptions.replace) { + // Don't return the .push promise, we don't want to display a loading modal during the page transition. + const currentTab = CoreNavigator.getCurrentMainMenuTab(); + routeDepth = CoreNavigator.getRouteDepth(`/main/${currentTab}/course/${course.id}`); + } const deepPath = '/deep'.repeat(routeDepth); CoreNavigator.navigateToSitePath(`course${deepPath}/${course.id}`, navOptions); diff --git a/src/core/features/grades/lang.json b/src/core/features/grades/lang.json index 504a4ae34..456ff43e8 100644 --- a/src/core/features/grades/lang.json +++ b/src/core/features/grades/lang.json @@ -9,6 +9,7 @@ "fail": "Fail", "feedback": "Feedback", "grade": "Grade", + "gradebook": "Gradebook", "gradeitem": "Grade item", "gradepass": "Grade to pass", "grades": "Grades", diff --git a/src/core/features/grades/services/grades-helper.ts b/src/core/features/grades/services/grades-helper.ts index 942f46895..1a827ce7d 100644 --- a/src/core/features/grades/services/grades-helper.ts +++ b/src/core/features/grades/services/grades-helper.ts @@ -457,6 +457,38 @@ export class CoreGradesHelperProvider { }).map((row) => this.formatGradeRow(row))); } + /** + * Get module grades to display. + * + * @param courseId Course Id. + * @param moduleId Module Id. + * @return Formatted table rows. + */ + async getModuleGrades(courseId: number, moduleId: number): Promise { + const table = await CoreGrades.getCourseGradesTable(courseId); + + if (!table.tabledata) { + return []; + } + + // Find href containing "/mod/xxx/xxx.php". + const regex = /href="([^"]*\/mod\/[^"|^/]*\/[^"|^.]*\.php[^"]*)/; + + return await Promise.all(table.tabledata.filter((row) => { + if (row.itemname && row.itemname.content) { + const matches = row.itemname.content.match(regex); + + if (matches && matches.length) { + const hrefParams = CoreUrlUtils.extractUrlParams(matches[1]); + + return hrefParams && parseInt(hrefParams.id) === moduleId; + } + } + + return false; + }).map((row) => this.formatGradeRowForTable(row))); + } + /** * Go to view grades. * diff --git a/src/core/features/siteplugins/components/module-index/module-index.ts b/src/core/features/siteplugins/components/module-index/module-index.ts index a87dd2527..2bd8be6b8 100644 --- a/src/core/features/siteplugins/components/module-index/module-index.ts +++ b/src/core/features/siteplugins/components/module-index/module-index.ts @@ -90,6 +90,9 @@ export class CoreSitePluginsModuleIndexComponent implements OnInit, OnDestroy, C displayRefresh = true; displayPrefetch = true; displaySize = true; + displayGrades = false; + // @TODO: // Currently display blogs is not an option since it may change soon adding new summary handlers. + displayBlog = false; ptrEnabled = true; isDestroyed = false; @@ -128,6 +131,7 @@ export class CoreSitePluginsModuleIndexComponent implements OnInit, OnDestroy, C this.displayRefresh = !CoreUtils.isFalseOrZero(handlerSchema.displayrefresh); this.displayPrefetch = !CoreUtils.isFalseOrZero(handlerSchema.displayprefetch); this.displaySize = !CoreUtils.isFalseOrZero(handlerSchema.displaysize); + this.displayGrades = CoreUtils.isTrueOrOne(handlerSchema.displaygrades); // False by default. this.ptrEnabled = !CoreUtils.isFalseOrZero(handlerSchema.ptrenabled); } @@ -196,7 +200,8 @@ export class CoreSitePluginsModuleIndexComponent implements OnInit, OnDestroy, C displayRefresh: this.displayRefresh, displayPrefetch: this.displayPrefetch, displaySize: this.displaySize, - displayBlog: false, + displayBlog: this.displayBlog, + displayGrades: this.displayGrades, }, }, }); diff --git a/src/core/features/siteplugins/services/siteplugins.ts b/src/core/features/siteplugins/services/siteplugins.ts index eded0cd07..29ba79e0a 100644 --- a/src/core/features/siteplugins/services/siteplugins.ts +++ b/src/core/features/siteplugins/services/siteplugins.ts @@ -871,6 +871,7 @@ export type CoreSitePluginsCourseModuleHandlerData = CoreSitePluginsHandlerCommo displayrefresh?: boolean; displayprefetch?: boolean; displaysize?: boolean; + displaygrades?: boolean; coursepagemethod?: string; ptrenabled?: boolean; supportedfeatures?: Record; From 9bab09daced7d12977a7ba6df0782936e8dee386 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Thu, 17 Feb 2022 09:02:54 +0100 Subject: [PATCH 09/12] MOBILE-3931 performance: Fix memory leak on context menu --- .../components/context-menu/context-menu-item.ts | 12 +++++++++++- src/core/components/context-menu/context-menu.ts | 10 ++++++---- .../context-menu/core-context-menu-popover.html | 2 -- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/core/components/context-menu/context-menu-item.ts b/src/core/components/context-menu/context-menu-item.ts index 2d6a66587..922332a07 100644 --- a/src/core/components/context-menu/context-menu-item.ts +++ b/src/core/components/context-menu/context-menu-item.ts @@ -13,6 +13,7 @@ // limitations under the License. import { Component, Input, Output, OnInit, OnDestroy, EventEmitter, OnChanges, SimpleChange } from '@angular/core'; +import { CoreLogger } from '@singletons/logger'; import { CoreContextMenuComponent } from '../context-menu/context-menu'; /** @@ -35,7 +36,6 @@ import { CoreContextMenuComponent } from '../context-menu/context-menu'; export class CoreContextMenuItemComponent implements OnInit, OnDestroy, OnChanges { @Input() content?: string; // Content of the item. - @Input() iconDescription?: string; // Name of the icon to be shown on the left side of the item. @Input() iconAction?: string; // Name of the icon to show on the right side of the item. Represents the action to do on click. // If is "spinner" an spinner will be shown. // If is "toggle" a toggle switch will be shown. @@ -58,6 +58,11 @@ export class CoreContextMenuItemComponent implements OnInit, OnDestroy, OnChange @Output() onClosed?: EventEmitter<() => void>; // Will emit an event when the popover is closed because the item was clicked. @Output() toggleChange = new EventEmitter();// Will emit an event when toggle changes to enable 2-way data binding. + /** + * @deprecated since 4.0. + */ + @Input() iconDescription?: string; // Name of the icon to be shown on the left side of the item. Not used anymore. + protected hasAction = false; protected destroyed = false; @@ -88,6 +93,11 @@ export class CoreContextMenuItemComponent implements OnInit, OnDestroy, OnChange if (!this.destroyed) { this.ctxtMenu.addItem(this); } + + if (this.iconDescription !== undefined) { + CoreLogger.getInstance('CoreContextMenuItemComponent') + .warn('iconDescription Input is deprecated and should not be used'); + } } /** diff --git a/src/core/components/context-menu/context-menu.ts b/src/core/components/context-menu/context-menu.ts index c2108a54a..e981e7500 100644 --- a/src/core/components/context-menu/context-menu.ts +++ b/src/core/components/context-menu/context-menu.ts @@ -13,7 +13,7 @@ // limitations under the License. import { Component, Input, OnInit, OnDestroy, ElementRef, ChangeDetectorRef } from '@angular/core'; -import { Subject } from 'rxjs'; +import { Subject, Subscription } from 'rxjs'; import { auditTime } from 'rxjs/operators'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreUtils } from '@services/utils/utils'; @@ -42,11 +42,12 @@ export class CoreContextMenuComponent implements OnInit, OnDestroy { protected itemsChangedStream: Subject; // Stream to update the hideMenu boolean when items change. protected parentContextMenu?: CoreContextMenuComponent; protected expanded = false; + protected itemsSubscription: Subscription; constructor(elementRef: ElementRef, changeDetector: ChangeDetectorRef) { // Create the stream and subscribe to it. We ignore successive changes during 250ms. this.itemsChangedStream = new Subject(); - this.itemsChangedStream.pipe(auditTime(250)).subscribe(() => { + this.itemsSubscription = this.itemsChangedStream.pipe(auditTime(250)).subscribe(() => { // Hide the menu if all items are hidden. this.hideMenu = !this.items.some((item) => !item.hidden); @@ -63,7 +64,7 @@ export class CoreContextMenuComponent implements OnInit, OnDestroy { } /** - * Component being initialized. + * @inheritdoc */ ngOnInit(): void { this.icon = this.icon || 'ellipsis-vertical'; @@ -197,10 +198,11 @@ export class CoreContextMenuComponent implements OnInit, OnDestroy { } /** - * Component destroyed. + * @inheritdoc */ ngOnDestroy(): void { this.removeMergedItems(); + this.itemsSubscription.unsubscribe(); } } diff --git a/src/core/components/context-menu/core-context-menu-popover.html b/src/core/components/context-menu/core-context-menu-popover.html index 7e73792da..c6cb35d5d 100644 --- a/src/core/components/context-menu/core-context-menu-popover.html +++ b/src/core/components/context-menu/core-context-menu-popover.html @@ -6,8 +6,6 @@ [href]="item.href" (click)="itemClicked($event, item)" [attr.aria-label]="item.ariaAction" [hidden]="item.hidden" [detail]="(item.href && !item.iconAction) || null" role="menuitem" [button]="(item.href && !item.iconAction)" [showBrowserWarning]="item.showBrowserWarning"> -

From c2ec52584ce16b6b2abc1d6025ec7e623048d2b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Thu, 17 Feb 2022 13:22:49 +0100 Subject: [PATCH 10/12] MOBILE-3931 login: Style oauth providers as buttons --- .../login/pages/credentials/credentials.html | 6 +++--- .../features/login/pages/reconnect/reconnect.html | 8 ++++---- src/theme/theme.base.scss | 14 ++++++++++++-- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/core/features/login/pages/credentials/credentials.html b/src/core/features/login/pages/credentials/credentials.html index ce0c56988..b9a85c9d3 100644 --- a/src/core/features/login/pages/credentials/credentials.html +++ b/src/core/features/login/pages/credentials/credentials.html @@ -73,11 +73,11 @@

{{ 'core.login.potentialidps' | translate }}

- + {{provider.name}} - +
diff --git a/src/theme/theme.base.scss b/src/theme/theme.base.scss index a2f2df66f..55ba3c65c 100644 --- a/src/theme/theme.base.scss +++ b/src/theme/theme.base.scss @@ -298,11 +298,13 @@ button, ion-button { margin: 4px 8px; - ion-spinner[slot=start] { + ion-spinner[slot=start], + img[slot=start] { @include margin-horizontal(-0.3em, 0.3em); } - ion-spinner[slot=end] { + ion-spinner[slot=end], + img[slot=end] { @include margin-horizontal(-0.3em, 0.3em); } } @@ -468,6 +470,14 @@ ion-alert { } } +ion-loading { + --border-radius: var(--huge-radius); + + .loading-wrapper { + border-radius: var(--border-radius) !important; + } +} + // Ionic list. ion-list { padding: 0 !important; From 97a8dc3f7a94439f530c1e83d703f96cae15851a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Thu, 17 Feb 2022 13:58:10 +0100 Subject: [PATCH 11/12] MOBILE-3931 module: Improve module summary style --- scripts/langindex.json | 1 + .../index/addon-mod-assign-index.html | 2 +- .../components/index/index.html | 2 +- .../index/addon-mod-book-index.html | 2 +- .../index/addon-mod-chat-index.html | 2 +- .../index/addon-mod-choice-index.html | 2 +- .../index/addon-mod-data-index.html | 2 +- src/addons/mod/data/components/index/index.ts | 2 +- .../index/addon-mod-feedback-index.html | 2 +- .../index/addon-mod-folder-index.html | 2 +- .../mod/forum/components/index/index.html | 2 +- .../index/addon-mod-glossary-index.html | 2 +- .../index/addon-mod-h5pactivity-index.html | 2 +- .../index/addon-mod-imscp-index.html | 2 +- .../index/addon-mod-lesson-index.html | 2 +- .../mod/lesson/components/index/index.ts | 1 - .../components/index/addon-mod-lti-index.html | 2 +- .../index/addon-mod-page-index.html | 2 +- .../index/addon-mod-quiz-index.html | 2 +- .../index/addon-mod-resource-index.html | 2 +- .../index/addon-mod-scorm-index.html | 2 +- .../index/addon-mod-survey-index.html | 2 +- .../components/index/addon-mod-url-index.html | 2 +- .../index/addon-mod-wiki-index.html | 2 +- .../index/addon-mod-workshop-index.html | 2 +- src/addons/storagemanager/lang.json | 1 + .../course/classes/main-resource-component.ts | 22 ++--- .../module-summary/module-summary.html | 87 ++++++++++++------- .../module-summary/module-summary.scss | 16 ++++ .../module-summary/module-summary.ts | 46 +++++----- .../features/course/services/course-helper.ts | 2 - .../core-courses-course-list-item.html | 5 +- .../features/grades/pages/course/course.scss | 4 +- .../features/grades/services/grades-helper.ts | 2 +- .../components/module-index/module-index.ts | 10 +-- src/theme/theme.base.scss | 5 ++ 36 files changed, 137 insertions(+), 111 deletions(-) diff --git a/scripts/langindex.json b/scripts/langindex.json index 59d395a92..e1beb3757 100644 --- a/scripts/langindex.json +++ b/scripts/langindex.json @@ -1114,6 +1114,7 @@ "addon.storagemanager.deletedata": "local_moodlemobileapp", "addon.storagemanager.deletedatafrom": "local_moodlemobileapp", "addon.storagemanager.downloadedcourses": "local_moodlemobileapp", + "addon.storagemanager.downloads": "local_moodlemobileapp", "addon.storagemanager.errordeletedownloadeddata": "local_moodlemobileapp", "addon.storagemanager.managedownloads": "local_moodlemobileapp", "addon.storagemanager.totaldownloads": "local_moodlemobileapp", diff --git a/src/addons/mod/assign/components/index/addon-mod-assign-index.html b/src/addons/mod/assign/components/index/addon-mod-assign-index.html index 69b0d2d25..44fce0030 100644 --- a/src/addons/mod/assign/components/index/addon-mod-assign-index.html +++ b/src/addons/mod/assign/components/index/addon-mod-assign-index.html @@ -1,6 +1,6 @@ - + diff --git a/src/addons/mod/bigbluebuttonbn/components/index/index.html b/src/addons/mod/bigbluebuttonbn/components/index/index.html index e46bb4a56..49ebe332d 100644 --- a/src/addons/mod/bigbluebuttonbn/components/index/index.html +++ b/src/addons/mod/bigbluebuttonbn/components/index/index.html @@ -1,6 +1,6 @@ - + diff --git a/src/addons/mod/book/components/index/addon-mod-book-index.html b/src/addons/mod/book/components/index/addon-mod-book-index.html index c673defcf..f8d3545b6 100644 --- a/src/addons/mod/book/components/index/addon-mod-book-index.html +++ b/src/addons/mod/book/components/index/addon-mod-book-index.html @@ -1,6 +1,6 @@ - + diff --git a/src/addons/mod/chat/components/index/addon-mod-chat-index.html b/src/addons/mod/chat/components/index/addon-mod-chat-index.html index 43db1e541..47f13bd8c 100644 --- a/src/addons/mod/chat/components/index/addon-mod-chat-index.html +++ b/src/addons/mod/chat/components/index/addon-mod-chat-index.html @@ -1,6 +1,6 @@ - + diff --git a/src/addons/mod/choice/components/index/addon-mod-choice-index.html b/src/addons/mod/choice/components/index/addon-mod-choice-index.html index 15cdfbec4..e29970de6 100644 --- a/src/addons/mod/choice/components/index/addon-mod-choice-index.html +++ b/src/addons/mod/choice/components/index/addon-mod-choice-index.html @@ -1,6 +1,6 @@ - + diff --git a/src/addons/mod/data/components/index/addon-mod-data-index.html b/src/addons/mod/data/components/index/addon-mod-data-index.html index a80142873..59f79533d 100644 --- a/src/addons/mod/data/components/index/addon-mod-data-index.html +++ b/src/addons/mod/data/components/index/addon-mod-data-index.html @@ -12,7 +12,7 @@ - + diff --git a/src/addons/mod/data/components/index/index.ts b/src/addons/mod/data/components/index/index.ts index 543cceeda..9bdb523fe 100644 --- a/src/addons/mod/data/components/index/index.ts +++ b/src/addons/mod/data/components/index/index.ts @@ -296,7 +296,7 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp this.hasNextPage = numEntries >= AddonModDataProvider.PER_PAGE && ((this.search.page + 1) * AddonModDataProvider.PER_PAGE) < entries.totalcount; - this.hasOffline = entries.hasOfflineActions; + this.hasOffline = !!entries.hasOfflineActions; this.hasOfflineRatings = !!entries.hasOfflineRatings; diff --git a/src/addons/mod/feedback/components/index/addon-mod-feedback-index.html b/src/addons/mod/feedback/components/index/addon-mod-feedback-index.html index eb54799b8..f564311d8 100644 --- a/src/addons/mod/feedback/components/index/addon-mod-feedback-index.html +++ b/src/addons/mod/feedback/components/index/addon-mod-feedback-index.html @@ -1,6 +1,6 @@ - + diff --git a/src/addons/mod/folder/components/index/addon-mod-folder-index.html b/src/addons/mod/folder/components/index/addon-mod-folder-index.html index e50f4f51b..b01b72741 100644 --- a/src/addons/mod/folder/components/index/addon-mod-folder-index.html +++ b/src/addons/mod/folder/components/index/addon-mod-folder-index.html @@ -1,6 +1,6 @@ - + diff --git a/src/addons/mod/forum/components/index/index.html b/src/addons/mod/forum/components/index/index.html index f6d887a7f..a7a5afbdc 100644 --- a/src/addons/mod/forum/components/index/index.html +++ b/src/addons/mod/forum/components/index/index.html @@ -1,6 +1,6 @@ - + diff --git a/src/addons/mod/glossary/components/index/addon-mod-glossary-index.html b/src/addons/mod/glossary/components/index/addon-mod-glossary-index.html index 05414fc94..524abb9b1 100644 --- a/src/addons/mod/glossary/components/index/addon-mod-glossary-index.html +++ b/src/addons/mod/glossary/components/index/addon-mod-glossary-index.html @@ -9,7 +9,7 @@ - + diff --git a/src/addons/mod/h5pactivity/components/index/addon-mod-h5pactivity-index.html b/src/addons/mod/h5pactivity/components/index/addon-mod-h5pactivity-index.html index fc7dfb9ed..039f30ce2 100644 --- a/src/addons/mod/h5pactivity/components/index/addon-mod-h5pactivity-index.html +++ b/src/addons/mod/h5pactivity/components/index/addon-mod-h5pactivity-index.html @@ -10,7 +10,7 @@ - + diff --git a/src/addons/mod/imscp/components/index/addon-mod-imscp-index.html b/src/addons/mod/imscp/components/index/addon-mod-imscp-index.html index a8d3096cf..d795fb3fe 100644 --- a/src/addons/mod/imscp/components/index/addon-mod-imscp-index.html +++ b/src/addons/mod/imscp/components/index/addon-mod-imscp-index.html @@ -4,7 +4,7 @@ - + diff --git a/src/addons/mod/lesson/components/index/addon-mod-lesson-index.html b/src/addons/mod/lesson/components/index/addon-mod-lesson-index.html index 350ed2e21..cb63e1d65 100644 --- a/src/addons/mod/lesson/components/index/addon-mod-lesson-index.html +++ b/src/addons/mod/lesson/components/index/addon-mod-lesson-index.html @@ -1,6 +1,6 @@ - + diff --git a/src/addons/mod/lesson/components/index/index.ts b/src/addons/mod/lesson/components/index/index.ts index c449a9fe9..1a9e8b614 100644 --- a/src/addons/mod/lesson/components/index/index.ts +++ b/src/addons/mod/lesson/components/index/index.ts @@ -73,7 +73,6 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo canManage?: boolean; // Whether the user can manage the lesson. canViewReports?: boolean; // Whether the user can view the lesson reports. showSpinner?: boolean; // Whether to display a spinner. - hasOffline?: boolean; // Whether there's offline data. retakeToReview?: AddonModLessonRetakeFinishedInSyncDBRecord; // A retake to review. preventReasons: AddonModLessonPreventAccessReason[] = []; // List of reasons that prevent the lesson from being seen. leftDuringTimed?: boolean; // Whether the user has started and left a retake. diff --git a/src/addons/mod/lti/components/index/addon-mod-lti-index.html b/src/addons/mod/lti/components/index/addon-mod-lti-index.html index 9892dd3ae..dc3b6068d 100644 --- a/src/addons/mod/lti/components/index/addon-mod-lti-index.html +++ b/src/addons/mod/lti/components/index/addon-mod-lti-index.html @@ -1,6 +1,6 @@ - + diff --git a/src/addons/mod/page/components/index/addon-mod-page-index.html b/src/addons/mod/page/components/index/addon-mod-page-index.html index e17fa3571..1cfc5f303 100644 --- a/src/addons/mod/page/components/index/addon-mod-page-index.html +++ b/src/addons/mod/page/components/index/addon-mod-page-index.html @@ -1,6 +1,6 @@ - + diff --git a/src/addons/mod/quiz/components/index/addon-mod-quiz-index.html b/src/addons/mod/quiz/components/index/addon-mod-quiz-index.html index 4f6ec82f8..6323f3cb5 100644 --- a/src/addons/mod/quiz/components/index/addon-mod-quiz-index.html +++ b/src/addons/mod/quiz/components/index/addon-mod-quiz-index.html @@ -1,6 +1,6 @@ - + diff --git a/src/addons/mod/resource/components/index/addon-mod-resource-index.html b/src/addons/mod/resource/components/index/addon-mod-resource-index.html index a299d9a31..89fbf28f7 100644 --- a/src/addons/mod/resource/components/index/addon-mod-resource-index.html +++ b/src/addons/mod/resource/components/index/addon-mod-resource-index.html @@ -1,6 +1,6 @@ - + diff --git a/src/addons/mod/scorm/components/index/addon-mod-scorm-index.html b/src/addons/mod/scorm/components/index/addon-mod-scorm-index.html index 0fa5c17a4..275c81125 100644 --- a/src/addons/mod/scorm/components/index/addon-mod-scorm-index.html +++ b/src/addons/mod/scorm/components/index/addon-mod-scorm-index.html @@ -1,6 +1,6 @@ - + diff --git a/src/addons/mod/survey/components/index/addon-mod-survey-index.html b/src/addons/mod/survey/components/index/addon-mod-survey-index.html index dedf029c9..6f08add31 100644 --- a/src/addons/mod/survey/components/index/addon-mod-survey-index.html +++ b/src/addons/mod/survey/components/index/addon-mod-survey-index.html @@ -1,6 +1,6 @@ - + diff --git a/src/addons/mod/url/components/index/addon-mod-url-index.html b/src/addons/mod/url/components/index/addon-mod-url-index.html index 8b531ded4..ccf09a1a2 100644 --- a/src/addons/mod/url/components/index/addon-mod-url-index.html +++ b/src/addons/mod/url/components/index/addon-mod-url-index.html @@ -1,6 +1,6 @@ - + diff --git a/src/addons/mod/wiki/components/index/addon-mod-wiki-index.html b/src/addons/mod/wiki/components/index/addon-mod-wiki-index.html index de251b747..462d5a1b4 100644 --- a/src/addons/mod/wiki/components/index/addon-mod-wiki-index.html +++ b/src/addons/mod/wiki/components/index/addon-mod-wiki-index.html @@ -21,7 +21,7 @@ - + diff --git a/src/addons/mod/workshop/components/index/addon-mod-workshop-index.html b/src/addons/mod/workshop/components/index/addon-mod-workshop-index.html index 9ff979bf8..f24b28232 100644 --- a/src/addons/mod/workshop/components/index/addon-mod-workshop-index.html +++ b/src/addons/mod/workshop/components/index/addon-mod-workshop-index.html @@ -1,6 +1,6 @@ - + diff --git a/src/addons/storagemanager/lang.json b/src/addons/storagemanager/lang.json index 84988fe0d..93476bbf6 100644 --- a/src/addons/storagemanager/lang.json +++ b/src/addons/storagemanager/lang.json @@ -11,6 +11,7 @@ "deletedata": "Delete downloaded data", "deletedatafrom": "Delete all downloaded data from '{{name}}'", "downloadedcourses": "Downloaded courses", + "downloads": "Downloads", "errordeletedownloadeddata": "Error deleting downloaded data.", "managedownloads": "Manage downloads", "totaldownloads": "Total downloads", diff --git a/src/core/features/course/classes/main-resource-component.ts b/src/core/features/course/classes/main-resource-component.ts index edbcd1680..6536145e0 100644 --- a/src/core/features/course/classes/main-resource-component.ts +++ b/src/core/features/course/classes/main-resource-component.ts @@ -115,7 +115,7 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, * @return Promise resolved when done. */ async doRefresh(refresher?: IonRefresher | null, showErrors = false): Promise { - if (!this.loaded || !this.module) { + if (!this.module) { // Module can be undefined if course format changes from single activity to weekly/topics. return; } @@ -224,7 +224,7 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, const lastDownloaded = await CoreCourseHelper.getModulePackageLastDownloaded(this.module, this.component); - this.downloadTimeReadable = lastDownloaded.downloadTimeReadable; + this.downloadTimeReadable = CoreTextUtils.ucFirst(lastDownloaded.downloadTimeReadable); } /** @@ -416,24 +416,14 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, }); if (data) { - if (data.action == 'refresh') { - const modal = await CoreDomUtils.showModalLoading(); - + if (this.loaded && (data.action == 'refresh' || data.action == 'sync')) { + this.loaded = false; try { - await this.doRefresh(); + await this.doRefresh(undefined, data.action == 'sync'); } finally { - modal.dismiss(); - } - } else if (data.action == 'sync') { - const modal = await CoreDomUtils.showModalLoading(); - - try { - await this.doRefresh( undefined, true); - } finally { - modal.dismiss(); + this.loaded = true; } } - } } diff --git a/src/core/features/course/components/module-summary/module-summary.html b/src/core/features/course/components/module-summary/module-summary.html index e77abac35..820e944ef 100644 --- a/src/core/features/course/components/module-summary/module-summary.html +++ b/src/core/features/course/components/module-summary/module-summary.html @@ -11,8 +11,13 @@ - + +

+ + + {{moduleNameTranslated}} +

@@ -25,9 +30,12 @@ - + -

{{ 'core.course' | translate}}

+

+ + {{ 'core.course' | translate}} +

@@ -35,44 +43,60 @@ - + +

+ {{ 'core.description' | translate}} +

- - -

{{ prefetchText }}

-

{{ downloadTimeReadable }}

-
- - + + + +

+ + {{ 'addon.storagemanager.downloads' | translate }} +

+
+
+ + +

{{ 'addon.storagemanager.totalspaceusage' | translate }}

+ {{ sizeReadable | coreBytesToSize }} +
+ + + + +
+ + +

{{ 'core.lastdownloaded' | translate }} {{ downloadTimeReadable }}

+
+
+ + + + + {{ 'core.download' | translate }} + - -
- - - -

{{ 'addon.storagemanager.totalspaceusage' | translate }}

- {{ sizeReadable | coreBytesToSize }} -
- - - - -
+ -

{{ 'core.grades.gradebook' | translate }}

+

+ {{ 'core.grades.gradebook' | translate + }} +

@@ -90,7 +114,8 @@

{{ 'core.grades.grade' | translate}}

-

+

+ Not graded @@ -163,12 +188,12 @@
- + {{ 'addon.blog.blog' | translate }} - +
diff --git a/src/core/features/course/components/module-summary/module-summary.scss b/src/core/features/course/components/module-summary/module-summary.scss index fb9664764..59ce443d5 100644 --- a/src/core/features/course/components/module-summary/module-summary.scss +++ b/src/core/features/course/components/module-summary/module-summary.scss @@ -1,3 +1,4 @@ +@import "~theme/globals"; :host ::ng-deep .collapsible-title ion-label { margin-top: 12px; @@ -6,3 +7,18 @@ h1 { font-size: 20px; } + +.core-modulename { + text-transform: uppercase; + core-mod-icon { + padding: 3px; + --size: 10px; + margin: 0; + } +} + + +ion-item ion-label ion-icon { + @include margin-horizontal(0, 4px); + vertical-align: text-top; +} diff --git a/src/core/features/course/components/module-summary/module-summary.ts b/src/core/features/course/components/module-summary/module-summary.ts index f66609555..a82c4801c 100644 --- a/src/core/features/course/components/module-summary/module-summary.ts +++ b/src/core/features/course/components/module-summary/module-summary.ts @@ -30,7 +30,7 @@ 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 { ModalController, Network, NgZone } from '@singletons'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { Subscription } from 'rxjs'; @@ -59,16 +59,17 @@ export class CoreCourseModuleSummaryComponent implements OnInit, OnDestroy { 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; + prefetchLoading = false; + canPrefetch = false;; + prefetchDisabled = false; + sizeReadable = ''; + downloadTimeReadable = ''; // Last download time in a readable format. grades?: CoreGradesFormattedRow[]; blog = false; // If blog is available. isOnline = false; // If the app is online or not. course?: CoreEnrolledCourseData; + modicon = ''; + moduleNameTranslated = ''; protected onlineSubscription: Subscription; // It will observe the status of the network connection. protected packageStatusObserver?: CoreEventObserver; // Observer of package status. @@ -172,6 +173,9 @@ export class CoreCourseModuleSummaryComponent implements OnInit, OnDestroy { this.externalUrl = this.module.url; this.courseId = this.courseId || this.module.course; + this.modicon = await CoreCourseModuleDelegate.getModuleIconSrc(this.module.modname, this.module.modicon, this.module); + this.moduleNameTranslated = CoreCourse.translateModuleName(this.module.modname || ''); + this.blog = await AddonBlog.isPluginEnabled(); await Promise.all([ @@ -196,27 +200,20 @@ export class CoreCourseModuleSummaryComponent implements OnInit, OnDestroy { const moduleInfo = await CoreCourseHelper.getModulePrefetchInfo(this.module, this.courseId, refresh, this.component); - this.prefetchStatusIcon = moduleInfo.statusIcon; - this.prefetchStatus = moduleInfo.status; + this.canPrefetch = moduleInfo.status != CoreConstants.NOT_DOWNLOADABLE; this.downloadTimeReadable = ''; - if (moduleInfo.status != CoreConstants.NOT_DOWNLOADABLE) { - // Module is downloadable, get the text to display to prefetch. + if (this.canPrefetch) { 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.prefetchLoading = moduleInfo.status == CoreConstants.DOWNLOADING; + this.prefetchDisabled = moduleInfo.status == CoreConstants.DOWNLOADED; } - 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 = ''; - } + this.sizeReadable = moduleInfo.size && moduleInfo.size > 0 + ? moduleInfo.sizeReadable + : ''; } /** @@ -280,8 +277,7 @@ export class CoreCourseModuleSummaryComponent implements OnInit, OnDestroy { return; } - const initialIcon = this.prefetchStatusIcon; - this.prefetchStatusIcon = CoreConstants.ICON_DOWNLOADING; // Show spinner since this operation might take a while. + this.prefetchLoading = true; // Show spinner since this operation might take a while. try { // We need to call getDownloadSize, the package might have been updated. @@ -293,7 +289,7 @@ export class CoreCourseModuleSummaryComponent implements OnInit, OnDestroy { await this.getPackageStatus(true); } catch (error) { - this.prefetchStatusIcon = initialIcon; + this.prefetchLoading = false; if (!this.isDestroyed) { CoreDomUtils.showErrorModalDefault(error, 'core.errordownloading', true); @@ -309,7 +305,7 @@ export class CoreCourseModuleSummaryComponent implements OnInit, OnDestroy { return; } - if (this.prefetchStatus == CoreConstants.DOWNLOADING) { + if (this.prefetchLoading) { CoreDomUtils.showAlertTranslated(undefined, 'core.course.cannotdeletewhiledownloading'); return; diff --git a/src/core/features/course/services/course-helper.ts b/src/core/features/course/services/course-helper.ts index 4c807d5e1..e14cae7f0 100644 --- a/src/core/features/course/services/course-helper.ts +++ b/src/core/features/course/services/course-helper.ts @@ -1426,8 +1426,6 @@ export class CoreCourseHelperProvider { downloadTimeReadable = moment(downloadTime * 1000).calendar(); } - downloadTimeReadable = CoreTextUtils.ucFirst(downloadTimeReadable); - return { downloadTime, downloadTimeReadable, diff --git a/src/core/features/courses/components/course-list-item/core-courses-course-list-item.html b/src/core/features/courses/components/course-list-item/core-courses-course-list-item.html index 51cb1e650..c63576239 100644 --- a/src/core/features/courses/components/course-list-item/core-courses-course-list-item.html +++ b/src/core/features/courses/components/course-list-item/core-courses-course-list-item.html @@ -4,7 +4,7 @@
- +
@@ -31,7 +31,8 @@ [class.item-disabled]="course.visible == 0"> - + diff --git a/src/core/features/grades/pages/course/course.scss b/src/core/features/grades/pages/course/course.scss index 32bb2b00a..1d73d15e2 100644 --- a/src/core/features/grades/pages/course/course.scss +++ b/src/core/features/grades/pages/course/course.scss @@ -80,8 +80,8 @@ } core-mod-icon { - padding: 0.1rem; - --size: 16px; + padding: 3px; + --size: 10px; } diff --git a/src/core/features/grades/services/grades-helper.ts b/src/core/features/grades/services/grades-helper.ts index 1a827ce7d..cff4e9b98 100644 --- a/src/core/features/grades/services/grades-helper.ts +++ b/src/core/features/grades/services/grades-helper.ts @@ -601,7 +601,7 @@ export class CoreGradesHelperProvider { row.iconAlt = Translate.instant('core.grades.aggregatesum'); } else if (text.indexOf('/outcomes') > -1 || text.indexOf('fa-tasks') > -1) { row.itemtype = 'outcome'; - row.icon = 'fas-chart-pie'; + row.icon = 'fas-tasks'; row.iconAlt = Translate.instant('core.grades.outcome'); } else if (text.indexOf('i/folder') > -1 || text.indexOf('fa-folder') > -1) { row.itemtype = 'category'; diff --git a/src/core/features/siteplugins/components/module-index/module-index.ts b/src/core/features/siteplugins/components/module-index/module-index.ts index 2bd8be6b8..3e15a13dc 100644 --- a/src/core/features/siteplugins/components/module-index/module-index.ts +++ b/src/core/features/siteplugins/components/module-index/module-index.ts @@ -206,14 +206,8 @@ export class CoreSitePluginsModuleIndexComponent implements OnInit, OnDestroy, C }, }); - if (data && data.action == 'refresh') { - const modal = await CoreDomUtils.showModalLoading(); - - try { - await this.doRefresh(); - } finally { - modal.dismiss(); - } + if (data && data.action == 'refresh' && this.content?.dataLoaded) { + this.content?.refreshContent(true); } } diff --git a/src/theme/theme.base.scss b/src/theme/theme.base.scss index 55ba3c65c..c0d3691c8 100644 --- a/src/theme/theme.base.scss +++ b/src/theme/theme.base.scss @@ -307,6 +307,11 @@ ion-button { img[slot=end] { @include margin-horizontal(-0.3em, 0.3em); } + + ion-spinner[slot] { + width: 20px; + color: inherit; + } } ion-button.button-outline { From b28ff9415fc7350a1fb356e92e9ee4d8ed179fc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Fri, 18 Feb 2022 10:13:40 +0100 Subject: [PATCH 12/12] MOBILE-3931 chore: Annotations on upgrade.txt --- upgrade.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/upgrade.txt b/upgrade.txt index 4755f12ea..ebb311bcb 100644 --- a/upgrade.txt +++ b/upgrade.txt @@ -20,6 +20,10 @@ information provided here is intended especially for developers. - Most of the functions or callbacks that handle redirects/deeplinks have been modified to accept an object instead of just path + options. E.g.: CoreLoginHelper.isSiteLoggedOut, CoreLoginHelper.openBrowserForSSOLogin, CoreLoginHelper.openBrowserForOAuthLogin, CoreLoginHelper.prepareForSSOLogin, CoreApp.storeRedirect, CoreSites.loadSite. - Course preview page route has changed from course/:courseId/preview to course/:courseId/summary to match with the page name and characteristics. - The parameters of the following functions in CoreCourseHelper have changed: navigateToModuleByInstance, navigateToModule, openModule. +- fillContextMenu, expandDescription, gotoBlog, prefetch and removeFiles functions have been removed from CoreCourseModuleMainResourceComponent. +- contextMenuPrefetch and fillContextMenu have been removed from CoreCourseHelper. + + === 3.9.5 ===