Merge pull request #4068 from NoelDeMartin/MOBILE-4470

MOBILE-4470 storagemanager: Fix removing courses
main
Dani Palou 2024-05-28 09:45:12 +02:00 committed by GitHub
commit 7e989c93d5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 61 additions and 7 deletions

View File

@ -14,6 +14,7 @@
import { DownloadStatus } from '@/core/constants'; import { DownloadStatus } from '@/core/constants';
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { CoreQueueRunner } from '@classes/queue-runner';
import { CoreCourse, CoreCourseProvider } from '@features/course/services/course'; import { CoreCourse, CoreCourseProvider } from '@features/course/services/course';
import { CoreCourseHelper } from '@features/course/services/course-helper'; import { CoreCourseHelper } from '@features/course/services/course-helper';
import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate'; import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate';
@ -50,6 +51,8 @@ export class AddonStorageManagerCoursesStoragePage implements OnInit, OnDestroy
courseStatusObserver?: CoreEventObserver; courseStatusObserver?: CoreEventObserver;
siteId: string; siteId: string;
private downloadedCoursesQueue = new CoreQueueRunner();
constructor() { constructor() {
this.siteId = CoreSites.getCurrentSiteId(); this.siteId = CoreSites.getCurrentSiteId();
} }
@ -87,7 +90,7 @@ export class AddonStorageManagerCoursesStoragePage implements OnInit, OnDestroy
} }
} }
await this.setDownloadedCourses(downloadedCourses); await this.downloadedCoursesQueue.run(() => this.setDownloadedCourses(downloadedCourses));
this.loaded = true; this.loaded = true;
} }
@ -124,7 +127,9 @@ export class AddonStorageManagerCoursesStoragePage implements OnInit, OnDestroy
try { try {
await Promise.all(deletedCourseIds.map((courseId) => CoreCourseHelper.deleteCourseFiles(courseId))); await Promise.all(deletedCourseIds.map((courseId) => CoreCourseHelper.deleteCourseFiles(courseId)));
await this.downloadedCoursesQueue.run(async () => {
await this.setDownloadedCourses(this.downloadedCourses.filter((course) => !deletedCourseIds.includes(course.id))); await this.setDownloadedCourses(this.downloadedCourses.filter((course) => !deletedCourseIds.includes(course.id)));
});
} catch (error) { } catch (error) {
CoreDomUtils.showErrorModalDefault(error, Translate.instant('core.errordeletefile')); CoreDomUtils.showErrorModalDefault(error, Translate.instant('core.errordeletefile'));
} finally { } finally {
@ -160,7 +165,9 @@ export class AddonStorageManagerCoursesStoragePage implements OnInit, OnDestroy
try { try {
await CoreCourseHelper.deleteCourseFiles(course.id); await CoreCourseHelper.deleteCourseFiles(course.id);
await this.downloadedCoursesQueue.run(async () => {
await this.setDownloadedCourses(CoreArray.withoutItem(this.downloadedCourses, course)); await this.setDownloadedCourses(CoreArray.withoutItem(this.downloadedCourses, course));
});
} catch (error) { } catch (error) {
CoreDomUtils.showErrorModalDefault(error, Translate.instant('core.errordeletefile')); CoreDomUtils.showErrorModalDefault(error, Translate.instant('core.errordeletefile'));
} finally { } finally {
@ -175,7 +182,7 @@ export class AddonStorageManagerCoursesStoragePage implements OnInit, OnDestroy
*/ */
private async onCourseUpdated(courseId: number, status: DownloadStatus): Promise<void> { private async onCourseUpdated(courseId: number, status: DownloadStatus): Promise<void> {
if (courseId == CoreCourseProvider.ALL_COURSES_CLEARED) { if (courseId == CoreCourseProvider.ALL_COURSES_CLEARED) {
await this.setDownloadedCourses([]); await this.downloadedCoursesQueue.run(() => this.setDownloadedCourses([]));
return; return;
} }
@ -189,7 +196,7 @@ export class AddonStorageManagerCoursesStoragePage implements OnInit, OnDestroy
course.isDownloading = status === DownloadStatus.DOWNLOADING; course.isDownloading = status === DownloadStatus.DOWNLOADING;
course.totalSize = await this.calculateDownloadedCourseSize(course.id); course.totalSize = await this.calculateDownloadedCourseSize(course.id);
await this.setDownloadedCourses(this.downloadedCourses); await this.downloadedCoursesQueue.run(() => this.setDownloadedCourses(this.downloadedCourses));
} }
/** /**

View File

@ -121,8 +121,17 @@ export class CoreQueueRunner {
* @param options Options. * @param options Options.
* @returns Promise resolved when the function has been executed. * @returns Promise resolved when the function has been executed.
*/ */
run<T>(id: string, fn: CoreQueueRunnerFunction<T>, options?: CoreQueueRunnerAddOptions): Promise<T> { run<T>(fn: CoreQueueRunnerFunction<T>, options?: CoreQueueRunnerAddOptions): Promise<T>;
options = options || {}; run<T>(id: string, fn: CoreQueueRunnerFunction<T>, options?: CoreQueueRunnerAddOptions): Promise<T>;
run<T>(
idOrFn: string | CoreQueueRunnerFunction<T>,
fnOrOptions?: CoreQueueRunnerFunction<T> | CoreQueueRunnerAddOptions,
options: CoreQueueRunnerAddOptions = {},
): Promise<T> {
let id = typeof idOrFn === 'string' ? idOrFn : this.getUniqueId('anonymous');
const fn = typeof idOrFn === 'function' ? idOrFn : fnOrOptions as CoreQueueRunnerFunction<T>;
options = typeof fnOrOptions === 'object' ? fnOrOptions : options;
if (id in this.queue) { if (id in this.queue) {
if (!options.allowRepeated) { if (!options.allowRepeated) {

View File

@ -0,0 +1,38 @@
// (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 { CoreQueueRunner } from '@classes/queue-runner';
import { CoreUtils } from '@services/utils/utils';
describe('CoreQueueRunner', () => {
it('Locks threads launched synchronously', async () => {
// Arrange
const concurrency = 100;
const range = Array.from({ length: concurrency }, (_, item) => item);
const lock = new CoreQueueRunner();
const items: string[] = [];
// Act
await Promise.all(range.map((i) => lock.run(async () => {
await CoreUtils.wait(Math.floor(Math.random() * 10));
items.push(`Item #${i}`);
})));
// Assert
expect(items).toEqual(range.map(i => `Item #${i}`));
});
});