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] 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(); + } } /**