MOBILE-4616 viewer: Create a service to have all the viewer functions

main
Pau Ferrer Ocaña 2024-07-18 14:43:22 +02:00
parent 072f0b8fd4
commit 1412a5571c
13 changed files with 201 additions and 103 deletions

View File

@ -67,6 +67,7 @@ import {
ADDON_MOD_ASSIGN_SUBMITTED_FOR_GRADING_EVENT,
ADDON_MOD_ASSIGN_UNLIMITED_ATTEMPTS,
} from '../../constants';
import { CoreViewer } from '@features/viewer/services/viewer';
/**
* Component that displays an assignment submission.
@ -866,7 +867,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
*/
showAdvancedGrade(): void {
if (this.feedback && this.feedback.advancedgrade) {
CoreTextUtils.viewText(
CoreViewer.viewText(
Translate.instant('core.grades.grade'),
this.feedback.gradefordisplay,
{

View File

@ -27,6 +27,8 @@ import { CoreUtils } from '@services/utils/utils';
import { AddonModAssignFeedbackPluginBaseComponent } from '@addons/mod/assign/classes/base-feedback-plugin-component';
import { ContextLevel } from '@/core/constants';
import { ADDON_MOD_ASSIGN_COMPONENT } from '@addons/mod/assign/constants';
import { CoreViewer } from '@features/viewer/services/viewer';
/**
* Component to render a comments feedback plugin.
*/
@ -67,7 +69,7 @@ export class AddonModAssignFeedbackCommentsComponent extends AddonModAssignFeedb
if (this.text) {
// Open a new state with the text.
CoreTextUtils.viewText(this.plugin.name, this.text, {
CoreViewer.viewText(this.plugin.name, this.text, {
component: this.component,
componentId: this.assign.cmid,
filter: true,

View File

@ -23,6 +23,7 @@ import { CoreUtils } from '@services/utils/utils';
import { AddonModAssignSubmissionOnlineTextPluginData } from '../services/handler';
import { ContextLevel } from '@/core/constants';
import { ADDON_MOD_ASSIGN_COMPONENT } from '@addons/mod/assign/constants';
import { CoreViewer } from '@features/viewer/services/viewer';
/**
* Component to render an onlinetext submission plugin.
@ -84,7 +85,7 @@ export class AddonModAssignSubmissionOnlineTextComponent extends AddonModAssignS
if (this.text) {
// Open a new state with the interpolated contents.
CoreTextUtils.viewText(this.plugin.name, this.text, {
CoreViewer.viewText(this.plugin.name, this.text, {
component: this.component,
componentId: this.assign.cmid,
filter: true,

View File

@ -15,7 +15,6 @@
import { Injectable } from '@angular/core';
import { CoreNavigator } from '@services/navigator';
import { CoreTextUtils } from '@services/utils/text';
import { CoreUtils } from '@services/utils/utils';
import { makeSingleton } from '@singletons';
import { CorePushNotificationsClickHandler } from '@features/pushnotifications/services/push-delegate';
@ -24,6 +23,7 @@ import { CoreContentLinksHelper } from '@features/contentlinks/services/contentl
import { AddonNotifications } from '../notifications';
import { AddonNotificationsMainMenuHandlerService } from './mainmenu';
import { AddonNotificationsHelper } from '../notifications-helper';
import { CoreViewer } from '@features/viewer/services/viewer';
/**
* Handler for non-messaging push notifications clicks.
@ -77,7 +77,7 @@ export class AddonNotificationsPushClickHandlerService implements CorePushNotifi
if (notification.customdata?.extendedtext) {
// Display the text in a modal.
return CoreTextUtils.viewText(notification.title || '', <string> notification.customdata.extendedtext, {
return CoreViewer.viewText(notification.title || '', <string> notification.customdata.extendedtext, {
displayCopyButton: true,
modalOptions: { cssClass: 'core-modal-fullscreen' },
});

View File

@ -57,6 +57,7 @@ import { CoreIcons } from '@singletons/icons';
import { ContextLevel } from '../constants';
import { CoreWait } from '@singletons/wait';
import { toBoolean } from '../transforms/boolean';
import { CoreViewer } from '@features/viewer/services/viewer';
/**
* Directive to format text rendered. It renders the HTML and treats all links and media, using CoreLinkDirective
@ -294,7 +295,7 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy, AsyncDirec
e.preventDefault();
e.stopPropagation();
CoreDomUtils.viewImage(imgSrc, img.getAttribute('alt'), this.component, this.componentId);
CoreViewer.viewImage(imgSrc, img.getAttribute('alt'), this.component, this.componentId);
});
img.parentNode?.appendChild(button);

View File

@ -36,6 +36,7 @@ import { CoreDom } from '@singletons/dom';
import { CoreSitesFactory } from '@services/sites-factory';
import { EMAIL_SIGNUP_FEATURE_NAME } from '@features/login/constants';
import { CoreInputErrorsMessages } from '@components/input-errors/input-errors';
import { CoreViewer } from '@features/viewer/services/viewer';
/**
* Page to signup using email.
@ -385,7 +386,7 @@ export class CoreLoginEmailSignupPage implements OnInit {
* Show authentication instructions.
*/
showAuthInstructions(): void {
CoreTextUtils.viewText(Translate.instant('core.login.instructions'), this.authInstructions);
CoreViewer.viewText(Translate.instant('core.login.instructions'), this.authInstructions);
}
/**

View File

@ -26,6 +26,7 @@ import { CoreTextUtils } from '@services/utils/text';
import { Translate } from '@singletons';
import { CoreMainMenuDeepLinkManager } from '@features/mainmenu/classes/deep-link-manager';
import { CoreDom } from '@singletons/dom';
import { CoreViewer } from '@features/viewer/services/viewer';
/**
* Page that displays the more page of the app.
@ -133,7 +134,7 @@ export class CoreMainMenuMorePage implements OnInit, OnDestroy {
* @param item Item to open.
*/
openItem(item: CoreMainMenuCustomItem): void {
CoreNavigator.navigateToSitePath('viewer/iframe', { params: { title: item.label, url: item.url } });
CoreViewer.openIframeViewer(item.label, item.url);
}
/**
@ -166,7 +167,7 @@ export class CoreMainMenuMorePage implements OnInit, OnDestroy {
});
} else {
// It's not a URL, open it in a modal so the user can see it and copy it.
CoreTextUtils.viewText(Translate.instant('core.qrscanner'), text, {
CoreViewer.viewText(Translate.instant('core.qrscanner'), text, {
displayCopyButton: true,
});
}

View File

@ -30,6 +30,7 @@ import { CoreIcons } from '@singletons/icons';
import { CoreUrl } from '@singletons/url';
import { ContextLevel } from '@/core/constants';
import { CoreIonicColorNames } from '@singletons/colors';
import { CoreViewer } from '@features/viewer/services/viewer';
/**
* Service with some common functions to handle questions.
@ -914,7 +915,7 @@ export class CoreQuestionHelperProvider {
event.preventDefault();
event.stopPropagation();
CoreTextUtils.viewText(title, target.html ?? '', {
CoreViewer.viewText(title, target.html ?? '', {
component: component,
componentId: componentId,
filter: true,

View File

@ -14,6 +14,7 @@
import { Component, OnInit } from '@angular/core';
import { CoreNavigator } from '@services/navigator';
import { CoreDomUtils } from '@services/utils/dom';
/**
* Page to display a URL in an iframe.
@ -28,9 +29,21 @@ export class CoreViewerIframePage implements OnInit {
url?: string; // Iframe URL.
autoLogin?: boolean; // Whether to try to use auto-login.
/**
* @inheritdoc
*/
async ngOnInit(): Promise<void> {
this.title = CoreNavigator.getRouteParam('title');
this.url = CoreNavigator.getRouteParam('url');
try {
this.title = CoreNavigator.getRequiredRouteParam('title');
this.url = CoreNavigator.getRequiredRouteParam('url');
} catch (error) {
CoreDomUtils.showErrorModal(error);
CoreNavigator.back();
return;
}
this.autoLogin = CoreNavigator.getRouteBooleanParam('autoLogin') ?? true;
}

View File

@ -0,0 +1,116 @@
// (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 { ContextLevel } from '@/core/constants';
import { Injectable } from '@angular/core';
import { ModalOptions } from '@ionic/angular';
import { CoreModals } from '@services/modals';
import { CoreNavigator } from '@services/navigator';
import { CoreWSFile } from '@services/ws';
import { makeSingleton } from '@singletons';
/**
* Viewer services.
*/
@Injectable({ providedIn: 'root' })
export class CoreViewerService {
/**
* View an image in a modal.
*
* @param image URL of the image.
* @param title Title of the page or modal.
* @param component Component to link the image to if needed.
* @param componentId An ID to use in conjunction with the component.
*/
async viewImage(
image: string,
title?: string | null,
component?: string,
componentId?: string | number,
): Promise<void> {
if (!image) {
return;
}
const { CoreViewerImageComponent } = await import('@features/viewer/components/image/image');
await CoreModals.openModal({
component: CoreViewerImageComponent,
componentProps: {
title,
image,
component,
componentId,
},
cssClass: 'core-modal-transparent',
});
}
/**
* Shows a text on a new page.
*
* @param title Title of the new state.
* @param content Content of the text to be expanded.
* @param options Options.
*/
async viewText(title: string, content: string, options?: CoreViewerTextOptions): Promise<void> {
if (!content.length) {
return;
}
options = options || {};
const { CoreViewerTextComponent } = await import('@features/viewer/components/text/text');
const modalOptions: ModalOptions = Object.assign(options.modalOptions || {}, {
component: CoreViewerTextComponent,
});
delete options.modalOptions;
modalOptions.componentProps = {
title,
content,
...options,
};
await CoreModals.openModal(modalOptions);
}
/**
* Navigate to iframe viewer.
*
* @param title Page title.
* @param url Iframe URL.
* @param autoLogin Whether to try to use auto-login.
*/
async openIframeViewer(title: string, url: string, autoLogin?: boolean): Promise<void> {
await CoreNavigator.navigateToSitePath('viewer/iframe', { params: { title, url, autoLogin } });
}
}
export const CoreViewer = makeSingleton(CoreViewerService);
/**
* Options for viewText.
*/
export type CoreViewerTextOptions = {
component?: string; // Component to link the embedded files to.
componentId?: string | number; // An ID to use in conjunction with the component.
files?: CoreWSFile[]; // List of files to display along with the text.
filter?: boolean; // Whether the text should be filtered.
contextLevel?: ContextLevel; // The context level.
instanceId?: number; // The instance ID related to the context.
courseId?: number; // Course ID the text belongs to. It can be used to improve performance with filters.
displayCopyButton?: boolean; // Whether to display a button to copy the text.
modalOptions?: Partial<ModalOptions>; // Modal options.
};

View File

@ -148,12 +148,11 @@ export class CoreFileProvider {
* @param path Relative path to the file.
* @returns Promise resolved when the file is retrieved.
*/
getFile(path: string): Promise<FileEntry> {
return this.init().then(() => {
this.logger.debug('Get file: ' + path);
async getFile(path: string): Promise<FileEntry> {
await this.init();
this.logger.debug('Get file: ' + path);
return File.resolveLocalFilesystemUrl(this.addBasePathIfNeeded(path));
}).then((entry) => <FileEntry> entry);
return <FileEntry> await File.resolveLocalFilesystemUrl(this.addBasePathIfNeeded(path));
}
/**
@ -162,12 +161,12 @@ export class CoreFileProvider {
* @param path Relative path to the directory.
* @returns Promise resolved when the directory is retrieved.
*/
getDir(path: string): Promise<DirectoryEntry> {
return this.init().then(() => {
this.logger.debug('Get directory: ' + path);
async getDir(path: string): Promise<DirectoryEntry> {
await this.init();
return File.resolveDirectoryUrl(this.addBasePathIfNeeded(path));
});
this.logger.debug('Get directory: ' + path);
return await File.resolveDirectoryUrl(this.addBasePathIfNeeded(path));
}
/**
@ -377,12 +376,14 @@ export class CoreFileProvider {
* @param path Relative path to the directory.
* @returns Promise to be resolved when the size is calculated.
*/
getDirectorySize(path: string): Promise<number> {
async getDirectorySize(path: string): Promise<number> {
path = this.removeBasePath(path);
this.logger.debug('Get size of dir: ' + path);
return this.getDir(path).then((dirEntry) => this.getSize(dirEntry));
const dirEntry = await this.getDir(path);
return this.getSize(dirEntry);
}
/**
@ -391,12 +392,14 @@ export class CoreFileProvider {
* @param path Relative path to the file.
* @returns Promise to be resolved when the size is calculated.
*/
getFileSize(path: string): Promise<number> {
async getFileSize(path: string): Promise<number> {
path = this.removeBasePath(path);
this.logger.debug('Get size of file: ' + path);
return this.getFile(path).then((fileEntry) => this.getSize(fileEntry));
const fileEntry = await this.getFile(path);
return this.getSize(fileEntry);
}
/**
@ -418,16 +421,15 @@ export class CoreFileProvider {
*
* @returns Promise resolved with the estimated free space in bytes.
*/
calculateFreeSpace(): Promise<number> {
return File.getFreeDiskSpace().then((size) => {
if (CorePlatform.isIOS()) {
// In iOS the size is in bytes.
return Number(size);
}
async calculateFreeSpace(): Promise<number> {
const size = await File.getFreeDiskSpace();
// The size is in KB, convert it to bytes.
return Number(size) * 1024;
});
if (CorePlatform.isIOS()) {
// In iOS the size is in bytes.
return Number(size);
}
return Number(size) * 1024;
}
/**
@ -642,8 +644,10 @@ export class CoreFileProvider {
* @param fullPath Absolute path to the file.
* @returns Promise to be resolved when the file is retrieved.
*/
getExternalFile(fullPath: string): Promise<FileEntry> {
return File.resolveLocalFilesystemUrl(fullPath).then((entry) => <FileEntry> entry);
async getExternalFile(fullPath: string): Promise<FileEntry> {
const entry = await File.resolveLocalFilesystemUrl(fullPath);
return <FileEntry>entry;
}
/**
@ -676,14 +680,14 @@ export class CoreFileProvider {
*
* @returns Promise to be resolved when the base path is retrieved.
*/
getBasePath(): Promise<string> {
return this.init().then(() => {
if (this.basePath.slice(-1) == '/') {
return this.basePath;
} else {
return this.basePath + '/';
}
});
async getBasePath(): Promise<string> {
await this.init();
if (this.basePath.slice(-1) === '/') {
return this.basePath;
} else {
return this.basePath + '/';
}
}
/**
@ -1004,15 +1008,10 @@ export class CoreFileProvider {
* @param isDir True if directory, false if file.
* @returns Promise resolved with metadata.
*/
getMetadataFromPath(path: string, isDir?: boolean): Promise<Metadata> {
let promise;
if (isDir) {
promise = this.getDir(path);
} else {
promise = this.getFile(path);
}
async getMetadataFromPath(path: string, isDir?: boolean): Promise<Metadata> {
const entry = isDir ? await this.getDir(path) : await this.getFile(path);
return promise.then((entry) => this.getMetadata(entry));
return this.getMetadata(entry);
}
/**

View File

@ -52,6 +52,7 @@ import { CoreToasts, ToastDuration, ShowToastOptions } from '../toasts';
import { fixOverlayAriaHidden } from '@/core/utils/fix-aria-hidden';
import { CoreModals, OpenModalOptions } from '@services/modals';
import { CorePopovers, OpenPopoverOptions } from '@services/popovers';
import { CoreViewer } from '@features/viewer/services/viewer';
/*
* "Utils" service with helper functions for UI, DOM elements and HTML code.
@ -1525,6 +1526,8 @@ export class CoreDomUtilsProvider {
* @param title Title of the page or modal.
* @param component Component to link the image to if needed.
* @param componentId An ID to use in conjunction with the component.
*
* @deprecated since 4.5. Use CoreViewer.viewImage instead.
*/
async viewImage(
image: string,
@ -1532,22 +1535,7 @@ export class CoreDomUtilsProvider {
component?: string,
componentId?: string | number,
): Promise<void> {
if (!image) {
return;
}
const { CoreViewerImageComponent } = await import('@features/viewer/components/image/image');
await CoreModals.openModal({
component: CoreViewerImageComponent,
componentProps: {
title,
image,
component,
componentId,
},
cssClass: 'core-modal-transparent',
});
await CoreViewer.viewImage(image, title, component, componentId);
}
/**

View File

@ -14,21 +14,19 @@
import { Injectable } from '@angular/core';
import { SafeUrl } from '@angular/platform-browser';
import { ModalOptions } from '@ionic/core';
import { CoreAnyError, CoreError } from '@classes/errors/error';
import { DomSanitizer, makeSingleton, Translate } from '@singletons';
import { CoreWSFile } from '@services/ws';
import { Locutus } from '@singletons/locutus';
import { CoreFileHelper } from '@services/file-helper';
import { CoreModals } from '@services/modals';
import { CoreUrl } from '@singletons/url';
import { AlertButton } from '@ionic/angular';
import { CorePath } from '@singletons/path';
import { CorePlatform } from '@services/platform';
import { ContextLevel } from '@/core/constants';
import { CoreDom } from '@singletons/dom';
import { CoreText } from '@singletons/text';
import { CoreViewer, CoreViewerTextOptions } from '@features/viewer/services/viewer';
/**
* Different type of errors the app can treat.
@ -1030,27 +1028,11 @@ export class CoreTextUtilsProvider {
* @param title Title of the new state.
* @param content Content of the text to be expanded.
* @param options Options.
* @returns Promise resolved when the modal is displayed.
*
* @deprecated since 4.5. Use CoreViewer.viewText instead.
*/
async viewText(title: string, content: string, options?: CoreTextUtilsViewTextOptions): Promise<void> {
if (!content.length) {
return;
}
options = options || {};
const { CoreViewerTextComponent } = await import('@features/viewer/components/text/text');
const modalOptions: ModalOptions = Object.assign(options.modalOptions || {}, {
component: CoreViewerTextComponent,
});
delete options.modalOptions;
modalOptions.componentProps = {
title,
content,
...options,
};
await CoreModals.openModal(modalOptions);
async viewText(title: string, content: string, options?: CoreViewerTextOptions): Promise<void> {
await CoreViewer.viewText(title, content, options);
}
}
@ -1058,18 +1040,10 @@ export const CoreTextUtils = makeSingleton(CoreTextUtilsProvider);
/**
* Options for viewText.
*
* @deprecated since 4.5. Use CoreViewerTextOptions instead.
*/
export type CoreTextUtilsViewTextOptions = {
component?: string; // Component to link the embedded files to.
componentId?: string | number; // An ID to use in conjunction with the component.
files?: CoreWSFile[]; // List of files to display along with the text.
filter?: boolean; // Whether the text should be filtered.
contextLevel?: ContextLevel; // The context level.
instanceId?: number; // The instance ID related to the context.
courseId?: number; // Course ID the text belongs to. It can be used to improve performance with filters.
displayCopyButton?: boolean; // Whether to display a button to copy the text.
modalOptions?: Partial<ModalOptions>; // Modal options.
};
export type CoreTextUtilsViewTextOptions = CoreViewerTextOptions;
/**
* Define text formatting types.