Merge pull request #3868 from NoelDeMartin/MOBILE-3947

MOBILE-3947: Fix build
main
Dani Palou 2023-11-30 10:28:53 +01:00 committed by GitHub
commit 19ad6eec86
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 303 additions and 160 deletions

View File

@ -47,6 +47,7 @@ const appConfig = {
Object: { Object: {
message: 'Use {} instead.', message: 'Use {} instead.',
}, },
Function: false,
}, },
}, },
], ],

View File

@ -9,12 +9,12 @@
}, },
"editor.formatOnSave": true, "editor.formatOnSave": true,
"eslint.format.enable": true, "eslint.format.enable": true,
"html.format.endWithNewline": true,
"html.format.wrapLineLength": 140, "html.format.wrapLineLength": 140,
"files.eol": "\n", "files.eol": "\n",
"files.trimFinalNewlines": true, "files.trimFinalNewlines": true,
"files.insertFinalNewline": true, "files.insertFinalNewline": true,
"files.trimTrailingWhitespace": true, "files.trimTrailingWhitespace": true,
"typescript.tsdk": "./node_modules/typescript/lib",
/** /**
* Config files. * Config files.

110
package-lock.json generated
View File

@ -39,7 +39,6 @@
"@awesome-cordova-plugins/sqlite": "^6.3.0", "@awesome-cordova-plugins/sqlite": "^6.3.0",
"@awesome-cordova-plugins/status-bar": "^6.3.0", "@awesome-cordova-plugins/status-bar": "^6.3.0",
"@awesome-cordova-plugins/web-intent": "^6.3.0", "@awesome-cordova-plugins/web-intent": "^6.3.0",
"@awesome-cordova-plugins/zip": "^6.3.0",
"@ionic/angular": "^7.0.0", "@ionic/angular": "^7.0.0",
"@ionic/cordova-builders": "^10.0.0", "@ionic/cordova-builders": "^10.0.0",
"@moodlehq/cordova-plugin-advanced-http": "3.3.1-moodle.1", "@moodlehq/cordova-plugin-advanced-http": "3.3.1-moodle.1",
@ -142,7 +141,7 @@
"jest-preset-angular": "^13.1.4", "jest-preset-angular": "^13.1.4",
"jsonc-parser": "^2.3.1", "jsonc-parser": "^2.3.1",
"keytar": "^7.2.0", "keytar": "^7.2.0",
"minimatch": "^5.1.0", "minimatch": "^9.0.3",
"native-run": "^2.0.0", "native-run": "^2.0.0",
"patch-package": "^6.5.0", "patch-package": "^6.5.0",
"ts-jest": "^29.1.1", "ts-jest": "^29.1.1",
@ -1034,18 +1033,6 @@
"rxjs": "^5.5.0 || ^6.5.0 || ^7.3.0" "rxjs": "^5.5.0 || ^6.5.0 || ^7.3.0"
} }
}, },
"node_modules/@awesome-cordova-plugins/zip": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/@awesome-cordova-plugins/zip/-/zip-6.4.0.tgz",
"integrity": "sha512-s6Bg+sBepwhVvN+8fdns8QOOY5Mo5pg9Iy1aJiFDBUipuQOLaO++zw5u+no0igObnYcaQAdSO2XyEBX/h0T59g==",
"dependencies": {
"@types/cordova": "latest"
},
"peerDependencies": {
"@awesome-cordova-plugins/core": "^6.0.1",
"rxjs": "^5.5.0 || ^6.5.0 || ^7.3.0"
}
},
"node_modules/@babel/code-frame": { "node_modules/@babel/code-frame": {
"version": "7.23.4", "version": "7.23.4",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.4.tgz", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.4.tgz",
@ -6058,21 +6045,6 @@
"node": "^14.17.0 || ^16.13.0 || >=18.0.0" "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
} }
}, },
"node_modules/@tufjs/models/node_modules/minimatch": {
"version": "9.0.3",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
"integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
"dev": true,
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=16 || 14 >=14.17"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@types/babel__core": { "node_modules/@types/babel__core": {
"version": "7.20.5", "version": "7.20.5",
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
@ -8701,21 +8673,6 @@
"node": ">=12" "node": ">=12"
} }
}, },
"node_modules/cacache/node_modules/minimatch": {
"version": "9.0.3",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
"integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
"dev": true,
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=16 || 14 >=14.17"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/cacache/node_modules/minipass": { "node_modules/cacache/node_modules/minipass": {
"version": "7.0.4", "version": "7.0.4",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz",
@ -13756,6 +13713,18 @@
"minimatch": "^5.0.1" "minimatch": "^5.0.1"
} }
}, },
"node_modules/filelist/node_modules/minimatch": {
"version": "5.1.6",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
"dev": true,
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=10"
}
},
"node_modules/fill-range": { "node_modules/fill-range": {
"version": "7.0.1", "version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
@ -16040,21 +16009,6 @@
"node": "^14.17.0 || ^16.13.0 || >=18.0.0" "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
} }
}, },
"node_modules/ignore-walk/node_modules/minimatch": {
"version": "9.0.3",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
"integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
"dev": true,
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=16 || 14 >=14.17"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/image-size": { "node_modules/image-size": {
"version": "0.5.5", "version": "0.5.5",
"resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz",
@ -20756,6 +20710,18 @@
"node": ">=12" "node": ">=12"
} }
}, },
"node_modules/make-fetch-happen/node_modules/minimatch": {
"version": "5.1.6",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
"dev": true,
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=10"
}
},
"node_modules/make-fetch-happen/node_modules/minipass": { "node_modules/make-fetch-happen/node_modules/minipass": {
"version": "3.3.6", "version": "3.3.6",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
@ -21237,15 +21203,18 @@
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
}, },
"node_modules/minimatch": { "node_modules/minimatch": {
"version": "5.1.6", "version": "9.0.3",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"brace-expansion": "^2.0.1" "brace-expansion": "^2.0.1"
}, },
"engines": { "engines": {
"node": ">=10" "node": ">=16 || 14 >=14.17"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
} }
}, },
"node_modules/minimist": { "node_modules/minimist": {
@ -23193,21 +23162,6 @@
"node": "^14.17.0 || ^16.13.0 || >=18.0.0" "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
} }
}, },
"node_modules/pacote/node_modules/minimatch": {
"version": "9.0.3",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
"integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
"dev": true,
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=16 || 14 >=14.17"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/pacote/node_modules/normalize-package-data": { "node_modules/pacote/node_modules/normalize-package-data": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-5.0.0.tgz", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-5.0.0.tgz",

View File

@ -74,7 +74,6 @@
"@awesome-cordova-plugins/sqlite": "^6.3.0", "@awesome-cordova-plugins/sqlite": "^6.3.0",
"@awesome-cordova-plugins/status-bar": "^6.3.0", "@awesome-cordova-plugins/status-bar": "^6.3.0",
"@awesome-cordova-plugins/web-intent": "^6.3.0", "@awesome-cordova-plugins/web-intent": "^6.3.0",
"@awesome-cordova-plugins/zip": "^6.3.0",
"@ionic/angular": "^7.0.0", "@ionic/angular": "^7.0.0",
"@ionic/cordova-builders": "^10.0.0", "@ionic/cordova-builders": "^10.0.0",
"@moodlehq/cordova-plugin-advanced-http": "3.3.1-moodle.1", "@moodlehq/cordova-plugin-advanced-http": "3.3.1-moodle.1",
@ -177,7 +176,7 @@
"jest-preset-angular": "^13.1.4", "jest-preset-angular": "^13.1.4",
"jsonc-parser": "^2.3.1", "jsonc-parser": "^2.3.1",
"keytar": "^7.2.0", "keytar": "^7.2.0",
"minimatch": "^5.1.0", "minimatch": "^9.0.3",
"native-run": "^2.0.0", "native-run": "^2.0.0",
"patch-package": "^6.5.0", "patch-package": "^6.5.0",
"ts-jest": "^29.1.1", "ts-jest": "^29.1.1",

View File

@ -218,7 +218,7 @@ export class AddonModLessonUserRetakePage implements OnInit {
* @returns Formatted data. * @returns Formatted data.
*/ */
protected formatRetake(retakeData: AddonModLessonGetUserAttemptWSResponse): RetakeToDisplay { protected formatRetake(retakeData: AddonModLessonGetUserAttemptWSResponse): RetakeToDisplay {
const formattedData = <RetakeToDisplay> retakeData; const formattedData = retakeData;
if (formattedData.userstats.gradeinfo) { if (formattedData.userstats.gradeinfo) {
// Completed. // Completed.
@ -229,19 +229,23 @@ export class AddonModLessonUserRetakePage implements OnInit {
// Format pages data. // Format pages data.
formattedData.answerpages.forEach((page) => { formattedData.answerpages.forEach((page) => {
if (AddonModLesson.answerPageIsContent(page)) { if (AddonModLesson.answerPageIsContent(page)) {
page.isContent = true; const contentPage = page as AnswerPage;
if (page.answerdata?.answers) { contentPage.isContent = true;
page.answerdata.answers.forEach((answer) => {
if (contentPage.answerdata?.answers) {
contentPage.answerdata.answers.forEach((answer) => {
// Content pages only have 1 valid field in the answer array. // Content pages only have 1 valid field in the answer array.
answer[0] = AddonModLessonHelper.getContentPageAnswerDataFromHtml(answer[0]); answer[0] = AddonModLessonHelper.getContentPageAnswerDataFromHtml(answer[0]);
}); });
} }
} else if (AddonModLesson.answerPageIsQuestion(page)) { } else if (AddonModLesson.answerPageIsQuestion(page)) {
page.isQuestion = true; const questionPage = page as AnswerPage;
if (page.answerdata?.answers) { questionPage.isQuestion = true;
page.answerdata.answers.forEach((answer) => {
if (questionPage.answerdata?.answers) {
questionPage.answerdata.answers.forEach((answer) => {
// Only the first field of the answer array requires to be parsed. // Only the first field of the answer array requires to be parsed.
answer[0] = AddonModLessonHelper.getQuestionPageAnswerDataFromHtml(answer[0]); answer[0] = AddonModLessonHelper.getQuestionPageAnswerDataFromHtml(answer[0]);
}); });

View File

@ -270,7 +270,7 @@ export class AddonModLessonHelperProvider {
if (option.checked || multiChoiceQuestion.multi) { if (option.checked || multiChoiceQuestion.multi) {
// Add the control. // Add the control.
const value = multiChoiceQuestion.multi ? const value = multiChoiceQuestion.multi ?
{ value: option.checked, disabled: option.disabled } : option.value; { value: option.checked, disabled: option.disabled } : option.checked;
questionForm.addControl(option.name, this.formBuilder.control(value)); questionForm.addControl(option.name, this.formBuilder.control(value));
controlAdded = true; controlAdded = true;
} }

View File

@ -337,7 +337,7 @@ export class AddonModScormProvider {
const re = /^(\d+)\*\{(.+)\}$/; // Sets like 3*{S34, S36, S37, S39}. const re = /^(\d+)\*\{(.+)\}$/; // Sets like 3*{S34, S36, S37, S39}.
const reOther = /^(.+)(=|<>)(.+)$/; // Other symbols. const reOther = /^(.+)(=|<>)(.+)$/; // Other symbols.
let matches = element.match(re); const matches = element.match(re);
if (matches) { if (matches) {
const repeat = Number(matches[1]); const repeat = Number(matches[1]);
@ -363,18 +363,18 @@ export class AddonModScormProvider {
element = '!'; element = '!';
} else if (reOther.test(element)) { } else if (reOther.test(element)) {
// Other symbols = | <> . // Other symbols = | <> .
matches = element.match(reOther) ?? []; const otherMatches = element.match(reOther) ?? [];
element = matches[1]?.trim(); element = otherMatches[1]?.trim();
if (trackData[element] !== undefined) { if (trackData[element] !== undefined) {
let value = matches[3].trim().replace(/('|")/gi, ''); let value = otherMatches[3].trim().replace(/('|")/gi, '');
let oper: string; let oper: string;
if (STATUSES[value] !== undefined) { if (STATUSES[value] !== undefined) {
value = STATUSES[value]; value = STATUSES[value];
} }
if (matches[2] == '<>') { if (otherMatches[2] == '<>') {
oper = '!='; oper = '!=';
} else { } else {
oper = '=='; oper = '==';

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import { InjectionToken, Injector, ModuleWithProviders, NgModule } from '@angular/core'; import { InjectionToken, Injector, ModuleWithProviders, NgModule, Type } from '@angular/core';
import { import {
PreloadAllModules, PreloadAllModules,
RouterModule, RouterModule,
@ -97,6 +97,12 @@ function buildConditionalUrlMatcher(pathOrMatcher: string | UrlMatcher, conditio
}; };
} }
/**
* Type to declare lazy route modules.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type LazyRoutesModule = Type<any>;
/** /**
* Build url matcher using a regular expression. * Build url matcher using a regular expression.
* *

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import { ApplicationInitStatus, APP_INITIALIZER, Injectable, Injector } from '@angular/core'; import { ApplicationInitStatus, Injectable, Injector } from '@angular/core';
import { setSingletonsInjector } from '@singletons'; import { setSingletonsInjector } from '@singletons';
@Injectable() @Injectable()
@ -21,7 +21,7 @@ export class CoreApplicationInitStatus extends ApplicationInitStatus {
constructor(injector: Injector) { constructor(injector: Injector) {
setSingletonsInjector(injector); setSingletonsInjector(injector);
super(injector.get(APP_INITIALIZER, [])); super();
} }
whenDone(callback: () => unknown): void { whenDone(callback: () => unknown): void {

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import { Injectable, Pipe } from '@angular/core'; import { Injectable, Pipe, PipeTransform } from '@angular/core';
import { TranslatePipe } from '@ngx-translate/core'; import { TranslatePipe } from '@ngx-translate/core';
/** /**
@ -25,4 +25,4 @@ import { TranslatePipe } from '@ngx-translate/core';
pure: false, // required to update the value when the promise is resolved pure: false, // required to update the value when the promise is resolved
standalone: true, standalone: true,
}) })
export class TranslatePipeForCompile extends TranslatePipe {} export class TranslatePipeForCompile extends TranslatePipe implements PipeTransform {}

View File

@ -75,6 +75,7 @@ import { CoreRemindersPushNotificationData } from '@features/reminders/services/
import { CoreLocalNotifications } from '@services/local-notifications'; import { CoreLocalNotifications } from '@services/local-notifications';
import { CoreEnrol } from '@features/enrol/services/enrol'; import { CoreEnrol } from '@features/enrol/services/enrol';
import { CoreEnrolAction, CoreEnrolDelegate } from '@features/enrol/services/enrol-delegate'; import { CoreEnrolAction, CoreEnrolDelegate } from '@features/enrol/services/enrol-delegate';
import { LazyRoutesModule } from '@/app/app-routing.module';
/** /**
* Prefetch info of a module. * Prefetch info of a module.
@ -1990,7 +1991,7 @@ export class CoreCourseHelperProvider {
* *
* @returns Course summary page module. * @returns Course summary page module.
*/ */
async getCourseSummaryRouteModule(): Promise<unknown> { async getCourseSummaryRouteModule(): Promise<LazyRoutesModule> {
return import('../course-summary-lazy.module').then(m => m.CoreCourseSummaryLazyModule); return import('../course-summary-lazy.module').then(m => m.CoreCourseSummaryLazyModule);
} }

View File

@ -30,6 +30,7 @@ import { of, firstValueFrom } from 'rxjs';
import { zipIncludingComplete } from '@/core/utils/rxjs'; import { zipIncludingComplete } from '@/core/utils/rxjs';
import { catchError, map } from 'rxjs/operators'; import { catchError, map } from 'rxjs/operators';
import { chainRequests, WSObservable } from '@classes/sites/authenticated-site'; import { chainRequests, WSObservable } from '@classes/sites/authenticated-site';
import { LazyRoutesModule } from '@/app/app-routing.module';
// Id for a course item representing all courses (for example, for course filters). // Id for a course item representing all courses (for example, for course filters).
export const ALL_COURSES_ID = -1; export const ALL_COURSES_ID = -1;
@ -432,7 +433,7 @@ export class CoreCoursesHelperProvider {
* *
* @returns My courses page module. * @returns My courses page module.
*/ */
async getMyRouteModule(): Promise<unknown> { async getMyRouteModule(): Promise<LazyRoutesModule> {
return import('../courses-my-lazy.module').then(m => m.CoreCoursesMyLazyModule); return import('../courses-my-lazy.module').then(m => m.CoreCoursesMyLazyModule);
} }

View File

@ -27,7 +27,7 @@ import { Geolocation } from '@awesome-cordova-plugins/geolocation/ngx';
import { InAppBrowser } from '@awesome-cordova-plugins/in-app-browser/ngx'; import { InAppBrowser } from '@awesome-cordova-plugins/in-app-browser/ngx';
import { LocalNotifications } from '@awesome-cordova-plugins/local-notifications/ngx'; import { LocalNotifications } from '@awesome-cordova-plugins/local-notifications/ngx';
import { MediaCapture } from '@awesome-cordova-plugins/media-capture/ngx'; import { MediaCapture } from '@awesome-cordova-plugins/media-capture/ngx';
import { Zip } from '@awesome-cordova-plugins/zip/ngx'; import { Zip } from '@features/native/plugins/zip';
// Mock services. // Mock services.
import { CameraMock } from './services/camera'; import { CameraMock } from './services/camera';

View File

@ -12,8 +12,19 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
/* eslint-disable deprecation/deprecation */
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { File, Entry, DirectoryEntry, FileEntry, IWriteOptions, RemoveResult } from '@awesome-cordova-plugins/file/ngx'; import {
File,
Entry,
FileEntry,
FileSystem,
IWriteOptions,
RemoveResult,
DirectoryEntry,
DirectoryReader,
} from '@awesome-cordova-plugins/file/ngx';
import { CorePath } from '@singletons/path'; import { CorePath } from '@singletons/path';
/** /**
@ -42,6 +53,82 @@ class FileError {
} }
/**
* Native APIs used in webkit window.
*/
interface WebkitWindow {
/**
* @deprecated
* @see https://www.w3.org/TR/2012/WD-file-system-api-20120417/
*/
// eslint-disable-next-line @typescript-eslint/naming-convention
LocalFileSystem: {
readonly TEMPORARY: number;
readonly PERSISTENT: number;
};
/**
* @deprecated
* @see https://developer.mozilla.org/en-US/docs/Web/API/Window/requestFileSystem
*/
requestFileSystem(
type: LocalFileSystem,
size: number,
successCallback: (fileSystem: FileSystem) => void,
errorCallback?: (fileError: FileError) => void,
): void;
/**
* @deprecated
*/
webkitRequestFileSystem(
type: LocalFileSystem,
size: number,
successCallback: (fileSystem: FileSystem) => void,
errorCallback?: (fileError: FileError) => void,
): void;
/**
* @deprecated
* @see https://www.w3.org/TR/2012/WD-file-system-api-20120417/
*/
resolveLocalFileSystemURL(
url: string,
successCallback: (entry: Entry) => void,
errorCallback?: (fileError: FileError) => void,
): void;
/**
* @deprecated
*/
webkitResolveLocalFileSystemURL(
url: string,
successCallback: (entry: Entry) => void,
errorCallback?: (fileError: FileError) => void,
): void;
}
/**
* Native APIs used in webkit navigator.
*/
interface WebkitNavigator {
/**
* @deprecated
* @see https://developer.chrome.com/docs/apps/offline_storage/
*/
webkitPersistentStorage: {
requestQuota(
newQuotaInBytes: number,
successCallback?: (bytesGranted: number) => void,
errorCallback?: (error: Error) => void,
): void;
};
}
/** /**
* Emulates the Cordova File plugin in browser. * Emulates the Cordova File plugin in browser.
* Most of the code is extracted from the File class of Ionic Native. * Most of the code is extracted from the File class of Ionic Native.
@ -285,39 +372,40 @@ export class FileMock extends File {
*/ */
async getFreeDiskSpace(): Promise<number> { async getFreeDiskSpace(): Promise<number> {
// Request a file system instance with a minimum size until we get an error. // Request a file system instance with a minimum size until we get an error.
if (window.requestFileSystem) { const window = this.getEmulatorWindow();
let iterations = 0;
let maxIterations = 50;
const calculateByRequest = (size: number, ratio: number): Promise<number> =>
new Promise((resolve): void => {
window.requestFileSystem(LocalFileSystem.PERSISTENT, size, () => {
iterations++;
if (iterations > maxIterations) {
resolve(size);
return; if (!window.requestFileSystem) {
}
// eslint-disable-next-line promise/catch-or-return
calculateByRequest(size * ratio, ratio).then(resolve);
}, () => {
resolve(size / ratio);
});
});
// General calculation, base 1MB and increasing factor 1.3.
let size = await calculateByRequest(1048576, 1.3);
// More accurate. Factor is 1.1.
iterations = 0;
maxIterations = 10;
size = await calculateByRequest(size, 1.1);
return size / 1024; // Return size in KB.
} else {
throw new Error('File system not available.'); throw new Error('File system not available.');
} }
let iterations = 0;
let maxIterations = 50;
const calculateByRequest = (size: number, ratio: number): Promise<number> =>
new Promise((resolve): void => {
window.requestFileSystem(LocalFileSystem.PERSISTENT, size, () => {
iterations++;
if (iterations > maxIterations) {
resolve(size);
return;
}
// eslint-disable-next-line promise/catch-or-return
calculateByRequest(size * ratio, ratio).then(resolve);
}, () => {
resolve(size / ratio);
});
});
// General calculation, base 1MB and increasing factor 1.3.
let size = await calculateByRequest(1048576, 1.3);
// More accurate. Factor is 1.1.
iterations = 0;
maxIterations = 10;
size = await calculateByRequest(size, 1.1);
return size / 1024; // Return size in KB.
} }
/** /**
@ -342,24 +430,23 @@ export class FileMock extends File {
*/ */
load(): Promise<string> { load(): Promise<string> {
return new Promise((resolve, reject): void => { return new Promise((resolve, reject): void => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any const window = this.getEmulatorWindow();
const win = <any> window; // Convert to <any> to be able to use non-standard properties.
if (win.requestFileSystem === undefined) { if (window.requestFileSystem === undefined) {
win.requestFileSystem = win.webkitRequestFileSystem; window.requestFileSystem = window.webkitRequestFileSystem;
} }
if (win.resolveLocalFileSystemURL === undefined) { if (window.resolveLocalFileSystemURL === undefined) {
win.resolveLocalFileSystemURL = win.webkitResolveLocalFileSystemURL; window.resolveLocalFileSystemURL = window.webkitResolveLocalFileSystemURL;
} }
win.LocalFileSystem = { window.LocalFileSystem = {
TEMPORARY: 0, // eslint-disable-line @typescript-eslint/naming-convention
PERSISTENT: 1, // eslint-disable-line @typescript-eslint/naming-convention PERSISTENT: 1, // eslint-disable-line @typescript-eslint/naming-convention
}; };
// Request a quota to use. Request 500MB. // Request a quota to use. Request 500MB.
// eslint-disable-next-line @typescript-eslint/no-explicit-any this.getEmulatorNavigator().webkitPersistentStorage.requestQuota(500 * 1024 * 1024, (granted) => {
(<any> navigator).webkitPersistentStorage.requestQuota(500 * 1024 * 1024, (granted) => { window.requestFileSystem(LocalFileSystem.PERSISTENT, granted, (fileSystem: FileSystem) => {
window.requestFileSystem(LocalFileSystem.PERSISTENT, granted, (entry) => { resolve(fileSystem.root.toURL());
resolve(entry.root.toURL());
}, reject); }, reject);
}, reject); }, reject);
}); });
@ -642,7 +729,7 @@ export class FileMock extends File {
resolveLocalFilesystemUrl(fileUrl: string): Promise<Entry> { resolveLocalFilesystemUrl(fileUrl: string): Promise<Entry> {
return new Promise<Entry>((resolve, reject): void => { return new Promise<Entry>((resolve, reject): void => {
try { try {
window.resolveLocalFileSystemURL(fileUrl, (entry: Entry) => { this.getEmulatorWindow().resolveLocalFileSystemURL(fileUrl, (entry: Entry) => {
resolve(entry); resolve(entry);
}, (error: FileError) => { }, (error: FileError) => {
this.fillErrorMessageMock(error); this.fillErrorMessageMock(error);
@ -799,4 +886,22 @@ export class FileMock extends File {
}); });
} }
/**
* Get emulator window.
*
* @returns Emulator window.
*/
private getEmulatorWindow(): WebkitWindow {
return window as unknown as WebkitWindow;
}
/**
* Get emulator navigator.
*
* @returns Emulator navigator.
*/
private getEmulatorNavigator(): WebkitNavigator {
return navigator as unknown as WebkitNavigator;
}
} }

View File

@ -13,7 +13,7 @@
// limitations under the License. // limitations under the License.
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Zip } from '@awesome-cordova-plugins/zip/ngx'; import { Zip } from '@features/native/plugins/zip';
import * as JSZip from 'jszip'; import * as JSZip from 'jszip';
import { CorePath } from '@singletons/path'; import { CorePath } from '@singletons/path';
import { File } from '@singletons'; import { File } from '@singletons';
@ -54,7 +54,6 @@ export class ZipMock extends Zip {
* @returns Promise that resolves with a number. 0 is success, -1 is error. * @returns Promise that resolves with a number. 0 is success, -1 is error.
*/ */
async unzip(source: string, destination: string, onProgress?: (ev: {loaded: number; total: number}) => void): Promise<number> { async unzip(source: string, destination: string, onProgress?: (ev: {loaded: number; total: number}) => void): Promise<number> {
// Replace all %20 with spaces. // Replace all %20 with spaces.
source = source.replace(/%20/g, ' '); source = source.replace(/%20/g, ' ');
destination = destination.replace(/%20/g, ' '); destination = destination.replace(/%20/g, ' ');

View File

@ -56,6 +56,7 @@ import {
IDENTITY_PROVIDERS_FEATURE_NAME, IDENTITY_PROVIDERS_FEATURE_NAME,
IDENTITY_PROVIDER_FEATURE_NAME_PREFIX, IDENTITY_PROVIDER_FEATURE_NAME_PREFIX,
} from '../constants'; } from '../constants';
import { LazyRoutesModule } from '@/app/app-routing.module';
/** /**
* Helper provider that provides some common features regarding authentication. * Helper provider that provides some common features regarding authentication.
@ -1440,7 +1441,7 @@ export class CoreLoginHelperProvider {
* *
* @returns Reconnect page route module. * @returns Reconnect page route module.
*/ */
async getReconnectRouteModule(): Promise<unknown> { async getReconnectRouteModule(): Promise<LazyRoutesModule> {
return import('@features/login/login-reconnect-lazy.module').then(m => m.CoreLoginReconnectLazyModule); return import('@features/login/login-reconnect-lazy.module').then(m => m.CoreLoginReconnectLazyModule);
} }
@ -1449,7 +1450,7 @@ export class CoreLoginHelperProvider {
* *
* @returns Credentials page route module. * @returns Credentials page route module.
*/ */
async getCredentialsRouteModule(): Promise<unknown> { async getCredentialsRouteModule(): Promise<LazyRoutesModule> {
return import('@features/login/login-credentials-lazy.module').then(m => m.CoreLoginCredentialsLazyModule); return import('@features/login/login-credentials-lazy.module').then(m => m.CoreLoginCredentialsLazyModule);
} }

View File

@ -246,7 +246,7 @@ export class CoreMainMenuUserMenuComponent implements OnInit, OnDestroy {
cssClass: 'core-modal-lateral core-modal-lateral-sm', cssClass: 'core-modal-lateral core-modal-lateral-sm',
}); });
if (closeAll) { if (thisModal && closeAll) {
await ModalController.dismiss(undefined, undefined, thisModal.id); await ModalController.dismiss(undefined, undefined, thisModal.id);
} }
} }

View File

@ -36,7 +36,7 @@ import { SplashScreen } from '@awesome-cordova-plugins/splash-screen/ngx';
import { SQLite } from '@awesome-cordova-plugins/sqlite/ngx'; import { SQLite } from '@awesome-cordova-plugins/sqlite/ngx';
import { StatusBar } from '@awesome-cordova-plugins/status-bar/ngx'; import { StatusBar } from '@awesome-cordova-plugins/status-bar/ngx';
import { WebIntent } from '@awesome-cordova-plugins/web-intent/ngx'; import { WebIntent } from '@awesome-cordova-plugins/web-intent/ngx';
import { Zip } from '@awesome-cordova-plugins/zip/ngx'; import { Zip } from '@features/native/plugins/zip';
export const CORE_NATIVE_SERVICES = [ export const CORE_NATIVE_SERVICES = [
Badge, Badge,
@ -87,7 +87,6 @@ export const CORE_NATIVE_SERVICES = [
StatusBar, StatusBar,
WebIntent, WebIntent,
WebView, WebView,
Zip,
], ],
}) })
export class CoreNativeModule {} export class CoreNativeModule {}

View File

@ -14,5 +14,7 @@
import { makeSingleton } from '@singletons'; import { makeSingleton } from '@singletons';
import { Chooser as ChooserService } from './chooser'; import { Chooser as ChooserService } from './chooser';
import { Zip as ZipService } from './zip';
export const Chooser = makeSingleton(ChooserService); export const Chooser = makeSingleton(ChooserService);
export const Zip = makeSingleton(ZipService);

View File

@ -0,0 +1,35 @@
// (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 { Injectable } from '@angular/core';
/**
* Zip plugin wrapper.
*/
@Injectable({ providedIn: 'root' })
export class Zip {
/**
* Extracts files from a ZIP archive
*
* @param source Source ZIP file
* @param destination Destination folder
* @param onProgress Callback to be called on progress update
* @returns 0 is success, -1 is error
*/
unzip(source: string, destination: string, onProgress?: (ev: {loaded: number; total: number}) => void): Promise<number> {
return new Promise(resolve => window.zip.unzip(source, destination, (result: number) => resolve(result), onProgress));
}
}

View File

@ -235,7 +235,8 @@ export class CoreSitePluginsPluginContentComponent implements OnInit, DoCheck {
this.args = args; this.args = args;
this.dataLoaded = false; this.dataLoaded = false;
this.preSets = preSets || this.preSets; this.preSets = preSets || this.preSets;
if (jsData) {
if (this.data && jsData) {
Object.assign(this.data, jsData); Object.assign(this.data, jsData);
} }

View File

@ -613,7 +613,6 @@ export class CoreSitePluginsHelperProvider {
for (const property of handlerProperties) { for (const property of handlerProperties) {
if (property !== 'constructor' && typeof handler[property] === 'function' && if (property !== 'constructor' && typeof handler[property] === 'function' &&
typeof jsResult[property] === 'function') { typeof jsResult[property] === 'function') {
// eslint-disable-next-line @typescript-eslint/ban-types
handler[property] = (<Function> jsResult[property]).bind(handler); handler[property] = (<Function> jsResult[property]).bind(handler);
} }
} }
@ -837,7 +836,6 @@ export class CoreSitePluginsHelperProvider {
for (const property of handlerProperties) { for (const property of handlerProperties) {
if (property !== 'constructor' && typeof handler[property] === 'function' && if (property !== 'constructor' && typeof handler[property] === 'function' &&
typeof jsResult[property] === 'function') { typeof jsResult[property] === 'function') {
// eslint-disable-next-line @typescript-eslint/ban-types
handler[property] = (<Function> jsResult[property]).bind(handler); handler[property] = (<Function> jsResult[property]).bind(handler);
} }
} }

View File

@ -14,6 +14,17 @@
import { CorePlatform } from '@services/platform'; import { CorePlatform } from '@services/platform';
import { CoreIframeUtils } from '@services/utils/iframe'; import { CoreIframeUtils } from '@services/utils/iframe';
import { WKUserScriptWindow } from 'cordova-plugin-wkuserscript';
/**
* Check Whether the window object has WKUserScript set.
*
* @param window Window object.
* @returns Whether the window object has WKUserScript set.
*/
function isWKUserScriptWindow(window: object): window is WKUserScriptWindow {
return CorePlatform.isIOS() && 'WKUserScript' in window;
}
/** /**
* Inject some scripts for iOS iframes. * Inject some scripts for iOS iframes.
@ -21,7 +32,7 @@ import { CoreIframeUtils } from '@services/utils/iframe';
export default async function(): Promise<void> { export default async function(): Promise<void> {
await CorePlatform.ready(); await CorePlatform.ready();
if (!CorePlatform.isIOS() || !('WKUserScript' in window)) { if (!isWKUserScriptWindow(window)) {
return; return;
} }

View File

@ -933,7 +933,6 @@ export class CoreFileProvider {
// If destFolder is not set, use same location as ZIP file. We need to use absolute paths (including basePath). // If destFolder is not set, use same location as ZIP file. We need to use absolute paths (including basePath).
destFolder = this.addBasePathIfNeeded(destFolder || CoreMimetypeUtils.removeExtension(path)); destFolder = this.addBasePathIfNeeded(destFolder || CoreMimetypeUtils.removeExtension(path));
// eslint-disable-next-line @typescript-eslint/ban-types
const result = await Zip.unzip(fileEntry.toURL(), destFolder, onProgress as unknown as Function); const result = await Zip.unzip(fileEntry.toURL(), destFolder, onProgress as unknown as Function);
if (result == -1) { if (result == -1) {

View File

@ -58,7 +58,6 @@ import { StatusBar as StatusBarService } from '@awesome-cordova-plugins/status-b
import { SplashScreen as SplashScreenService } from '@awesome-cordova-plugins/splash-screen/ngx'; import { SplashScreen as SplashScreenService } from '@awesome-cordova-plugins/splash-screen/ngx';
import { SQLite as SQLiteService } from '@awesome-cordova-plugins/sqlite/ngx'; import { SQLite as SQLiteService } from '@awesome-cordova-plugins/sqlite/ngx';
import { WebIntent as WebIntentService } from '@awesome-cordova-plugins/web-intent/ngx'; import { WebIntent as WebIntentService } from '@awesome-cordova-plugins/web-intent/ngx';
import { Zip as ZipService } from '@awesome-cordova-plugins/zip/ngx';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
@ -192,7 +191,6 @@ export const SplashScreen = makeSingleton(SplashScreenService);
export const SQLite = makeSingleton(SQLiteService); export const SQLite = makeSingleton(SQLiteService);
export const WebIntent = makeSingleton(WebIntentService); export const WebIntent = makeSingleton(WebIntentService);
export const WebView = makeSingleton(WebViewService); export const WebView = makeSingleton(WebViewService);
export const Zip = makeSingleton(ZipService);
export const Camera = makeSingleton(CameraService); export const Camera = makeSingleton(CameraService);

View File

@ -0,0 +1,27 @@
// (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.
/**
* Types for file cordova plugin.
*
* @see https://github.com/moodlemobile/cordova-plugin-zip
*/
interface Window {
zip: {
unzip(source: string, destination: string, onSuccess: Function, onProgress?: Function): void;
};
}

View File

@ -5,6 +5,7 @@
"outDir": "./dist/out-tsc", "outDir": "./dist/out-tsc",
"sourceMap": true, "sourceMap": true,
"declaration": false, "declaration": false,
"skipLibCheck": true,
"downlevelIteration": true, "downlevelIteration": true,
"experimentalDecorators": true, "experimentalDecorators": true,
"strictNullChecks": true, "strictNullChecks": true,
@ -14,7 +15,7 @@
"module": "esnext", "module": "esnext",
"moduleResolution": "node", "moduleResolution": "node",
"importHelpers": true, "importHelpers": true,
"target": "es2015", "target": "es2022",
"resolveJsonModule": true, "resolveJsonModule": true,
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"lib": [ "lib": [

View File

@ -2,6 +2,7 @@
"extends": "./tsconfig.json", "extends": "./tsconfig.json",
"compilerOptions": { "compilerOptions": {
"outDir": "./out-tsc/tests", "outDir": "./out-tsc/tests",
"target": "es2016",
"allowJs": true, "allowJs": true,
"esModuleInterop": true, "esModuleInterop": true,
"emitDecoratorMetadata": true, "emitDecoratorMetadata": true,